For a given position on the board, most chess engines are only launched using pseudo-legal moves. By pseudo-legal, I mean that a move will be generated, even if it:
- Leave the king in a cage
- Moves the king to the check
- Castles in the attacked squares
The reason for this is performance. Since many moves will not actually be searched due to beta reduction, you save time by avoiding a full check of the validity of the moves.
For each step that is sought, you must make sure that it is truly valid. This is usually done by passing the color and square of the King (and the squares next to the King for the castling move) to the IsAttacked method. If this method returns true, you know that this movement is not valid and therefore you should not include it in your search.
This is the IsAttacked method from my own C # chess engine. Keep in mind that my engine is a -based magic board , so the code will not directly apply to the chess starting set that you are associated with. If you are not familiar with magic bitboards, the translation will not be trivial.
// IsAttacked is primarily used as a move legality test to see if a set of // one or more squares is under attack from the side to move. // It returns true as soon as an attack is detected, otherwise returns false. // It can be used for check detection, castling legality, or simply to // detect whether a specific square is attacked. internal bool IsAttacked(Board board, UInt64 targetSquares, bool whiteAttacking) { UInt64 slidingAttackers; Int32 targetSquare; UInt64 remainingTargetSquares = targetSquares; // Test for attacks by WHITE on any of the target squares. if (whiteAttacking) { // For the remaining target squares... while (remainingTargetSquares != 0) { // Find the next square in the list. targetSquare = BitOperations.BitScanForward(remainingTargetSquares); // Is this square attacked by a pawn, knight, or king? if ((board.WhitePawns & Constants.BLACK_PAWN_ATTACKS[targetSquare]) != 0) return true; if ((board.WhiteKnights & Constants.KNIGHT_ATTACKS[targetSquare]) != 0) return true; if ((board.WhiteKing & Constants.KING_ATTACKS[targetSquare]) != 0) return true; // Is this square attacked by a queen or rook along a file or rank? slidingAttackers = board.WhiteQueens | board.WhiteRooks; if (slidingAttackers != 0) { if (this.RankMoves(board.OccupiedSquares, slidingAttackers, targetSquare) != 0) return true; if (this.FileMoves(board.OccupiedSquares, slidingAttackers, targetSquare) != 0) return true; } // Is this square attacked by a queen or bishop along a diagonal? slidingAttackers = board.WhiteQueens | board.WhiteBishops; if (slidingAttackers != 0) { if (this.DiagonalA8H1Moves(board.OccupiedSquares, slidingAttackers, targetSquare) != 0) return true; if (this.DiagonalA1H8Moves(board.OccupiedSquares, slidingAttackers, targetSquare) != 0) return true; } // This square isn't attacked - remove and move on to next target square. remainingTargetSquares ^= Constants.BITSET[targetSquare]; } } // Test for attacks by BLACK on any of the target squares. else { // For the remaining target squares... while (remainingTargetSquares != 0) { // Find the next square in the list. targetSquare = BitOperations.BitScanForward(remainingTargetSquares); // Is this square attacked by a pawn, knight, or king? if ((board.BlackPawns & Constants.WHITE_PAWN_ATTACKS[targetSquare]) != 0) return true; if ((board.BlackKnights & Constants.KNIGHT_ATTACKS[targetSquare]) != 0) return true; if ((board.BlackKing & Constants.KING_ATTACKS[targetSquare]) != 0) return true; // Is this square attacked by a queen or rook along a file or rank? slidingAttackers = board.BlackQueens | board.BlackRooks; if (slidingAttackers != 0) { if (this.RankMoves(board.OccupiedSquares, slidingAttackers, targetSquare) != 0) return true; if (this.FileMoves(board.OccupiedSquares, slidingAttackers, targetSquare) != 0) return true; } // Is this square attacked by a queen or bishop along a diagonal? slidingAttackers = board.BlackQueens | board.BlackBishops; if (slidingAttackers != 0) { if (this.DiagonalA8H1Moves(board.OccupiedSquares, slidingAttackers, targetSquare) != 0) return true; if (this.DiagonalA1H8Moves(board.OccupiedSquares, slidingAttackers, targetSquare) != 0) return true; } // This square isn't attacked - remove and move on to next target square. remainingTargetSquares ^= Constants.BITSET[targetSquare]; } } // None of the target squares are attacked. return false; }
Here is a snippet of code that generates pseudo-legal castling moves for whites:
// If White can still castle kingside... if ((board.WhiteCastlingStatus & Board.EnumCastlingStatus.CanCastleOO) != 0) { // And the White kingside castling squares (F1/G1) aren't occupied... if ((Constants.MASK_FG[Constants.WHITE_MOVE] & board.OccupiedSquares) == 0) { board.MoveBuffer[moveIndex++] = Constants.WHITE_CASTLING_OO; } } // If White can still castle queenside... if ((board.WhiteCastlingStatus & Board.EnumCastlingStatus.CanCastleOOO) != 0) { // And the White queenside castling squares (D1/C1/B1) aren't occupied... if ((Constants.MASK_BD[Constants.WHITE_MOVE] & board.OccupiedSquares) == 0) { board.MoveBuffer[moveIndex++] = Constants.WHITE_CASTLING_OOO; } }
And here is the code that checks if the pseudo-legal castling move is really legal:
// Checks whether the King is moving from or into check. // Checks whether the King is moving across attacked squares. internal bool IsCastlingMoveLegal(Board board, Move move) { if (move.IsCastlingOO) { if (move.IsWhiteMove) { // Are any of the White kingside castling squares (E1/F1/G1) attacked? return !this.IsAttacked(board, Constants.MASK_EG[Constants.WHITE_MOVE], false); } else { // Are any of the Black kingside castling squares (E8/F8/G8) attacked? return !this.IsAttacked(board, Constants.MASK_EG[Constants.BLACK_MOVE], true); } } else if (move.IsCastlingOOO) { if (move.IsWhiteMove) { // Are any of the White queenside castling squares (E1/D1/C1) attacked? return !this.IsAttacked(board, Constants.MASK_CE[Constants.WHITE_MOVE], false); } else { // Are any of the Black queenside castling squares (E8/D8/C8) attacked? return !this.IsAttacked(board, Constants.MASK_CE[Constants.BLACK_MOVE], true); } } // Not a castling move! else { Debug.Assert(false, "Not a castling move!"); return true; } }