示例#1
0
        /// <summary>
        /// Checks if a given piece can target a specified square.  It is not required
        /// that the square be occupied by an enemy piece, just potentially reachable
        /// </summary>
        /// <param name="board">ChessBoard to check against</param>
        /// <param name="piece">ChessPiece to check</param>
        /// <param name="targetFile">file to target</param>
        /// <param name="targetRank">rank to target</param>
        /// <returns>true if the piece can target the given square</returns>
        public static bool CanPieceTargetSquare(ChessBoard board, ChessPiece piece, PieceFile targetFile, int targetRank)
        {
            bool result = false;

            if (piece.Captured == true)
            {
                return(false);
            }

            BoardSquare        targetSquare = new BoardSquare(targetFile, targetRank);
            List <BoardSquare> moves        = new List <BoardSquare>();

            switch (piece.Job)
            {
            case PieceClass.Pawn:
                // Can't reuse the normal helper as it requires the space to be occupied
                // also w/o en-passant and double moves, this can be simpler
                int pawnTargetRank = (piece.Color == PieceColor.White) ? piece.Rank + 1 : piece.Rank - 1;
                if (targetRank == pawnTargetRank)
                {
                    if (((piece.File.ToInt() - 1) == targetFile.ToInt()) ||
                        ((piece.File.ToInt() + 1) == targetFile.ToInt()))
                    {
                        result = true;
                    }
                }
                break;

            case PieceClass.Knight:
                moves = GetLegalMoves_Knight(piece, board);
                break;

            case PieceClass.Bishop:
                moves = GetLegalMoves_Bishop(piece, board);
                break;

            case PieceClass.Rook:
                moves = GetLegalMoves_Rook(piece, board);
                break;

            case PieceClass.Queen:
                moves = GetLegalMoves_Queen(piece, board);
                break;

            case PieceClass.King:
                // don't recurse into the normal call, also alternate method to examine
                // These are pairs of offsets (-1, 0), (-1, 1),...etc so there are twice
                // as many of these as squares to check
                int[] offsets = new int[] { -1, 0, -1, 1, -1, -1, 1, 0, 1, 1, 1, -1, 0, 1, 0, -1 };
                for (int index = 0; index < offsets.Length / 2; index++)
                {
                    int fileOffset = offsets[index * 2];
                    int rankOffset = offsets[(index * 2) + 1];
                    // Test the validity of the square offset
                    if (BoardSquare.IsValid(piece.File.ToInt() + fileOffset, piece.Rank + rankOffset))
                    {
                        BoardSquare testSquare =
                            new BoardSquare(new PieceFile(piece.File.ToInt() + fileOffset), piece.Rank + rankOffset);
                        if (testSquare == targetSquare)
                        {
                            result = true;
                            break;
                        }
                    }
                }
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            // King is special above
            if (piece.Job != PieceClass.King)
            {
                // Check moves for the target square
                foreach (BoardSquare square in moves)
                {
                    if (square == targetSquare)
                    {
                        result = true;
                        break;
                    }
                }
            }
            return(result);
        }
示例#2
0
        /// <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);
        }
示例#3
0
        /// <summary>
        /// Returns a list of valid squares a pawn 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_Pawn(ChessPiece piece, ChessBoard board)
        {
            List <BoardSquare> moves = new List <BoardSquare>();

            /* Pawns can move one space forward on any move provided the square is empty
             * and 2 squares if it's the first move the pawn has ever made.
             * A pawn may move diagonally if the square is occupied by an opponent piece (capture)
             * or if the space behind the diagonal is occuped by an opponent pawn that
             * just moved 2 spaces (en-passant)
             * +---+---+---+---+---+---+---+---+
             * |   |   |   |   |   |   |   |   |
             * +---+---+---+---+---+---+---+---+
             * |   |   |   |   |   |   |   |   |
             * +---+---+---+---+---+---+---+---+
             * |   |   |   |   |   |   |   |   |  C = Capture only
             * +---+---+---+---+---+---+---+---+  T = Move or capture
             * |   |   |   | M |   |   |   |   |  M = Move Only
             * +---+---+---+---+---+---+---+---+
             * |   |   | C | M | C |   |   |   |
             * +---+---+---+---+---+---+---+---+
             * |   |   |   | P |   |   |   |   |
             * +---+---+---+---+---+---+---+---+
             * |   |   |   |   |   |   |   |   |
             * +---+---+---+---+---+---+---+---+
             */
            // One rank "forward" which depends on your color
            int rank = (piece.Color == PieceColor.White) ? piece.Rank + 1 : piece.Rank - 1;

            if (null == board.FindPieceAt(piece.File, rank))
            {
                moves.Add(new BoardSquare(piece.File, rank));

                // The 2nd move is only valid of the 1st one was (i.e. you can't move through
                // another piece on your first pawn move)
                if (piece.Deployed == false)
                {
                    rank += (piece.Color == PieceColor.White) ? 1 : -1;
                    if (null == board.FindPieceAt(piece.File, rank))
                    {
                        moves.Add(new BoardSquare(piece.File, rank));
                    }
                }
            }

            // Get the en-passant target if it exists, most of the time it will not
            // it only exists the move after an enemy pawn has jumped 2 spaces on
            // its initial move.
            BoardSquare enPassantTarget;
            bool        enPassantValid = board.GetEnPassantTarget(out enPassantTarget);

            // Targets will ALWAYS be 1 rank away (enPassant target is behind piece captured)
            rank = (piece.Color == PieceColor.White) ? piece.Rank + 1 : piece.Rank - 1;

            // Lambda helper
            CheckPieceTargets checkPawnTargets = (p, fileOffset, rankOffset, b, m) =>
            {
                int newFileIndex = p.File.ToInt() + fileOffset;

                // Can't have diagonal targets on the back rank, or if we're up
                // against the edge we want to check towards
                if (!ChessPiece.IsOnBackRank(piece) && (newFileIndex > 0) && (newFileIndex <= 8))
                {
                    PieceFile   tFile        = new PieceFile(p.File.ToInt() + fileOffset);
                    BoardSquare targetSquare = new BoardSquare(tFile, rank);
                    ChessPiece  tPiece       = b.FindPieceAt(tFile, rank);

                    // Either there is a piece of the opposing color on this square
                    // or the sqaure is a valid enpassant target
                    if (((tPiece != null) && (tPiece.Color != p.Color)) ||
                        ((targetSquare == enPassantTarget) && enPassantValid))
                    {
                        m.Add(new BoardSquare(tFile, rank));
                    }
                }
            };

            // There are 2 possible Files (L,R or Kingside, Queenside, etc)
            // Diagonal left (lower file) PieceFile.ToInt() is 0-based since the
            // drawing code used it first...so adjust by 1 here
            checkPawnTargets(piece, -1, 0, board, moves);
            checkPawnTargets(piece, 1, 0, board, moves);
            return(moves);
        }
示例#4
0
        /// <summary>
        /// Helper to return all legal moves for the given piece.
        /// </summary>
        /// <param name="piece">piece to check, assumed to be valid</param>
        /// <param name="board">board to check agains, also assumed valid</param>
        /// <returns>List of squares the piece can legally move to</returns>
        public static List <BoardSquare> GetLegalMoves(ChessPiece piece, ChessBoard board)
        {
            List <BoardSquare> preCheckMoves = new List <BoardSquare>();

            // Get a list of legal moves ignoring check violations
            switch (piece.Job)
            {
            case PieceClass.Pawn:
                preCheckMoves = GetLegalMoves_Pawn(piece, board);
                break;

            case PieceClass.Knight:
                preCheckMoves = GetLegalMoves_Knight(piece, board);
                break;

            case PieceClass.Bishop:
                preCheckMoves = GetLegalMoves_Bishop(piece, board);
                break;

            case PieceClass.Rook:
                preCheckMoves = GetLegalMoves_Rook(piece, board);
                break;

            case PieceClass.Queen:
                preCheckMoves = GetLegalMoves_Queen(piece, board);
                break;

            case PieceClass.King:
                preCheckMoves = GetLegalMoves_King(piece, board);
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            // Eliminate any move found that would place the King in check
            List <BoardSquare> legalMoves = new List <BoardSquare>();
            ChessPiece         playerKing = board.GetKing(piece.Color);

            foreach (BoardSquare square in preCheckMoves)
            {
                // Move the piece for the check - also remove any capture for the test
                ChessPiece tempCapture = board.FindPieceAt(square.File, square.Rank);
                piece.TempMove(square.File, square.Rank);
                if (tempCapture != null)
                {
                    tempCapture.Captured = true;
                }

                if (!IsSquareInCheck(board, playerKing.File, playerKing.Rank, playerKing.Color))
                {
                    legalMoves.Add(square);
                }

                if (tempCapture != null)
                {
                    tempCapture.Captured = false;
                }

                // reset the piece (this bypasses the ChessBoard class)
                piece.ResetTempMove();
            }
            return(legalMoves);
        }
示例#5
0
 /// <summary>
 /// Checks if the piece is on the back rank (color matters)
 /// </summary>
 /// <param name="piece">ChessPiece to check</param>
 /// <returns>true if the piece is on the back rank (8 for white, 1 for black)</returns>
 public static bool IsOnBackRank(ChessPiece piece)
 {
     return((piece.Color == PieceColor.White) ? (piece.Rank == 8) : (piece.Rank == 1));
 }
示例#6
0
 /// <summary>
 /// Returns true if the given ChessPiece is located at the given file:rank
 /// and that piece is not captured (must be visible)
 /// </summary>
 /// <param name="piece">ChessPiece object to check</param>
 /// <param name="file">ChessFile to check against</param>
 /// <param name="rank">Rank to check against</param>
 /// <returns>true if piece is found</returns>
 private bool IsPieceAtLocation(ChessPiece piece, PieceFile file, int rank)
 {
     return(!piece.Captured && (piece.Rank == rank) && (piece.File == file));
 }
示例#7
0
        /// <summary>
        /// Move a piece from startFile:startRank -> targetFile:targetRank.  Because
        /// self-play is the only mode enabled right now, these moves are always
        /// going to be considered valid, since they came from the chess engine
        /// (and we will assume it is correct).  In the future, this will likely
        /// remain, and validation of the legallity for the player can be handled
        /// above this call
        /// </summary>
        /// <param name="moveInfo">detailed move information struct</param>
        public void MovePiece(ref MoveInformation moveInfo)
        {
            // Get the player piece at the starting location
            // Piece should never be null if chess logic is sound
            PieceFile startFile  = moveInfo.Start.File;
            int       startRank  = moveInfo.Start.Rank;
            PieceFile targetFile = moveInfo.End.File;
            int       targetRank = moveInfo.End.Rank;

            ChessPiece playerPiece = FindPieceAt(startFile, startRank);

            if (playerPiece.Color != activePlayer)
            {
                // This also should not be able to happen with correct game logic
                throw new InvalidOperationException();
            }

            // Get each side's pieces
            List <ChessPiece> playerPieces   = ActivePlayerPieces;
            List <ChessPiece> opponentPieces = OpponentPlayerPieces;

            // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            // PRE-MOVE CHECKS

            // We have to detect castling.  It does not come across the wire as O-O or O-O-O
            // but rather as a regular move like e1g1.  Separate the detection from the move
            // of the rook
            bool isCastling = IsCastling(playerPiece, targetFile);

            // We also need to check for an en-passant capture if the pieces is a pawn
            if (playerPiece.Job == PieceClass.Pawn)
            {
                moveInfo.CapturedPiece = HandleEnPassant(startFile, startRank, targetFile, targetRank);
            }

            // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            // RAW MOVE(S)
            playerPiece.Move(targetFile, targetRank);
            if (isCastling)
            {
                PerformCastle(targetFile, ref moveInfo); // Also move the rook if needed
            }

            // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            // POST-MOVE CHECKS/UPDATES

            // For normal captures, just do a quick iteration of the opponent pieces
            // there are only 16 of these total in normal chess
            foreach (ChessPiece enemyPiece in opponentPieces)
            {
                if ((enemyPiece.Rank == targetRank) &&   // Enemy piece is located in
                    (enemyPiece.File == targetFile) &&   // the square we just moved to
                    !enemyPiece.Captured)                // and it's not already captured
                {
                    enemyPiece.Captured    = true;       // Stop drawing it (capture)
                    moveInfo.CapturedPiece = enemyPiece; // Record the capture
                    break;                               // exit the search loop
                }
            }

            // save the last capture state for external callers
            lastMoveWasCapture = moveInfo.IsCapture;

            Moves.Add(moveInfo);

            // Update our FEN
            currentFEN     = FenParser.ApplyMoveToFEN(currentFEN, moveInfo.ToString());
            enPassantValid = FenParser.ExtractEnPassantTarget(currentFEN, out enPassantTarget);

            FenParser.ExtractCastlingRights(CurrentFEN, ref whiteCastlingRights, ref blackCastlingRights);
            FenParser.ExtractMoveCounts(CurrentFEN, ref halfMoveCount, ref fullMoveCount);

            // Flip players - easy to just do here rather than parse the FEN again
            activePlayer = OppositeColor(activePlayer);
        }