Esempio n. 1
0
    def addPiece(self, location, colour, pieceType):
        # Create the piece
        piece = ChessPiece(colour, pieceType)

        # Put the piece in it's initial location
        assert(self.squares.has_key(location) is False)
        assert(type(location) == str)
        self.squares[location] = piece
        
        # Update the bitboards
        field = bitboard.LOCATIONS[bitboard.getIndex(location)]
        if colour is WHITE:
            self.whiteBitBoard |= field
        else:
            self.blackBitBoard |= field
        self.allBitBoard |= field

        return piece
Esempio n. 2
0
    def movePiece(self, colour, start, end, promotionType = QUEEN, testCheck = True, allowSuicide = False, applyMove = True):
        """Move a piece.
        
        'colour' is the colour of the player moving.
        'start' is a the location to move from in algebraic format (string).
        'end' is a the location to move to in algebraic format (string).
        'promotionType' is the type of piece to promote to if required.
        'testCheck' is a flag to control if the opponent will be in check after this move.
        'allowSuicide' if True means a move is considered valid even
                       if it would put the moving player in check.
        'applyMove' is a flag to control if the move is applied to the board (True) or just tested (False).
        
        Returns the pieces moved in the form (result, moves).
        The moves are a list containing tuples of the form (piece, start, end). If a piece was removed
        'end' is None. If the result is successful the pieces on the board are modified.
        If the move is illegal None is returned.
        """
        assert(promotionType is not KING)
        assert(type(start) is str and len(start) == 2)
        assert(type(end) is str and len(end) == 2)

        # Get the piece to move
        try:
            piece = self.squares[start]
        except KeyError:
            return None
        if piece.getColour() is not colour:
            return None

        # BitBoard indexes
        startIndex = bitboard.getIndex(start)
        endIndex = bitboard.getIndex(end)
        
        # Check if this move is possible
        field = self.allowedMoves[colour][piece.getType()]
        if field[startIndex] & bitboard.LOCATIONS[endIndex] == 0:
            return None
            
        # Check if there are any pieces between the two moves
        # Note this only checks horizontal, vertical and diagonal moves so
        # has no effect on the knights
        if self.allBitBoard & bitboard.INBETWEEN_SQUARES[startIndex][endIndex]:
            return None

        # Get the players
        if colour is WHITE:
            enemyColour = BLACK
            playerState = self.whiteState
        elif colour is BLACK:
            enemyColour = WHITE
            playerState = self.blackState
        else:
            assert(False)
        
        # Copy the player state before it is changed
        originalPlayerState = ChessPlayerState(playerState)
        
        whiteBitBoard = self.whiteBitBoard
        blackBitBoard = self.blackBitBoard
        allBitBoard = self.allBitBoard

        # Check if moving onto another piece (must be enemy)
        try:
            target = self.squares[end]
            if target.getColour() == colour:
                return None
        except KeyError:
            target = None
        victim = target
        
        # Get the rank relative to this colour's start rank
        if colour == BLACK:
            baseFile = '8'
        else:
            baseFile = '1'
            
        # The new en-passant square
        enPassantSquare = None
        
        # A list of pieces that have been moved
        moves = []
        
        # Check move is valid:

        # King can move one square or castle
        if piece.getType() is KING:
            # Castling:
            shortCastle = ('e' + baseFile, 'g' + baseFile)
            longCastle  = ('e' + baseFile, 'c' + baseFile)
            if (start, end) == shortCastle or (start, end) == longCastle:
                # Cannot castle out of check
                if self.inCheck(colour):
                    return None

                # Cannot castle if required pieces have moved
                if end[0] == 'c':
                    if not playerState.canLongCastle:
                        return None
                    rookLocation = 'a' + baseFile
                    rookEndLocation = 'd' + baseFile
                else:
                    if not playerState.canShortCastle:
                        return None
                    rookLocation = 'h' + baseFile
                    rookEndLocation = 'f' + baseFile

                # Check rook is still there
                try:
                    rook = self.squares[rookLocation]
                except KeyError:
                    return None
                if rook is None or rook.getType() is not ROOK or rook.getColour() != piece.getColour():
                    return None

                # Check no pieces between the rook and king
                a = bitboard.getIndex(rookLocation)
                b = bitboard.getIndex(rookEndLocation)
                if self.allBitBoard & bitboard.INBETWEEN_SQUARES[a][b]:
                    return None
                
                # The square the king moves over cannot be attackable
                if self.movePiece(colour, start, rookEndLocation, applyMove = False) is None:
                    return None

                # Rook moves with the king
                moves.append((rook, rookLocation, rookEndLocation, False))

            # Can no longer castle once the king is moved
            playerState.canShortCastle = False
            playerState.canLongCastle = False
            
            moves.append((piece, start, end, False))

        # Rooks move orthogonal
        elif piece.getType() is ROOK:
            # Can no longer castle once have move the required rook
            if start == 'a' + baseFile:
                playerState.canLongCastle = False
            elif start == 'h' + baseFile:
                playerState.canShortCastle = False
            moves.append((piece, start, end, False))

        # On base rank pawns move on or two squares forwards.
        # Pawns take other pieces diagonally (1 square).
        # Pawns can take other pawns moving two ranks using 'en passant'.
        # Pawns are promoted on reaching the other side of the board.
        elif piece.getType() is PAWN:
            # Calculate the files that pawns start on and move over on marches
            if baseFile == '1':
                pawnFile  = '2'
                marchFile = '3'
                farFile   = '8'
            else:
                pawnFile  = '7'
                marchFile = '6'
                farFile   = '1'
                
            # When marching the square that is moved over can be taken by en-passant
            if (start[1] == '2' and end[1] == '4') or (start[1] == '7' and end[1] == '5'):
                enPassantSquare = start[0] + marchFile
    
            # Can only take when moving diagonally
            if start[0] != end[0]:
                # FIXME: Set victim
                # We either need a victim or be attacking the en-passant square
                if victim is None:
                    if end != self.enPassantSquare:
                        return None
                    
                    # Kill the pawn that moved
                    moves.append((self.lastMove[0], self.lastMove[2], self.lastMove[2], True))
            elif victim is not None:
                return None
            
            # Promote pawns when they hit the far rank
            if end[1] == farFile:
                # Delete the current piece and create a new piece
                moves.append((piece, start, end, True))
                moves.append((ChessPiece(colour, promotionType), None, end, False))
            else:
                moves.append((piece, start, end, False))

        # Other pieces are well behaved
        else:
            moves.append((piece, start, end, False))

        # Store this move
        oldLastMove = self.lastMove
        self.lastMove = (piece, start, end)
        oldEnPassantSquare = self.enPassantSquare
        self.enPassantSquare = enPassantSquare

        # Delete a victim
        if victim is not None:
            moves.append((victim, end, end, True))
            
        # Move the pieces:

        # Remove the moving pieces from the board
        for (p, s, e, d) in moves:
            if s is None:
                continue
            self.squares.pop(s)
            
            field = bitboard.LOCATIONS[bitboard.getIndex(s)]
            self.whiteBitBoard &= ~field
            self.blackBitBoard &= ~field
            self.allBitBoard &= ~field
                
        # Put pieces in their new locations
        for (p, s, e, d) in moves:
            if d:
                continue
            self.squares[e] = p

            field = bitboard.LOCATIONS[bitboard.getIndex(e)]
            if p.getColour() is WHITE:
                self.whiteBitBoard |= field
            else:
                self.blackBitBoard |= field
            self.allBitBoard |= field

        # Test for check and checkmate
        result = moves
        if testCheck:
            # Cannot move into check, if would be then undo move
            if self.inCheck(colour):
                applyMove = False
                result = None

        # Undo the moves if only a test
        if applyMove is False:
            # Empty any squares moved into
            for (p, s, e, d) in moves:
                if not d:
                    self.squares.pop(e)
                        
            # Put pieces back into their original locatons
            for (p, s, e, d) in moves:
                if s is not None:
                    self.squares[s] = p

            # Undo player state
            if colour == WHITE:
                self.whiteState = originalPlayerState
            else:
                self.blackState = originalPlayerState
            
            # Undo stored move and en-passant location
            self.lastMove = oldLastMove
            self.enPassantSquare = oldEnPassantSquare

            # Revert bitboards
            self.whiteBitBoard = whiteBitBoard
            self.blackBitBoard = blackBitBoard
            self.allBitBoard = allBitBoard
            
        else:
            self.moves = result
            
            # Remember the casualties
            if victim is not None:
                self.casualties.append(victim)

            # If a piece taken or a pawn moved 50 move count is reset
            if victim is not None or piece.getType() is PAWN:
                self.fiftyMoveCount = 0
            else:
                self.fiftyMoveCount += 1
                
        return result
Esempio n. 3
0
    def __init__(self, lastState = None):
        """Constuctor for storing the state of a chess board.
        
        'lastState' is the previous board state
                    or a dictionary containing the initial state of the board
                    or None to start an empty board.

        Example:
            
        pawn = ChessPiece(WHITE, PAWN)
        ChessBoardState({'a2': pawn, ...})
        
        Note if a dictionary is provided the casualties will only record the pieces
        killed from this point onwards.
        """
        # Start empty
        if lastState is None:
            self.whiteBitBoard = 0
            self.blackBitBoard = 0
            self.allBitBoard = 0
            self.moveNumber = 0
            self.squares = {}
            self.casualties = []
            self.moves = []
            self.whiteState = ChessPlayerState()
            self.blackState = ChessPlayerState()
            
        # Use provided initial pieces
        elif type(lastState) is dict:
            self.moveNumber = 0
            self.squares = {}
            self.casualties = []
            self.moves = []            
            self.whiteBitBoard = 0
            self.blackBitBoard = 0
            self.allBitBoard = 0
            for coord, piece in lastState.iteritems():
                self.squares[coord] = piece
                field = bitboard.LOCATIONS[bitboard.getIndex(coord)]
                if piece.getColour() is WHITE:
                    self.whiteBitBoard |= field
                else:
                    self.blackBitBoard |= field
                self.allBitBoard |= field

            self.whiteState = ChessPlayerState()
            self.blackState = ChessPlayerState()

        # Copy exisiting state
        elif isinstance(lastState, ChessBoardState):
            self.whiteBitBoard = lastState.whiteBitBoard
            self.blackBitBoard = lastState.blackBitBoard
            self.allBitBoard = lastState.allBitBoard
            self.moveNumber = lastState.moveNumber + 1
            self.squares = lastState.squares.copy()
            self.casualties = lastState.casualties[:]
            self.lastMove = lastState.lastMove
            self.moves = lastState.moves[:]
            self.enPassantSquare = lastState.enPassantSquare
            self.whiteState = ChessPlayerState(lastState.whiteState)
            self.blackState = ChessPlayerState(lastState.blackState)
            self.fiftyMoveCount = lastState.fiftyMoveCount

        else:
            raise TypeError('ChessBoardState(oldState) or ChessBoardState({(0,0):pawn, ...})')