/// <summary> /// Converts BoardSide enum castling rights into a FEN compliant string /// </summary> /// <param name="castlingRights">Rights to convert</param> /// <param name="color">Color (for casing)</param> /// <returns>FEN compliant string for the BoardSide, e.g. "KQ"</returns> private static string TokenizeCastlingRights(BoardSide castlingRights, PieceColor color) { string result = String.Empty; if (castlingRights.HasFlag(BoardSide.King) & castlingRights.HasFlag(BoardSide.Queen)) { result = String.Concat(result, "kq"); } else if (castlingRights.HasFlag(BoardSide.King)) { result = String.Concat(result, "k"); } else if (castlingRights.HasFlag(BoardSide.Queen)) { result = String.Concat(result, "q"); } else { result = "-"; } if (color == PieceColor.White) { result = result.ToUpper(); } return(result); }
/// <summary> /// Checks if a given player is allowed to castle /// </summary> /// <param name="playerColor">Player to check</param> /// <param name="side">Side of the board to validate.</param> /// <returns>True if the player can castle on the given side</returns> public bool CanPlayerCastle(PieceColor playerColor, BoardSide side) { BoardSide playerSide = (playerColor == PieceColor.White) ? whiteCastlingRights : blackCastlingRights; return(playerSide.HasFlag(side)); }
/// <summary> /// Returns a list of valid squares a king can move to. The list can be empty /// </summary> /// <param name="piece">ChessPiece to examine</param> /// <param name="board">ChessBoard the piece exists within</param> /// <returns>List of valid squares, or empty list</returns> public static List <BoardSquare> GetLegalMoves_King(ChessPiece piece, ChessBoard board) { List <BoardSquare> moves = new List <BoardSquare>(); /* The King is special. He can move one square in any direction * (provided it is on the board) so long as the square is empty or * it has an opponent piece on it. However, the King can never * move into check, even if the square or capture would be legal * otherwise, so it requires some extra checking. * * Further complicating things, if the king is castling, he cannot * move THROUGH check either (basically check those squares as if * they were final destinations) * * +---+---+---+---+---+---+---+---+ * | | | | | | | | | * +---+---+---+---+---+---+---+---+ r = enemy rook (block) * | | | | | | p | | | T = Move or capture * +---+---+---+---+---+---+---+---+ p = enemy pawn (block) * | | | T | T | X | | b | | b = enemy bishop (block) * +---+---+---+---+---+---+---+---+ X = illegal move * | | | T | K | T | | | | * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+ * | | | T | T | X | | | | | K | T | | | | | * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+ * | | | | | | | | | | X | X | | | | r | * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+ * | | | | | | | | | | | | | | | | * +---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+ */ // Cannot castle if in check if (!IsSquareInCheck(board, piece.File, piece.Rank, piece.Color)) { /* Castling may only be done if the king has never moved, the rook involved has never moved, * the squares between the king and the rook involved are unoccupied, the king is not in check, * and the king does not cross over or end on a square in which it would be in check. * * The ChessBoard will keep track of the castling rights when various pieces move, but it * won't constantly update the legality of the move */ // Build a list of squares to check BoardSide castlingRights = (piece.Color == PieceColor.White) ? board.WhiteCastlingRights : board.BlackCastlingRights; BoardSide[] sidesToCheck = new BoardSide[2] { BoardSide.King, BoardSide.Queen }; foreach (BoardSide sideToCheck in sidesToCheck) { // Backrank depends on color int kingRank = (piece.Color == PieceColor.White) ? 1 : 8; BoardSquare[] squares = null; // First check if we still have the right, if not, no need to persue it if (castlingRights.HasFlag(sideToCheck)) { squares = new BoardSquare[2]; // The target Files depend on the side of the board we're checking // put the final target in [0] if (sideToCheck == BoardSide.King) { squares[0] = new BoardSquare(new PieceFile(7), kingRank); squares[1] = new BoardSquare(new PieceFile(6), kingRank); } else // Queenside { squares[0] = new BoardSquare(new PieceFile(3), kingRank); squares[1] = new BoardSquare(new PieceFile(4), kingRank); } } // There should be 2 and only 2 from above if we found potential targets if (squares != null) { // must be empty and not in check - empty is faster so verify it first if ((board.FindPieceAt(squares[0].File, squares[0].Rank) == null) && (board.FindPieceAt(squares[1].File, squares[1].Rank) == null)) { // Now make sure neither square is in check if (!IsSquareInCheck(board, squares[0].File, squares[0].Rank, piece.Color) && !IsSquareInCheck(board, squares[1].File, squares[1].Rank, piece.Color)) { // King can still castle to this side, add the move option moves.Add(squares[0]); } } } } } // Check each of the 8 squares around the king. If it's free or has // an enemy piece, then check if it's targetable by the opponent // (moving into check) If not, then add it to the list CheckPieceTargets checkKingTargets = (p, fileDelta, rankDelta, b, m) => { // Verify targets are reachable (not off edge) int startCol = p.File.ToInt(); int startRow = p.Rank; int endCol = startCol + (fileDelta); int endRow = startRow + (rankDelta); bool occupied; // ignored here if (SquareIsFreeOrContainsOpponent(endCol, endRow, b, p.Color, out occupied)) { m.Add(new BoardSquare(new PieceFile(endCol), endRow)); } }; // Check all 8 squares around the king checkKingTargets(piece, 0, 1, board, moves); checkKingTargets(piece, 0, -1, board, moves); checkKingTargets(piece, 1, 0, board, moves); checkKingTargets(piece, -1, 0, board, moves); checkKingTargets(piece, 1, 1, board, moves); checkKingTargets(piece, -1, -1, board, moves); checkKingTargets(piece, 1, -1, board, moves); checkKingTargets(piece, -1, 1, board, moves); // Check violations are handled by the common caller for regulatr moves return(moves); }