Beispiel #1
0
def _enumMoves_pawn(board, color, fromPosn):
    pawnStartRank = ChessBoard.PAWN_STARTING_RANKS[color]
    pawnDirection = [-1, 1][color == ChessBoard.WHITE]
    oneAhead = fromPosn.getTranslatedBy(pawnDirection, 0)
    twoAhead = fromPosn.getTranslatedBy(pawnDirection * 2, 0)

    moves = []

    if board[oneAhead] is None:
        moves.append(oneAhead)

    if (fromPosn.rankN == pawnStartRank and board[oneAhead] is None
            and board[twoAhead] is None):
        moves.append(twoAhead)

    otherColor = ChessBoard.getOtherColor(color)
    otherPawnStartRank = ChessBoard.PAWN_STARTING_RANKS[otherColor]
    for fileDelta in [-1, 1]:
        toPosn = fromPosn.getTranslatedBy(pawnDirection, fileDelta)

        if __enumMoves_isOnBoard(toPosn):
            enemyP = (board[toPosn] is not None
                      and board[toPosn].color == otherColor)
            enpP = (board[toPosn] is None
                    and board.flag_enpassant[otherColor][toPosn.fileN]
                    and twoAhead.rankN == otherPawnStartRank)
            if enemyP or enpP:
                moves.append(toPosn)

    return moves
Beispiel #2
0
def heuristicPcsUnderAttack(color, board):
    """Return the value of the given color's pieces that are under attack

    @param color: The color of the pieces to test -
                one of maverick.data.ChessBoard.WHITE or
                maverick.data.ChessBoard.BLACK
    @param board: a ChessBoard object

    @return: A number representing the value of the given color's
            pieces that are under attack, weighted by piece value"""

    otherColor = ChessBoard.getOtherColor(color)

    # Get posns the enemy can move to
    enemyMoveDstPosns = [m[1] for m in enumMoves(board, otherColor)]

    # Record which friendly pieces are under attack
    # A piece is under attack if its posn (pcPsn) is in myPieces
    # TODO: use filter and lambda
    piecesUnderAttack = [pcPsn for pcPsn in board.getPiecesOfColor(color)
                         if pcPsn in enemyMoveDstPosns]

    # Sum weighted values of under-attack pieces
    weightedTotal = 0
    for piecePosn in piecesUnderAttack:

        piece = board[piecePosn]
        # Check if there is a value for this piece in the mappings
        if piece.pieceType in PIECE_VALUES:
            weightedTotal += PIECE_VALUES[piece.pieceType]

    # Compress return value into range [-1..1]
    return 1 - 2 * (weightedTotal / MAX_TOTAL_PIECE_VALUE)
Beispiel #3
0
def _enumMoves_pawn(board, color, fromPosn):
    pawnStartRank = ChessBoard.PAWN_STARTING_RANKS[color]
    pawnDirection = [-1, 1][color == ChessBoard.WHITE]
    oneAhead = fromPosn.getTranslatedBy(pawnDirection, 0)
    twoAhead = fromPosn.getTranslatedBy(pawnDirection * 2, 0)

    moves = []

    if board[oneAhead] is None:
        moves.append(oneAhead)

    if (fromPosn.rankN == pawnStartRank and
        board[oneAhead] is None and
        board[twoAhead] is None):
        moves.append(twoAhead)

    otherColor = ChessBoard.getOtherColor(color)
    otherPawnStartRank = ChessBoard.PAWN_STARTING_RANKS[otherColor]
    for fileDelta in [-1, 1]:
        toPosn = fromPosn.getTranslatedBy(pawnDirection, fileDelta)

        if __enumMoves_isOnBoard(toPosn):
            enemyP = (board[toPosn] is not None and
                      board[toPosn].color == otherColor)
            enpP = (board[toPosn] is None and
                    board.flag_enpassant[otherColor][toPosn.fileN] and
                    twoAhead.rankN == otherPawnStartRank)
            if enemyP or enpP:
                moves.append(toPosn)

    return moves
Beispiel #4
0
def _getBoard2():
    _w = ChessBoard.WHITE
    _b = ChessBoard.BLACK
    return ChessBoard(startLayout=[[
        ChessPiece(_w, ChessBoard.ROOK), None, None, None,
        ChessPiece(_w, "K"), None, None,
        ChessPiece(_w, "R")
    ],
                                   [
                                       ChessPiece(_w, "P"),
                                       ChessPiece(_w, "P"),
                                       ChessPiece(_w, "P"), None, None,
                                       ChessPiece(_w, "P"),
                                       ChessPiece(_w, "P"),
                                       ChessPiece(_w, "P")
                                   ],
                                   [
                                       None, None, None,
                                       ChessPiece(_w, "B"), None, None, None,
                                       None
                                   ],
                                   [
                                       None, None, None,
                                       ChessPiece(_w, "P"),
                                       ChessPiece(_w, "N"), None, None, None
                                   ],
                                   [
                                       None, None, None, None,
                                       ChessPiece(_w, "N"), None, None,
                                       ChessPiece(_w, "Q")
                                   ],
                                   [
                                       None,
                                       ChessPiece(_b, "P"), None, None,
                                       ChessPiece(_b, "P"),
                                       ChessPiece(_b, "B"), None, None
                                   ],
                                   [
                                       ChessPiece(_b, "P"),
                                       ChessPiece(_b, "B"),
                                       ChessPiece(_b, "P"),
                                       ChessPiece(_b, "P"),
                                       ChessPiece(_b, "Q"), None,
                                       ChessPiece(_b, "P"),
                                       ChessPiece(_b, "P")
                                   ],
                                   [
                                       ChessPiece(_b, "R"),
                                       ChessPiece(_b, "N"), None, None, None,
                                       ChessPiece(_b, "R"),
                                       ChessPiece(_b, "K"), None
                                   ]],
                      startEnpassantFlags={
                          _b: [False] * 8,
                          _w: [False] * 8
                      },
                      startCanCastleFlags={
                          _b: (False, False),
                          _w: (True, False)
                      })
Beispiel #5
0
def getBoard8():
    return ChessBoard(startLayout=[[
        None, None,
        ChessPiece(_w, "B"),
        ChessPiece(_w, "Q"),
        ChessPiece(_w, "K"),
        ChessPiece(_w, "R"), None, None
    ],
                                   [
                                       ChessPiece(_w, "P"), None,
                                       ChessPiece(_w, "P"), None, None,
                                       ChessPiece(_w, "P"), None, None
                                   ],
                                   [
                                       None,
                                       ChessPiece(_w, "N"), None, None,
                                       ChessPiece(_b, "Q"),
                                       ChessPiece(_w, "N"), None,
                                       ChessPiece(_w, "P")
                                   ],
                                   [
                                       None,
                                       ChessPiece(_w, "P"), None, None,
                                       ChessPiece(_w, "R"),
                                       ChessPiece(_b, "B"),
                                       ChessPiece(_w, "P"), None
                                   ],
                                   [
                                       None, None, None,
                                       ChessPiece(_b, "P"),
                                       ChessPiece(_w, "P"), None, None, None
                                   ],
                                   [
                                       ChessPiece(_b, "P"), None,
                                       ChessPiece(_b, "N"), None,
                                       ChessPiece(_b, "P"),
                                       ChessPiece(_b, "P"),
                                       ChessPiece(_b, "N"), None
                                   ],
                                   [
                                       None,
                                       ChessPiece(_b, "P"), None, None,
                                       ChessPiece(_b, "K"), None, None,
                                       ChessPiece(_b, "P")
                                   ],
                                   [
                                       ChessPiece(_b, "R"), None,
                                       ChessPiece(_b, "B"), None, None,
                                       ChessPiece(_b, "R"), None, None
                                   ]],
                      startEnpassantFlags={
                          _b: [False] * 8,
                          _w: [False] * 8
                      },
                      startCanCastleFlags={
                          _b: (False, False),
                          _w: (False, False)
                      })
Beispiel #6
0
def getBoard5():
    return ChessBoard(startLayout=[[
        None, None, None, None, None,
        ChessPiece("W", "R"),
        ChessPiece("W", "N"),
        ChessPiece("W", "R")
    ],
                                   [
                                       None, None, None,
                                       ChessPiece("W", "K"), None, None,
                                       ChessPiece("W", "P"), None
                                   ],
                                   [
                                       None,
                                       ChessPiece("B", "P"),
                                       ChessPiece("W", "P"),
                                       ChessPiece("W", "P"), None,
                                       ChessPiece("W", "P"), None, None
                                   ],
                                   [
                                       ChessPiece("W", "P"), None, None,
                                       ChessPiece("B", "Q"), None, None,
                                       ChessPiece("B", "N"),
                                       ChessPiece("W", "P")
                                   ],
                                   [
                                       ChessPiece("B", "P"),
                                       ChessPiece("W", "N"), None, None, None,
                                       ChessPiece("B", "P"), None, None
                                   ],
                                   [
                                       None,
                                       ChessPiece("W", "Q"), None, None, None,
                                       ChessPiece("W", "B"),
                                       ChessPiece("B", "P"), None
                                   ],
                                   [
                                       None,
                                       ChessPiece("B", "P"), None, None,
                                       ChessPiece("B", "P"),
                                       ChessPiece("B", "K"), None,
                                       ChessPiece("B", "P")
                                   ],
                                   [
                                       ChessPiece("B", "R"),
                                       ChessPiece("B", "N"),
                                       ChessPiece("B", "B"), None, None, None,
                                       None,
                                       ChessPiece("B", "R")
                                   ]],
                      startEnpassantFlags={
                          'B': [False] * 8,
                          'W': [False] * 8
                      },
                      startCanCastleFlags={
                          'B': [False, False],
                          'W': [False, False]
                      })
Beispiel #7
0
    def _request_getState(self, playerID, gameID):
        """Return the current state of the game

        The state contains information about
        the playerIDs of the black and white players, whose turn it is,
        the current board state, and the game history.

        @param playerID: the integer of the playerID of the player on which
                         _request_getState is being called
        @param gameID: the integer gameID of an in-progress game"""

        response = self._makeRequest("GET_STATE",
                                     playerID=playerID,
                                     gameID=gameID)
        ## TODO (James): check constants to validate received data

        # Construct board object from serialized data

        # The serialized board layout
        rawLayout = response["board"]["layout"]
        # The deserialized board layout
        layout = MaverickClient.__request_getState_deserializeLayout(rawLayout)

        # The serialized history
        rawHst = response["history"]
        # Deserialize the history
        histList = MaverickClient.__request_getState_deserializeHistory(rawHst)

        curEnPassantFlags = response["board"]["enPassantFlags"]
        curCastleFlags = response["board"]["canCastleFlags"]

        curBoardObj = ChessBoard(startLayout=layout,
                                 startEnpassantFlags=curEnPassantFlags,
                                 startCanCastleFlags=curCastleFlags)

        # Build up return dictionary
        stateDict = {}
        stateDict["youAreColor"] = response["youAreColor"]
        stateDict["isWhitesTurn"] = response["isWhitesTurn"]
        stateDict["board"] = curBoardObj
        stateDict["history"] = histList

        return stateDict
    def _boardSearch(self, board, color, depth, alpha, beta, isMaxNode,
                     stopSrchTime):
        """Performs a board via alpha-beta pruning/quiescence search

        NOTE: Not guaranteed to stop promptly at stopSrchTime - may take some
        time to terminate. Leave a time buffer.

        Selectively explores past the final depth if many pieces have
        been captured recently, by calling quiescent search

        @param board: The starting board state to evaluate
        @param color: The color of the player to generate a move for
        @param depth: The number of plies forward that should be explored
        @param alpha: Nodes with a likability below this will be ignored
        @param beta: Nodes with a likability above this will be ignored
        @param isMaxNode: Is this a beta node? (Is this node seeking to
                        maximize the value of child nodes?)
        @param stopSrchTime: Time at which the search should begin to terminate
        @param nodesVisited: The number of nodes already searched

        @return: A tuple with the following elements:
                1. None, or a move of the form (fromChessPosn, toChessPosn)
                    representing the next move that should be made by the given
                    player
                2. The likability of this move's path in the tree, as followed
                    by the search and as determined by the likability of the
                    leaf node terminating the path
                3. The number of nodes visited in the search

        Implementation based on information found here: http://bit.ly/t1dHKA"""
        ## TODO (James): Check timeout less than once per iteration

        ## TODO (James): Make logging conditional - temporarily disabled

        ## TODO (James): Set this to True before handing in!
        # Whether to limit based on wall clock time or number of nodes seen
        USE_WALL_CLOCK = False

        # Number of nodes to visit (for when wall clock time is not used)
        NUM_NODES_TO_VISIT = 1200

        #logStrF = "Performing minimax search to depth {0}.".format(depth)
        #QLAI._logger.debug(logStrF)

        # Note that we've visited a node
        nodesVisited = 1

        otherColor = ChessBoard.getOtherColor(color)

        # Check if we are at a leaf node
        if (depth == 0):
            (a, b) = self._quiescentSearch(board, color, alpha, beta,
                                           isMaxNode)
            return (a, b, 1)

        # Check if we should otherwise terminate
        elif (time() > stopSrchTime or board.isKingCheckmated(color)
              or board.isKingCheckmated(otherColor)):
            return (None,
                    evaluateBoardLikability(color, board,
                                            self.heuristicWgts), nodesVisited)

        else:
            moveChoices = enumMoves(board, color)
            #logStrF = "Considering {0} poss. moves".format(len(moveChoices))
            #QLAI._logger.debug(logStrF)

            # Check whether seeking to find minimum or maximum value
            if isMaxNode:
                newMin = alpha
                newMoveChoice = None
                for move in moveChoices:

                    # Rather than calling getPlyResult, use THIS board. Much
                    # faster. REMEMBER TO UNDO THIS HYPOTHETICAL MOVE

                    # Save the old flag sets so they can be restored
                    boardMoveUndoDict = board.getPlyResult(move[0], move[1])

                    # Find the next move for this node, and how likable the
                    # enemy will consider this child node
                    (_, nodeEnemyLikability,
                     nVisit) = self._boardSearch(board, otherColor, depth - 1,
                                                 newMin, beta, not isMaxNode,
                                                 stopSrchTime)
                    # RESTORE THE OLD BOARD STATE - VERY IMPORTANT
                    board.undoPlyResult(boardMoveUndoDict)

                    # Note how many more nodes we've visited
                    nodesVisited += nVisit

                    # Make note of the least likable branches that it still
                    # makes sense to pursue, given how likable this one is
                    if nodeEnemyLikability > newMin:
                        newMin = nodeEnemyLikability
                        newMoveChoice = move

                    # Don't search outside of the target range
                    elif nodeEnemyLikability > beta:
                        #QLAI._logger.debug("Pruning because new value > beta")
                        return (move, beta, nodesVisited)

                    # Check to see if we've evaluated the max number of nodes
                    if ((not USE_WALL_CLOCK)
                            and (nodesVisited > NUM_NODES_TO_VISIT)):
                        return (newMoveChoice, newMin, nodesVisited)

                return (newMoveChoice, newMin, nodesVisited)
            else:
                newMax = beta
                newMoveChoice = None
                for move in moveChoices:

                    # Rather than calling getPlyResult, use THIS board. Much
                    # faster. REMEMBER TO UNDO THIS HYPOTHETICAL MOVE

                    # Save the old flag sets so they can be restored
                    boardMoveUndoDict = board.getPlyResult(move[0], move[1])

                    # Find how likable the enemy will consider this child node
                    (_, nodeEnemyLikability,
                     nVisit) = self._boardSearch(board, otherColor, depth - 1,
                                                 alpha, newMax, not isMaxNode,
                                                 stopSrchTime)

                    # RESTORE THE OLD BOARD STATE - VERY IMPORTANT:
                    board.undoPlyResult(boardMoveUndoDict)

                    # Note how many more nodes we've visited
                    nodesVisited += nVisit

                    # Make note of the most likable branches that it still
                    # makes sense to pursue, given how likable this one is
                    if nodeEnemyLikability < newMax:
                        newMax = nodeEnemyLikability
                        newMoveChoice = move

                    # Don't bother searching outside of our target range
                    elif nodeEnemyLikability < alpha:
                        #QLAI._logger.debug("pruning because new val < alpha")
                        return (move, alpha, nodesVisited)

                    # Check to see if we've evaluated the max number of nodes
                    if ((not USE_WALL_CLOCK)
                            and (nodesVisited > NUM_NODES_TO_VISIT)):
                        return (newMoveChoice, newMin, nodesVisited)
                return (newMoveChoice, newMax, nodesVisited)
    def _quiescentSearch(self, board, color, alpha, beta, isMaxNode):
        """Perform a quiescent search on the given board, examining captures

        Enumerates captures, and evaluates them to see if they alter results

        @param board: The starting board state to evaluate
        @param color: The color of the player to generate a move for
        @param alpha: Nodes with a likability below this will be ignored
        @param beta: Nodes with a likability above this will be ignored
        @param isMaxNode: Is this a beta node? (Is this node seeking to
                        maximize the value of child nodes?)

        @return: A tuple with the following elements:
                1. None, or a move of the form (fromChessPosn, toChessPosn)
                    representing the next move that should be made by the given
                    player
                2. The likability of this move's path in the tree, as followed
                    by the search and as determined by the likability of the
                    leaf node terminating the path

        No timeout is allowed. This shouldn't take long, anyway. If it does,
        then we're doing good things.

        Note: this was influenced by information here: http://bit.ly/VYlJVC """

        otherColor = ChessBoard.getOtherColor(color)

        # Note the appeal of this board, with no captures
        standPatVal = evaluateBoardLikability(color, board, self.heuristicWgts)

        # Build up a list of capture moves

        moveChoices = enumMoves(board, color)
        moveFilterFunct = lambda m: (
            (board[m[1]] is not None) and (board[m[1]].color == otherColor))
        captureMoves = filter(moveFilterFunct, moveChoices)

        # Determine whether captures are a good or a bad thing
        if isMaxNode:

            # Check whether it is even worth proceeding with evaluation
            if (standPatVal > beta):
                return (None, beta)
            elif (alpha < standPatVal):
                alpha = standPatVal

            # Evaluate all captures
            for capMv in captureMoves:
                boardMoveUndoDict = board.getPlyResult(capMv[0], capMv[1])
                moveResultScore = evaluateBoardLikability(
                    otherColor, board, self.heuristicWgts)
                board.undoPlyResult(boardMoveUndoDict)

                # Don't bother searching outside of target range
                if (moveResultScore > beta):
                    return (None, beta)

                # Check whether we've found something superior to our best
                elif (moveResultScore > alpha):
                    alpha = moveResultScore

            # All captures for this node have been evaluated - return best
            return (None, alpha)
        else:
            # Check whether it is even worth proceeding with evaluation
            if (standPatVal < alpha):
                return (None, alpha)
            elif (beta > standPatVal):
                beta = standPatVal

            # Evaluate all captures
            for capMv in captureMoves:
                boardMoveUndoDict = board.getPlyResult(capMv[0], capMv[1])
                moveResultScore = evaluateBoardLikability(
                    otherColor, board, self.heuristicWgts)
                board.undoPlyResult(boardMoveUndoDict)

                # Don't bother searching outside of target range
                if (moveResultScore > alpha):
                    return (None, alpha)

                # Check whether we've found something superior to our best
                elif (moveResultScore < beta):
                    beta = moveResultScore

            # All captures for this node have been evaluated - return best
            return (None, beta)
Beispiel #10
0
def getBoardWD4():
    board = ChessBoard()
    # TODO (mattsh): Is this what we want to do?
    board.getPlyResult(ChessPosn(1, 3), ChessPosn(3, 3))
    return board
Beispiel #11
0
def evaluateBoardLikability(color, board, weightDict):
    """Return a number in [-1,1] based on board's likability to color

    @param color: One of maverick.data.ChessBoard.WHITE or
                   maverick.data.ChessBoard.BLACK
    @param board: A ChessBoard Object
    @param weightDict: A dictionary describing the weights to use for the
                        various heuristics, of form
                        {'pieceValWeight': pieceValWeight,
                        'inCheckWeight': inCheckWeight,
                        'pcsUnderAttackWeight': pcsUnderAttackWeight,
                        'emptySpaceCvgWeight': emptySpaceCoverageWeight,
                        'piecesCoveredWeight': piecesCoveredWeight}

    @return: a number in [-1,1] indicating the likability of the given
    board state for the given color, where -1 is least likable and 1 is
    most likable.

    The calculated value has the following properties:
     - Lower numbers indicate greater likelihood of a bad outcome (loss)
     - Higher numbers indicate greater likelihood of a good outcome (win)
     - -1 means guaranteed loss
     - 0 means neither player is favored; can mean a state of draw
     - +1 means guaranteed win"""

    # Pairing of heuristics with their weights
    pieceValueWeight = weightDict['pieceValWeight']
    inCheckWeight = weightDict['inCheckWeight']
    piecesUnderAttackWeight = weightDict['pcsUnderAttackWeight']
    emptySpaceCoverageWeight = weightDict['emptySpaceCvgWeight']
    piecesCoveredWeight = weightDict['piecesCoveredWeight']  # TODO (mattsh)

    # Determine opposing player color
    otherColor = ChessBoard.getOtherColor(color)

    # Check to see if either player is checkmated and return appropriately
    if board.isKingCheckmated(otherColor):
        return 1
    elif board.isKingCheckmated(color):
        return -1
    else:

        # Data structure of 'opinions' from heuristics
        # Format: ListOf[("Name", weight, value)]
        opinions = []

        ## TODO (James): Clean up the code below a bit - there has to be
        #                a cleaner way to do this

        # Add piece value opinion
        pieceValueFriend = heuristicPieceValue(otherColor, board)
        pieceValueFoe = heuristicPieceValue(color, board)
        pieceValueRes = combineHeuristicValues(pieceValueFriend, pieceValueFoe)
        opinions.append(("PieceValue", pieceValueWeight, pieceValueRes))

        # Add in check opinion
        inCheckFriend = heuristicInCheck(color, board)
        inCheckFoe = heuristicInCheck(otherColor, board)
        inCheckRes = combineHeuristicValues(inCheckFriend,
                                                    inCheckFoe)
        opinions.append(("InCheck", inCheckWeight, inCheckRes))

        # Add pieces under attack opinion
        pcsUnderAtkFriend = heuristicPcsUnderAttack(color, board)
        pcsUnderAtkFoe = heuristicPcsUnderAttack(otherColor,
                                                          board)
        pcsUnderAtkRes = combineHeuristicValues(pcsUnderAtkFriend,
                                                     pcsUnderAtkFoe)
        opinions.append(("PiecesUnderAttack", piecesUnderAttackWeight,
                        pcsUnderAtkRes))

        # Add empty space coverage opinion
        emptySpcsCvdFriend = heuristicEmptySpaceCvrg(color, board)
        emptySpcsCvdFoe = heuristicEmptySpaceCvrg(otherColor, board)
        emptySpcsCvdRes = combineHeuristicValues(emptySpcsCvdFriend,
                                                      emptySpcsCvdFoe)
        opinions.append(("EmptySpaceCoverage", emptySpaceCoverageWeight,
                        emptySpcsCvdRes))

        ## TODO (James): Re-enable this when it is more efficient

        # Add pieces covered opinion
#        pcsCoveredFriend = heuristicPiecesCovered(color, board)
#        pcsCoveredFoe = heuristicPiecesCovered(otherColor, board)
#        pcsCoveredRes = combineHeuristicValues(pcsCoveredFriend,
#                                                    pcsCoveredFoe)
#        opinions.append(("PiecesCovered", piecesCoveredWeight,
#                        pcsCoveredRes))

        # Return the weighted average
        return sum([weight * value for (_, weight, value) in opinions]) / \
            sum([weight for (_, weight, _) in opinions])
Beispiel #12
0
def heuristicEmptySpaceCvrg(color, board):
    """Return a value representing the number of empty squares controlled

    @param color: one of maverick.data.ChessBoard.WHITE or
                maverick.data.ChessBoard.BLACK
    @param board: a ChessBoard object

    @return: a value representing the number of empty squares that the
            given color can attack on the given board, with weight for
            center squares"""

    # The value of regular and center squares
    ## TODO (James): research and tweak these.
    #                See http://tinyurl.com/cpjqnw4l
    centerSquareValue = 2
    squareValue = 1

    # Build up a list of all piece locations as tuples

    pieceLocations = []
    otherColor = ChessBoard.getOtherColor(color)

    # TODO (mattsh): Not sure, do we want to add both ours and theirs?

    # Find friendly piece locations and add to pieceLocations
    for piecePosn in board.getPiecesOfColor(color):
        pieceLocations.append(piecePosn)

    # Find enemy piece locations and add to pieceLocations
    for enemyPiecePosn in board.getPiecesOfColor(otherColor):
        pieceLocations.append(enemyPiecePosn)

    # Build list of empty squares

    emptyLocations = []

    # Check each location to see if it is occupied
    for r in range(0, ChessBoard.BOARD_LAYOUT_SIZE):
        for f in range(0, ChessBoard.BOARD_LAYOUT_SIZE):
            testPosn = ChessPosn(r, f)

            if testPosn not in pieceLocations:
                emptyLocations.append(testPosn)

    # Build list of possible friendly piece moves
    friendlyMoves = enumMoves(board, color)
    friendlyMoveDestPosns = map(lambda x: x[1], friendlyMoves)

    # Find possible moves to empty squares and build up return value

    # Accumulator for return value
    weightedReturn = 0

    for dest in emptyLocations:

        # Check if a move can be made to that Posn
        if dest in friendlyMoveDestPosns:

            #Check if it is a center square
            if __heuristicEmptySpaceCoverage_isCenterSquare(dest):
                weightedReturn += centerSquareValue
            else:
                weightedReturn += squareValue

    # Calculate total weight of empty squares on board
    totalEmptyPosnWeight = 0
    for posn in emptyLocations:
        if __heuristicEmptySpaceCoverage_isCenterSquare(posn):
            totalEmptyPosnWeight += centerSquareValue
        else:
            totalEmptyPosnWeight += squareValue

    # Compress return value into range [-1..1]
    return -1 + weightedReturn / totalEmptyPosnWeight * 2
    def _boardSearch(self, board, color, depth, alpha, beta,
                     isMaxNode, stopSrchTime):
        """Performs a board via alpha-beta pruning/quiescence search

        NOTE: Not guaranteed to stop promptly at stopSrchTime - may take some
        time to terminate. Leave a time buffer.

        Selectively explores past the final depth if many pieces have
        been captured recently, by calling quiescent search

        @param board: The starting board state to evaluate
        @param color: The color of the player to generate a move for
        @param depth: The number of plies forward that should be explored
        @param alpha: Nodes with a likability below this will be ignored
        @param beta: Nodes with a likability above this will be ignored
        @param isMaxNode: Is this a beta node? (Is this node seeking to
                        maximize the value of child nodes?)
        @param stopSrchTime: Time at which the search should begin to terminate
        @param nodesVisited: The number of nodes already searched

        @return: A tuple with the following elements:
                1. None, or a move of the form (fromChessPosn, toChessPosn)
                    representing the next move that should be made by the given
                    player
                2. The likability of this move's path in the tree, as followed
                    by the search and as determined by the likability of the
                    leaf node terminating the path
                3. The number of nodes visited in the search

        Implementation based on information found here: http://bit.ly/t1dHKA"""
        ## TODO (James): Check timeout less than once per iteration

        ## TODO (James): Make logging conditional - temporarily disabled

        ## TODO (James): Set this to True before handing in!
        # Whether to limit based on wall clock time or number of nodes seen
        USE_WALL_CLOCK = False

        # Number of nodes to visit (for when wall clock time is not used)
        NUM_NODES_TO_VISIT = 1200

        #logStrF = "Performing minimax search to depth {0}.".format(depth)
        #QLAI._logger.debug(logStrF)

        # Note that we've visited a node
        nodesVisited = 1

        otherColor = ChessBoard.getOtherColor(color)

        # Check if we are at a leaf node
        if (depth == 0):
            (a, b) = self._quiescentSearch(board, color, alpha, beta,
                                           isMaxNode)
            return (a, b, 1)

        # Check if we should otherwise terminate
        elif (time() > stopSrchTime or
              board.isKingCheckmated(color) or
              board.isKingCheckmated(otherColor)):
            return (None, evaluateBoardLikability(color, board,
                                                  self.heuristicWgts),
                    nodesVisited)

        else:
            moveChoices = enumMoves(board, color)
            #logStrF = "Considering {0} poss. moves".format(len(moveChoices))
            #QLAI._logger.debug(logStrF)

            # Check whether seeking to find minimum or maximum value
            if isMaxNode:
                newMin = alpha
                newMoveChoice = None
                for move in moveChoices:

                    # Rather than calling getPlyResult, use THIS board. Much
                    # faster. REMEMBER TO UNDO THIS HYPOTHETICAL MOVE

                    # Save the old flag sets so they can be restored
                    boardMoveUndoDict = board.getPlyResult(move[0], move[1])

                    # Find the next move for this node, and how likable the
                    # enemy will consider this child node
                    (_, nodeEnemyLikability, nVisit) = self._boardSearch(board,
                                                                 otherColor,
                                                                 depth - 1,
                                                                 newMin, beta,
                                                                 not isMaxNode,
                                                                 stopSrchTime)
                    # RESTORE THE OLD BOARD STATE - VERY IMPORTANT
                    board.undoPlyResult(boardMoveUndoDict)

                    # Note how many more nodes we've visited
                    nodesVisited += nVisit

                    # Make note of the least likable branches that it still
                    # makes sense to pursue, given how likable this one is
                    if nodeEnemyLikability > newMin:
                        newMin = nodeEnemyLikability
                        newMoveChoice = move

                    # Don't search outside of the target range
                    elif nodeEnemyLikability > beta:
                        #QLAI._logger.debug("Pruning because new value > beta")
                        return (move, beta, nodesVisited)

                    # Check to see if we've evaluated the max number of nodes
                    if ((not USE_WALL_CLOCK) and
                        (nodesVisited > NUM_NODES_TO_VISIT)):
                        return (newMoveChoice, newMin, nodesVisited)

                return (newMoveChoice, newMin, nodesVisited)
            else:
                newMax = beta
                newMoveChoice = None
                for move in moveChoices:

                    # Rather than calling getPlyResult, use THIS board. Much
                    # faster. REMEMBER TO UNDO THIS HYPOTHETICAL MOVE

                    # Save the old flag sets so they can be restored
                    boardMoveUndoDict = board.getPlyResult(move[0], move[1])

                    # Find how likable the enemy will consider this child node
                    (_, nodeEnemyLikability, nVisit) = self._boardSearch(board,
                                                                 otherColor,
                                                                 depth - 1,
                                                                 alpha, newMax,
                                                                 not isMaxNode,
                                                                 stopSrchTime)

                    # RESTORE THE OLD BOARD STATE - VERY IMPORTANT:
                    board.undoPlyResult(boardMoveUndoDict)

                    # Note how many more nodes we've visited
                    nodesVisited += nVisit

                    # Make note of the most likable branches that it still
                    # makes sense to pursue, given how likable this one is
                    if nodeEnemyLikability < newMax:
                        newMax = nodeEnemyLikability
                        newMoveChoice = move

                    # Don't bother searching outside of our target range
                    elif nodeEnemyLikability < alpha:
                        #QLAI._logger.debug("pruning because new val < alpha")
                        return (move, alpha, nodesVisited)

                    # Check to see if we've evaluated the max number of nodes
                    if ((not USE_WALL_CLOCK) and
                        (nodesVisited > NUM_NODES_TO_VISIT)):
                        return (newMoveChoice, newMin, nodesVisited)
                return (newMoveChoice, newMax, nodesVisited)
    def _quiescentSearch(self, board, color, alpha, beta, isMaxNode):
        """Perform a quiescent search on the given board, examining captures

        Enumerates captures, and evaluates them to see if they alter results

        @param board: The starting board state to evaluate
        @param color: The color of the player to generate a move for
        @param alpha: Nodes with a likability below this will be ignored
        @param beta: Nodes with a likability above this will be ignored
        @param isMaxNode: Is this a beta node? (Is this node seeking to
                        maximize the value of child nodes?)

        @return: A tuple with the following elements:
                1. None, or a move of the form (fromChessPosn, toChessPosn)
                    representing the next move that should be made by the given
                    player
                2. The likability of this move's path in the tree, as followed
                    by the search and as determined by the likability of the
                    leaf node terminating the path

        No timeout is allowed. This shouldn't take long, anyway. If it does,
        then we're doing good things.

        Note: this was influenced by information here: http://bit.ly/VYlJVC """

        otherColor = ChessBoard.getOtherColor(color)

        # Note the appeal of this board, with no captures
        standPatVal = evaluateBoardLikability(color, board,
                                              self.heuristicWgts)

        # Build up a list of capture moves

        moveChoices = enumMoves(board, color)
        moveFilterFunct = lambda m: ((board[m[1]] is not None) and
                                    (board[m[1]].color == otherColor))
        captureMoves = filter(moveFilterFunct, moveChoices)

        # Determine whether captures are a good or a bad thing
        if isMaxNode:

            # Check whether it is even worth proceeding with evaluation
            if (standPatVal > beta):
                return (None, beta)
            elif (alpha < standPatVal):
                alpha = standPatVal

            # Evaluate all captures
            for capMv in captureMoves:
                boardMoveUndoDict = board.getPlyResult(capMv[0], capMv[1])
                moveResultScore = evaluateBoardLikability(otherColor, board,
                                                          self.heuristicWgts)
                board.undoPlyResult(boardMoveUndoDict)

                # Don't bother searching outside of target range
                if (moveResultScore > beta):
                    return (None, beta)

                # Check whether we've found something superior to our best
                elif (moveResultScore > alpha):
                    alpha = moveResultScore

            # All captures for this node have been evaluated - return best
            return (None, alpha)
        else:
            # Check whether it is even worth proceeding with evaluation
            if (standPatVal < alpha):
                return (None, alpha)
            elif (beta > standPatVal):
                beta = standPatVal

            # Evaluate all captures
            for capMv in captureMoves:
                boardMoveUndoDict = board.getPlyResult(capMv[0], capMv[1])
                moveResultScore = evaluateBoardLikability(otherColor, board,
                                                          self.heuristicWgts)
                board.undoPlyResult(boardMoveUndoDict)

                # Don't bother searching outside of target range
                if (moveResultScore > alpha):
                    return (None, alpha)

                # Check whether we've found something superior to our best
                elif (moveResultScore < beta):
                    beta = moveResultScore

            # All captures for this node have been evaluated - return best
            return (None, beta)
Beispiel #15
0
def getBoardWD4():
    board = ChessBoard()
    # TODO (mattsh): Is this what we want to do?
    board.getPlyResult(ChessPosn(1, 3), ChessPosn(3, 3))
    return board
Beispiel #16
0
def getBoardNew():
    return ChessBoard()