We're working on a project and we are completely stuck.
Basically, we are coding a virtual Quoridor player module.
However, our player module thinks that it's starting one square in front of it's home!
Why could this be?

from interface import *
import engine
import random
import time
import copy

class StackNode(object):
    """A building block for stacks.
       Each object contains a value and a link to another node.
    """
    __slots__ = ( "value", "next" )

    def __init__(self,value,next):
        """Set the values of a newly created StackNode"""
        self.value = value
        self.next = next

class EmptyStackNode(object):
    """A special stack node used to mark the end of the links.
       Also, a stack's top containing an instance of this class
       implies an empty stack.
    """
    __slots__ = ()

class Stack(object):
    """A linear collection accessed in Last-In-First-Out order."""
    __slots__ = ( "top" )

    def __init__(self):
        """Initialize an empty stack by setting its top to
           refer to an EmptyStackNode.
        """
        self.top = EmptyStackNode()

    def push(self,value):
        """Insert an element onto the top of the stack."""
        self.top = StackNode(value,self.top)

    def pop(self):
        """Remove the top (newest) element from the stack.
           Nothing is returned.
        """
        # Precondition: stack is not empty
        self.top = self.top.next

    def topItem(self):
        """Access and return the top (newest) element in the stack.
           The stack does not change.
        """
        # Precondition: stack is not empty
        return self.top.value
    
    def isEmpty(self):
        """Is the stack empty?"""
        return isinstance( self.top, EmptyStackNode )
    
#Queue and related classes and methods. Implemented by Peter Sevich.
class QueueNode(object):
    """A building block for queues.
       Each object contains a value and a link to another node.
    """
    __slots__ = ( "value", "next" )

    def __init__(self,value,next):
        """Set the values of a newly created QueueNode."""
        self.value = value
        self.next = next

class EmptyQueueNode(object):
    """A special queue node used to mark the end of the links.
       Also, a queue's front and back containing instances of this
       class implies an empty queue.
    """
    __slots__ = ()


class Queue(object):
    """A linear collection accessed in First-In-First-Out order"""
    __slots__ = ( "front", "back" )
    
    def __init__(self):
        """Initialize an empty queue by setting both the front and
           back to refer to EmptyQueueNodes.
        """
        self.front = EmptyQueueNode()    # The front node in the queue
        self.back = EmptyQueueNode()        # The back node in the queue

    def enqueue(self, value):
        """Insert an element onto the back of the queue."""
        newnode = QueueNode(value,EmptyQueueNode())
        if self.isEmpty():
            self.front = newnode
            # Front is only updated on enqueue when the queue was empty.
        else: # There is a last node; append to it.
            self.back.next = newnode
        self.back = newnode
        
    def dequeue(self):
        """Remove the front element from the queue."""
        # Precondition: queue is not empty 
        self.front = self.front.next
        if self.isEmpty():
            self.back = EmptyQueueNode()
            # Back is only updated on dequeue if the queue ends up empty.

    def frontItem(self):
        """Access and return the first element in the queue.
           The queue does not change.
        """
        # Precondition: queue is not empty. 
        return self.front.value
        
    def isEmpty(self):
        """Is the queue empty?"""
        return isinstance(self.front, EmptyQueueNode)
    
class wall(object):
    __slots__ = ("cords")
    
    def __init__(self):
        self.cords = time.time()
    
class tile(object):
    '''
    Left, right, up and down are directions West East North and South (respectively).
    'cords' variable is a tuple where the first value represents row, the second column.
    'neighbors' is all tiles not seperated by a wall
    '''
    __slots__ = ("left", "up", "right", "down", "cords", "neighbors", "previous")
    def __init__(self, cords):
        self.cords = cords
        
    def connect(self, cords, board):
        '''Run after initialization of the board to connect all neighboring tiles and
        put walls around the outside of the board.
        '''

        if(self.cords[1]!= 0):
            self.left = board.tiles[cords[0]][cords[1]-1]
        else:
            self.left = wall()
                            
  
        if(self.cords[0]!= board.dims-1):
            self.down = board.tiles[cords[0]+1][cords[1]]
        else:
            self.down = wall()
            


        if(self.cords[1]!= board.dims-1):
            self.right = board.tiles[cords[0]][cords[1]+1]
        else:
            self.right = wall()

        if(self.cords[0]!=0):
            self.up = board.tiles[cords[0]-1][cords[1]]
        else:
            self.up = wall()
    
    def find_neighbors(self):
        '''Called after adding walls to find the neighbors of every tile not separated by a wall.'''
        neighbors = []
        if isinstance(self.left, tile):
            neighbors.append(self.left)
        if isinstance(self.right, tile): 
            neighbors.append(self.right)
        if isinstance(self.up, tile):
            neighbors.append(self.up)
        if isinstance(self.down, tile):
            neighbors.append(self.down)
        self.neighbors = neighbors
        
    def __str__(self):
        return(str(self.cords))
        
class board(object):
    __slots__ = ("tiles", "dims")
    ''' 'tiles' is a two dimensional array of all tile objects on the board
    dims is equal to the value of BOARD_DIM imported from interface.'''
    def __init__(self, BOARD_DIM):
        self.dims = BOARD_DIM
        self.tiles = [ [ None ] * self.dims for i in range(self.dims) ]
        for i in range(self.dims):
            for j in range(self.dims):
                self.tiles[i][j] = tile([i,j])
        
        for i in range(self.dims):
            for j in range(self.dims):
                self.tiles[i][j].connect([i,j], self)
                
    def addwalls(self, gameFile):
        '''Adds a wall by setting all the directions of the tiles touching the wall to wall instead of another tile.
        Preconditions: input file has 2 sets of coordinates R1, C1, R2, C2 per line and that these coordinates
        represent walls with a length equal to two'''
        file = open(gameFile)
        for line in file:
            wStart = [int(line.split()[0]),int(line.split()[1])]
            wEnd = [int(line.split()[2]), int(line.split()[3])]

            if wStart[1] == wEnd[1]:
                
                self.tiles[wStart[0]][wStart[1]].left = wall()
                self.tiles[wEnd[0]-1][wEnd[1]].left = wall()
                
                self.tiles[wStart[0]][wStart[1]-1].right = wall()
                self.tiles[wEnd[0]-1][wEnd[1]-1].right = wall()
                
                
            if(wStart[0] == wEnd[0]):
                
                self.tiles[wStart[0]][wStart[1]].up = wall()
                self.tiles[wEnd[0]][wEnd[1]-1].up = wall()
                
                self.tiles[wStart[0]-1][wStart[1]].down = wall()
                self.tiles[wEnd[0]-1][wEnd[1]-1].down = wall()
            
    def printboard(board, BOARD_DIM):
        '''Prints one tile of the board per line in the form "start up right left down"
        where i is the row j is the column.'''
        for i in range(BOARD_DIM):
            for j in range(BOARD_DIM):
                print("start:" + str(self.tiles[i][j].cords) +  " up:" + str(self.tiles[i][j].up.cords) + " right:" +\
                str(self.tiles[i][j].right.cords) + " down:" + str(self.tiles[i][j].down.cords) + " left:" + str(self.tiles[i][j].left.cords))            


class player(object):
    #Updated by Thomas W. Yoo and Andrew Jaffe
    __slots__ = ("board", "playerId", "numPlayers","PlayerLocation", "PlayerDestinations", "PlayerHomes", "wallsRemaining", "locations" )
    
    def __init__(self, board, ID, numPlayers, PlayerLocation, PlayerHomes, wallsRemaining):
        self.board = board
        self.playerId = ID
        self.numPlayers = numPlayers
        self.PlayerHomes = PlayerHomes
        self.PlayerLocation = self.board.tiles[PlayerHomes[ID][0]][PlayerHomes[ID][1]]
        if numPlayers == 1 or numPlayers == 2 :
            self.wallsRemaining = wallsRemaining[0]
        else:
            self.wallsRemaining = wallsRemaining[1]
        self.PlayerDestinations = []
        self.locations = []
        for i in range(numPlayers):
            self.locations.append(self.board.tiles[self.PlayerHomes[i][0]][self.PlayerHomes[i][1]])
        for i in range(numPlayers):
            self.PlayerDestinations.append('temp')
            self.PlayerDestinations[i] = set()
            for j in range(self.board.dims):
                if i == 0:
                    self.PlayerDestinations[i].add(self.board.tiles[0][j])
                if i == 1:
                    self.PlayerDestinations[i].add(self.board.tiles[self.board.dims -1 ][j])
                if i == 2:
                    self.PlayerDestinations[i].add(self.board.tiles[j][self.board.dims - 1])
                if i == 3:
                    self.PlayerDestinations[i].add(self.board.tiles[j][0])


def init(gameFile, playerId, numPlayers, playerHomes, wallsRemaining):
    """
    For all parts the engine calls this method so the player can initialize their data.
        gameFile - a string which is the file containing the initial board state.
        playerId - the numeric Id of the player (0-4).
        numPlayers - the number of players (2 or 4).
        playerHomes - a list of coordinates for each players starting cell (PO-P4).
        wallsRemaining - the number of walls remaining (same for all players at start).
    """
    
    # log any message string to standard output and the current log file
    engine.log_msg("player.init called for player " + str(playerId) + " File=" + gameFile +
                   ', playerId=' + str(playerId) + ', numPlayers=' + str(numPlayers) + ', playerHomes=' +
                   str(playerHomes) + ', wallsRemaining=' + str(wallsRemaining))
 
    gameboard = board(BOARD_DIM)
    gameboard.addwalls(gameFile)
    
    for i in range(BOARD_DIM):
        for j in range(BOARD_DIM):
            gameboard.tiles[i][j].find_neighbors()
    
    playerData = player(gameboard, playerId, numPlayers, playerHomes, playerHomes, wallsRemaining)
    
    # initialize your data structure here, and then return it.  It will be
    # passed back to you as 'playerData' in the shortest_path function.

    return playerData


def shortest_path(playerData, playerID):
    #Updated by Andrew Jaffe and Peter Sevich
    """Returns a list of coordinates representing the shortest path on the
    board between the start and finish coordinates.
        playerData - The player's data
        source - the start coordinate
        dest - the end coordinate
    """

    board = playerData.board
    path = []
    # Implement your shortest path algorithm here.  If no path is found
    # return an empty list.
    start = playerData.locations[playerID]
    finish = playerData.PlayerDestinations[playerID]
        
        
    """start and finish are nodes. Print a path from start to finish."""
    visited = set()
    stk = Queue()
    visited.add(start)
    start.previous = None
    stk.enqueue(start)
    while not stk.isEmpty():
        node = stk.frontItem()
        stk.dequeue()
        if node in finish:
            break
        for neighbor in node.neighbors:
            if neighbor not in visited:
                neighbor.previous = node
                visited.add(neighbor)
                stk.enqueue(neighbor)

    if node in visited:
        backPath = Stack()
        while node != start:
            backPath.push(node)
            node = node.previous
        backPath.push(start)
        while not backPath.isEmpty():
            path.append(backPath.topItem())
            backPath.pop()

    if(path[-1] not in finish):
        return []
    else:
        return path

def is_valid_wall(player, start, end):
    '''conditions Start and end are neighbors of eachother
    start and end are tile objects
    wall will be placed above (if horiontal) or left (if vertical) of the tiles
    function returns False if wall is not valid
    functino returns True if wall is valid'''

    if(start.cords[0] == end.cords[0]):#wall is horizontal
        if(isinstance(start.up, wall) or isinstance(end.up, wall)):
            return False #wall overlaps an already overlaping wall
        if(isinstance(start.right, wall) and\
           isinstance(player.board.tiles[start.cords[0] - 1][start.cords[1]].right, wall)):
            return False #Horizontal wall through a vertical wall
    if(start.cords[1] == end.cords[1]):#wall is vertical
        if(isinstance(start.left, wall) or isinstance(end.left, wall)):
            return False #Vertical wall overlaps
        if(isinstance(start.down, wall) and\
           isinstance(player.board.tiles[start.cords[0]][start.cords[1] - 1].down, wall)):
            return False #Vertical wall crosses a horizontal wall

    return True

def place_wall(player, start, end):
    '''conditions: start and end are tile objects that are touching
    the location of the wall must have already been validated'''
    if(start.cords[0] == end.cords[0]):#row equal wall is horizontal
        start.up = wall()
        start.find_neighbors()
        player.board.tiles[start.cords[0]][start.cords[1]-1].down = wall()
        player.board.tiles[start.cords[0]][start.cords[1]-1].find_neighbors()
        end.up = wall()
        end.find_neighbors()
        player.board.tiles[end.cords[0]][end.cords[1]-1].down = wall()
        player.board.tiles[end.cords[0]][end.cords[1]-1].find_neighbors()
    elif(start.cords[1] == end.cords[1]):#column equal wall is vertical
        start.left = wall()
        start.find_neighbors()
        player.board.tiles[start.cords[0]-1][start.cords[1]].right = wall()
        player.board.tiles[start.cords[0]-1][start.cords[1]].find_neighbors()
        end.left = wall()
        end.find_neighbors()
        player.board.tiles[end.cords[0]-1][end.cords[1]].right = wall()
        player.board.tiles[end.cords[0]-1][end.cords[1]].find_neighbors()
    return player

def find_walls(player, start, end):
    '''start and end are tiles in player.board.tiles, and are touching
    find_walls returns a list of all walls that go between the two'''
    walls = []
    if(start.cords[0] == end.cords[0]):#row equal path is horizontal #vertical wall
        if(start.cords[1] <  8):
            walls.append(start, player.board.tiles[start.cords[0]][start.cords[1]+1])#one tile below start
        if(start.cords[1] == 8):
            walls.append(player.board.tiles[start.cords[0]][start.cords[1]-1], start)
##        if(start.cords[1] == 0):
##            walls.append(start, player.board.tiles[start.cords[0]][start.cords[1]+1])#oen tile above start

    if(start.cords[1] == end.cords[1]):#path is vertical horizontal wall
        if(start.cords[0] < 8):
            walls.append((start, player.board.tiles[start.cords[0]+1][start.cords[1]]))
        if(start.cords[1] == 8):
            walls.append((player.board.tiles[start.cords[0]-1][start.cords[1]], start))
##        if(start.cords[0] == 0):
##            walls.append(start, player.board.tiles[start.cords[0] +1][start.cordss[1]])

    for wallnum in range(len(walls)):
        if is_valid_wall(player, walls[wallnum][0], walls[wallnum][1]) == False:
            walls.pop(wallnum)

    return walls

def last_move(playerData, playerMove):
    #Written and implemented by Thomas W. Yoo
    if playerMove.move == False:
        place_wall(playerData,playerData.board.tiles[playerMove.start[0]][playerMove.start[1]],playerData.board.tiles[playerMove.end[0]][playerMove.end[1] - 1])
    elif playerMove.move == True:
        playerData.locations[playerMove.playerId] = playerData.board.tiles[playerMove.end[0]][playerMove.end[1]]
    return playerData

def test_wall(player, start, end, playerids):
    '''takes in a valid wall location and a list of players
    adds the wall to a test board config and re-evaluates
    and returns the shortest paths'''
    test = copy.deepcopy(player)
    test = place_wall(test, start, end)
    player_paths = []
    for Id in playerids:
        player_paths.append(shortest_path(test, Id))
    return player_paths

        
def find_best_wall(player,target):
    print("best wall was called")
    '''currently only writen for 2 player mode
    tkes in player data
    returns a movetype data structure with the placement of the best wall
    or if it is better to wait with moving player.playerId forward one on his path'''
    player1 = player.playerId #our player
    player2 = target #opponent
    player1_path = shortest_path(player, player1)
    player2_path = shortest_path(player, player2)
    player1_paths = []
    player2_paths = []
    #for player_loc in range(len(player2_path)):
    for position in range(len(player2_path) - 1):
        walls = find_walls(player, player2_path[position], player2_path[position+1])
        for wall in walls:
            player_paths = test_wall(player, wall[0], wall[1], (0,1))
            if( not(len(player_paths[0]) > len(player1_path))):
                player2_paths.append((player_paths[1], wall))
    for x in range(len(player2_paths)):
        tempmax = len(player2_paths[x])
        maxim = 0
        if tempmax > maxim:
            maxim = tempmax
            best_path = player2_paths[x]
    wall_spot = best_path[1]
    when_to_place = best_path[0][-1]
    if( player.locations[player2] == when_to_place):
        start = (best_path[1][0].cords[0], best_path[1][0].cords[1])
        end = (best_path[1][1].cords[0], best_path[1][1].cords[1])
        if end[0] == start [0]:
            end[1] += 1
        else:
            end[0] += 1
        print("best wall returned start = " + str(start) + " end = " + str(end))
        return PlayerMove(player.playerId, False, start, end)
    elif( player.locations[player2] != when_to_place):
        start = (player1_path[0].cords[0], player1_path[0].cords[1])
        end = (player1_path[1].cords[1], player1_path[1].cords[1])
        print("best wall returned start = " + str(start) + " end = " + str(end))
        return PlayerMove(player.playerId, True, start, end)

def move(player):
    for i in range(player.numPlayers):
        player_path = shortest_path(player,player.playerId)
        myshort = shortest_path(player,player.playerId)
        tempshort = shortest_path(player,i)
        currentShortest = tempshort
        if len(tempshort) < len(myshort) or len(tempshort) < len(currentShortest):
            targetPlayer = i    #Target to fuck over
            currentShortest = tempshort #Length of above's path
            movedata = find_best_wall(player,targetPlayer)
            player.PlayerLocation = player.board.tiles[movedata.end[0]][movedata.end[1]]
            player.locations[player.playerId] = player.PlayerLocation
        else:
            start = (player_path[0].cords[0], player_path[0].cords[1])
            end = (player_path[1].cords[0], player_path[1].cords[1])
            if player.board.tiles[end[0]][end[1]] in player.locations:
                if player.board.tiles[end[0]][end[1]] == player.PlayerLocation or( (len(path)>2) and player.board.tiles[path[2][0]][path[2][1]] in player.locations):
                    end = start
                else:
                    end = (path[2][0],path[2][1])
            movedata = PlayerMove(player.playerId, True, start, end)
            player.PlayerLocation = player.board.tiles[end[0]][end[1]]
            player.locations[player.playerId] = player.PlayerLocation
    return player, movedata

Recommended Answers

All 2 Replies

Must say that I am lost. I would have made game with simple list of lists or strings and list of player locations. This is not my idea of readable and maintainable 'obvious' code Python is all about. I thought this game is played in simple square board of 9x9 and fixed player start positions. Now I noticed it even does not do anything but import something called interface and engine which I never listened about!

Can you please post the part of your code where you think you have errors?

I hate long codes :)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.