Example #1
0
        /// <summary>
        /// Returns a ChessPiece at a given file:rank.  It's very possible there
        /// is no piece and if so returns null
        /// </summary>
        /// <param name="file">ChessFile to check against</param>
        /// <param name="rank">Rank to check against</param>
        /// <returns>ChessPiece object at the given file:rank or null if not found</returns>
        public ChessPiece FindPieceAt(PieceFile file, int rank)
        {
            ChessPiece result = null;
            // Check each piece in the list (32 max)
            List <ChessPiece> pieces = AllPieces;

            foreach (ChessPiece piece in pieces)
            {
                // Should only ever be 1 in the list at any given location visible
                if (IsPieceAtLocation(piece, file, rank))
                {
                    result = piece; // Again we can stop on the 1st hit
                    break;
                }
            }
            return(result);
        }
Example #2
0
        /// <summary>
        /// Moves the rook for a castle move of a King.  At this point the castling
        /// has (or should have) been detected.  This is called to bring the rook
        /// along for the ride when the king is moved. See IsCastling for detection
        /// </summary>
        /// <param name="targetFile">ChessFile the King is moving to</param>
        /// <param name="moveInfo">Extended move information</param>
        private void PerformCastle(PieceFile targetFile, ref MoveInformation moveInfo)
        {
            // Find the corresponding Rook and move it too
            int       rookRank    = (activePlayer == PieceColor.White) ? 1 : 8;
            PieceFile gTargetFile = new PieceFile('g');
            PieceFile cTargetFile = new PieceFile('c');
            PieceFile rookStartFile;
            PieceFile rookTargetFile;

            // These are the only 2 legal files to move a king when castling
            // and they are the same for both players (only the rank differs above)
            if (targetFile == gTargetFile)
            {
                rookStartFile  = new PieceFile('h');
                rookTargetFile = new PieceFile('f');
            }
            else if (targetFile == cTargetFile)
            {
                rookStartFile  = new PieceFile('a');
                rookTargetFile = new PieceFile('d');
            }
            else
            {
                // If the above chess logic was sound, this should not happen.
                throw new ArgumentOutOfRangeException();
            }

            // Get the rook (which should exist if logic is sound)
            ChessPiece castleRook = FindPieceAt(rookStartFile, rookRank);

            // Move it
            castleRook.Move(rookTargetFile, rookRank);

            // Save the castling info
            moveInfo.CastlingRook = castleRook;

            // Remove all castling rights for the active player
            if (activePlayer == PieceColor.White)
            {
                whiteCastlingRights = BoardSide.None;
            }
            else
            {
                blackCastlingRights = BoardSide.None;
            }
        }
Example #3
0
        /// <summary>
        /// Find the rect for a given location on the board
        /// </summary>
        /// <param name="file">File for the piece</param>
        /// <param name="rank">Rank for the piece</param>
        /// <returns>Rectangle for the board location in client coordinates</returns>
        private Rectangle GetRect(PieceFile file, int rank)
        {
            // This simple diagram helps demonstrate the inverted relationship
            // (not pictured is the overall board offset)
            //
            //       Black                White
            //   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
            // 8 | |X| |X| |X| |X|   | |X| |X| |X| |X| 1
            //   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
            // 7 |X| |X| |X| |X| |   |X| |X| |X| |X| | 2
            //   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
            // 6 | |X| |X| |X| |X|   | |X| |X| |X| |X| 3
            //   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
            // 5 |X| |X| |X| |X| |   |X| |X| |X| |X| | 4
            //   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
            // 4 | |X| |X| |X| |X|   | |X| |X| |X| |X| 5
            //   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
            // 3 |X| |X| |X| |X| |   |X| |X| |X| |X| | 6
            //   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
            // 2 | |X| |X| |X| |X|   | |X| |X| |X| |X| 7
            //   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
            // 1 |X| |X| |X| |X| |   |X| |X| |X| |X| | 8
            //   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
            //    a b c d e f g h     h g f e d c b a
            //       White                Black
            int X;
            int Y;

            // PieceFile is 1-based, same as the physical board
            if (data.Orientation == BoardOrientation.WhiteOnBottom)
            {
                X = (file.ToInt() - 1) * squareSizeInPixels;
                Y = (8 - rank) * squareSizeInPixels;
            }
            else
            {
                X = (8 - file.ToInt()) * squareSizeInPixels;
                Y = (rank - 1) * squareSizeInPixels;
            }

            // Return the calculated rect offset from the overall topLeft location
            return(new Rectangle(X + topLeft.X, Y + topLeft.Y, squareSizeInPixels, squareSizeInPixels));
        }
Example #4
0
        /// <summary>
        /// For pawn moves, this check if the move was "en-passant" (in-passing) and if
        /// so, it will remove the captured piece.  It is assumed the move being
        /// checked is for a pawn
        /// </summary>
        /// <param name="startFile">ChessFile the piece originated from</param>
        /// <param name="startRank">Rank the piece originated from </param>
        /// <param name="targetFile">ChessFile the piece is moving to</param>
        /// <param name="targetRank">Rank the piece is moving to</param>
        /// <returns>captured piece or null</returns>
        private ChessPiece HandleEnPassant(PieceFile startFile, int startRank, PieceFile targetFile, int targetRank)
        {
            ChessPiece enPassantVictim = null;

            if ((startFile != targetFile) &&                     // Diagonal move
                (!IsAnyPieceAtLocation(targetFile, targetRank))) // There is some piece behind us
            {
                int enPassantTargetRank = startRank;             // Our potential target rank

                // This should never return a null object if the chess logic around it is sound
                // as a diagonal move (already detected) is not possible otherwise for a pawn
                // that did not make a capture (already detected)
                enPassantVictim = FindPieceAt(targetFile, enPassantTargetRank);

                // Capture the piece
                enPassantVictim.Captured = true;
            }
            return(enPassantVictim);
        }
Example #5
0
        /// <summary>
        /// When a pawn has made it to the back rank, it can be promoted.  This method
        /// will mark a piece as needing promotion on the next move.  We don't want to
        /// change the job until it has moved to keep inline with the rest of the logic
        /// </summary>
        /// <param name="startFile"></param>
        /// <param name="startRank"></param>
        /// <param name="targetFile"></param>
        /// <param name="targetRank"></param>
        /// <param name="promotionClass"></param>
        /// <param name="moveInfo">Detailed move info</param>
        public void PromotePiece(PieceFile startFile, int startRank, PieceFile targetFile, int targetRank, PieceClass promotionClass, ref MoveInformation moveInfo)
        {
            ChessPiece piece     = FindPieceAt(startFile, startRank);
            int        validRank = (piece.Color == PieceColor.White) ? 8 : 1;

            if (targetRank != validRank)
            {
                throw new ArgumentOutOfRangeException();
            }

            // Find the pawn and mark it
            if (piece.Job != PieceClass.Pawn)
            {
                // Logic check
                throw new InvalidOperationException();
            }
            moveInfo.PromotionJob = promotionClass;
            piece.PromoteOnNextMove(promotionClass);
        }
Example #6
0
        /// <summary>
        /// Returns true if any ChessPiece is located at the given file:rank
        /// </summary>
        /// <param name="file">ChessFile to check against</param>
        /// <param name="rank">Rank to check against</param>
        /// <returns>true if any piece is found</returns>
        private bool IsAnyPieceAtLocation(PieceFile file, int rank)
        {
            bool result = false;

            // Check each piece in the list (32 max)
            List <ChessPiece> pieces = AllPieces;

            foreach (ChessPiece piece in pieces)
            {
                // Only 1 piece in the list should ever return true for this
                // if the chess logic is sound (non-visible or captured pieces
                // will still be in the list though) so we can stop on the 1st hit
                if (IsPieceAtLocation(piece, file, rank))
                {
                    result = true;
                    break;
                }
            }
            return(result);
        }
Example #7
0
 /// <summary>
 /// Highlight a single square
 /// </summary>
 /// <param name="file">[a-h] file</param>
 /// <param name="rank">[1-8] rank</param>
 void IChessBoardView.HighlightSquare(PieceFile file, int rank)
 {
     highlightedSquares.Add(new BoardSquare(file, rank));
 }
Example #8
0
 /// <summary>
 /// Save the file, rank for the location
 /// </summary>
 /// <param name="file">[a-h]</param>
 /// <param name="rank">[1-8]</param>
 public BoardSquare(PieceFile file, int rank)
 {
     pieceFile = file;
     pieceRank = rank;
 }
Example #9
0
 /// <summary>
 /// Copy constructor
 /// </summary>
 /// <param name="oldFile"></param>
 public PieceFile(PieceFile oldFile)
 {
     pieceFile = oldFile.pieceFile;
 }
Example #10
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));
 }
Example #11
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);
        }
Example #12
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);
        }
Example #13
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);
        }