Example #1
0
        /// <summary>
        /// Checks if a square is either empty, or has an opponent piece in it
        /// It also performs bounds checking
        /// </summary>
        /// <param name="col">int based col (can be out of bounds)</param>
        /// <param name="row">int based row (can be out of bounds)</param>
        /// <param name="board">ChessBoard to check</param>
        /// <param name="playerColor">PieceColor of the player</param>
        /// <param name="occupied">set to true if an opponent piece is also there</param>
        /// <returns>true if the square is empty or contains an opponent piece</returns>
        public static bool SquareIsFreeOrContainsOpponent(int col, int row, ChessBoard board, PieceColor playerColor, out bool occupied)
        {
            bool result = false;

            occupied = false;
            if (BoardSquare.IsValid(col, row))
            {
                // Get the piece at the square if any
                ChessPiece tPiece = board.FindPieceAt(new PieceFile(col), row);
                // No piece...
                if (tPiece == null)
                {
                    result   = true;
                    occupied = false;
                }
                else // ...or opponent piece
                {
                    PieceColor opponentColor = (playerColor == PieceColor.White) ? PieceColor.Black : PieceColor.White;
                    if (tPiece.Color == opponentColor)
                    {
                        result   = true;
                        occupied = true;
                    }
                }
            }
            return(result);
        }
Example #2
0
 /// <summary>
 /// Create a new MoveInformation struct and fill in the minumum
 /// required information
 /// </summary>
 /// <param name="start">starting square</param>
 /// <param name="end">ending square</param>
 /// <param name="deployed">true if piece has ever moved, prior to this move</param>
 /// <param name="oldFEN">last FEN for board</param>
 public MoveInformation(BoardSquare start, BoardSquare end, bool deployed, string oldFEN)
 {
     prevFEN        = oldFEN;
     ancillaryPiece = null;
     promotionClass = PieceClass.King; // Invalid promotion job
     startSquare    = start;
     endSquare      = end;
     firstMove      = (deployed == false);
     color          = PieceColor.White; // assume white
     castlingRights = BoardSide.None;
     isCapture      = false;
     isCastle       = false;
 }
Example #3
0
        /// <summary>
        /// Process the input when waiting for the current player to select a move
        /// </summary>
        /// <param name="x">x coordinate in form</param>
        /// <param name="y">y coordinate in form</param>
        private void OnWaitingForMoveSelection(int x, int y)
        {
            BoardSquare square = ((IChessBoardView)view).GetSquare(x, y);

            foreach (BoardSquare move in legalMoves)
            {
                if (move == square)
                {
                    // Done - this is the move
                    MoveInformation moveInfo = new MoveInformation(
                        new BoardSquare(selectedPiece.File, selectedPiece.Rank), move, selectedPiece.Deployed, board.CurrentFEN);

                    moveInfo.Color          = selectedPiece.Color;
                    moveInfo.CastlingRights = board.ActivePlayerCastlingRights;

                    Debug.WriteLine("Valid Move Detected: [{0},{1}]=>[{2},{3}]",
                                    selectedPiece.File, selectedPiece.Rank, move.File, move.Rank);

                    // Need to detect promotion and launch dialog for it...
                    bool isPawnMovingToBackRank = (selectedPiece.Color == PieceColor.White) ? (moveInfo.End.Rank == 8) : (moveInfo.End.Rank == 1);
                    if ((selectedPiece.Job == PieceClass.Pawn) && isPawnMovingToBackRank)
                    {
                        PieceClass promotionJob = view.ChoosePromotionJob();
                        board.PromotePiece(moveInfo.Start.File, moveInfo.Start.Rank, moveInfo.End.File, moveInfo.End.Rank, promotionJob, ref moveInfo);
                    }

                    // Always returns true now
                    board.MovePiece(ref moveInfo);
                    view.Invalidate();

                    Debug.WriteLine(String.Format("Fullmoves: {0}", board.FullMoveCount));
                    Debug.WriteLine(String.Format("Halfmoves: {0}", board.HalfMoveCount));
                    Debug.WriteLine(String.Format("WhCastle: {0}", board.WhiteCastlingRights.ToString()));
                    Debug.WriteLine(String.Format("BlCastle: {0}", board.BlackCastlingRights.ToString()));

                    // Update the position with the engine
                    UpdateEnginePosition();
                    break;
                }
            }

            // Either way this gets cleared
            legalMoves.Clear();
            ((IChessBoardView)view).ClearHiglightedSquares();
            selectedPiece = null;
        }
Example #4
0
        /// <summary>
        /// Extracts just the enpassant target if any from the FEN
        /// </summary>
        /// <param name="fen">FEN string to parse</param>
        /// <param name="enPassantSquare">BoardSquare with the target if return is true</param>
        /// <returns>If true, enPassantSquare holds the target, otherwise it will be a1(never valid)</returns>
        public static bool ExtractEnPassantTarget(string fen, out BoardSquare enPassantSquare)
        {
            string[] fenTokens = TokenizeFEN(fen);
            bool     result    = false;
            string   enpassant = fenTokens[3];

            enPassantSquare = new BoardSquare(new PieceFile('a'), 1);

            // '-' or a square like e5
            if (String.Compare(enpassant, "-") != 0)
            {
                result          = true;
                enPassantSquare = new BoardSquare(new PieceFile(enpassant[0]),
                                                  Convert.ToInt16(enpassant[1]) - Convert.ToUInt16('0'));
            }
            return(result);
        }
Example #5
0
 /// <summary>
 /// Returns the current en-passant target square (if any)
 /// </summary>
 /// <param name="target">BoardSquare that is the en-passant target</param>
 /// <returns>true if target exists, in which case 'target' contains the square.
 /// false if there is no target, and the contents of 'target' are invalid</returns>
 public bool GetEnPassantTarget(out BoardSquare target)
 {
     target = enPassantTarget;
     return(enPassantValid);
 }
Example #6
0
        /// <summary>
        /// Produces a new FEN given a FEN and a move string
        /// </summary>
        /// <param name="fen">Starting FEN</param>
        /// <param name="sanMove">move e.g. e2e4 or d7c8q</param>
        /// <returns>Updated FEN for new position</returns>
        public static string ApplyMoveToFEN(string fen, string sanMove)
        {
            // Extract the original pieces of the FEN
            string[] fenTokens = TokenizeFEN(fen);

            // Extract start and end squares and promotion info
            BoardSquare startSquare = new BoardSquare(
                new PieceFile(sanMove[0]), (Convert.ToInt16(sanMove[1]) - Convert.ToInt16('0')));
            BoardSquare endSquare = new BoardSquare(
                new PieceFile(sanMove[2]), (Convert.ToInt16(sanMove[3]) - Convert.ToInt16('0')));
            bool isPromotion = (sanMove.Length == 5);

            PieceColor activePlayer = ExtractActivePlayer(fen);

            // token[2] is castling rights
            BoardSide whiteCastlingRights = BoardSide.None;
            BoardSide blackCastlingRights = BoardSide.None;

            FenParser.ParseCastlingRights(fenTokens[2], ref whiteCastlingRights, ref blackCastlingRights);

            // Get the current move counts
            int halfMoves = 0;
            int fullMoves = 0;

            ExtractMoveCounts(fen, ref halfMoves, ref fullMoves);

            // Needed for move count updates
            bool isCapture            = false;
            bool isPawnMove           = false;
            bool isNewEnPassantNeeded = false;

            // Expand the start and target ranks (one or both if different)
            fen = ExpandRank(fen, startSquare.Rank);
            if (startSquare.Rank != endSquare.Rank)
            {
                fen = ExpandRank(fen, endSquare.Rank);
            }

            // Get the piece moving
            char       fenPiece   = PieceAtBoardPosition(fen, startSquare.File.ToInt(), startSquare.Rank);
            PieceColor pieceColor = char.IsUpper(fenPiece) ? PieceColor.White : PieceColor.Black;

            if (isPromotion)
            {
                isPawnMove = true;
                fenPiece   = (activePlayer == PieceColor.White) ?  char.ToUpper(sanMove[4]) : sanMove[4];// Update the piece type
            }

            // target piece if any
            char fenTargetPiece = PieceAtBoardPosition(fen, endSquare.File.ToInt(), endSquare.Rank);

            // Common lambda for castling updates - used on rook moves and captures
            UpdateCastlingRightsOnEqualRank UpdateCastlingRightsIfNeeded = (rankA, rankB, targetSquare, color) =>
            {
                if (rankA == rankB)
                {
                    // A File?
                    if (targetSquare.File.ToInt() == 1)
                    {
                        if (color == PieceColor.White)
                        {
                            whiteCastlingRights &= ~BoardSide.Queen;
                        }
                        else
                        {
                            blackCastlingRights &= ~BoardSide.Queen;
                        }
                    }
                    // H File?
                    else if (targetSquare.File.ToInt() == 8)
                    {
                        if (color == PieceColor.White)
                        {
                            whiteCastlingRights &= ~BoardSide.King;
                        }
                        else
                        {
                            blackCastlingRights &= ~BoardSide.King;
                        }
                    }
                }
            };

            // generic capture
            // en-passant caught under pawn moves
            if (fenTargetPiece != '1')
            {
                isCapture = true;

                // If captured piece is enemy ROOK, that will potentially remove
                // rights on the opponent's side (if at home) - you can't capture
                // the king, so no need to check there
                PieceColor opponentColor = ChessBoard.OppositeColor(activePlayer);
                UpdateCastlingRightsIfNeeded(ChessBoard.RookHomeRank(opponentColor), endSquare.Rank, endSquare, opponentColor);
            }

            // Check the type of piece moving
            if (Char.ToUpper(fenPiece) == 'P') // PAWN
            {
                isPawnMove = true;             // resets halfmoves

                // en-passant only matters in pawn moves
                if (String.Compare(fenTokens[3], "-") != 0)
                {
                    // There is an en-passant square
                    BoardSquare enPassantSquare = new BoardSquare(
                        new PieceFile(fenTokens[3][0]), (Convert.ToInt16(fenTokens[3][1]) - Convert.ToInt16('0')));

                    // If the en-passant target is the move target, this is a capture
                    if (enPassantSquare == endSquare)
                    {
                        // en-passant capture - must also expand the rank 'behind' the target and
                        // remove that pawn - mark as capture
                        int  captureRank = (pieceColor == PieceColor.White) ? endSquare.Rank - 1 : endSquare.Rank + 1;
                        char captured;
                        fen = FenParser.RemovePiece(fen, endSquare.File.ToInt(), captureRank, out captured);
                        if (captured == '1')
                        {
                            throw new InvalidOperationException();
                        }
                        isCapture = true;
                    }
                }
                else if (Math.Abs(endSquare.Rank - startSquare.Rank) == 2)
                {
                    // If there is an enemy pawn on either side of the endSquare,
                    // then we need to create an enpassant target
                    // rank is already expanded
                    char neighbor = '1';

                    if (endSquare.File.ToInt() > 1)
                    {
                        neighbor = PieceAtBoardPosition(fen, endSquare.File.ToInt() - 1, endSquare.Rank);
                        if (char.ToUpper(neighbor) == 'P' && (ColorFromFen(neighbor) != activePlayer))
                        {
                            isNewEnPassantNeeded = true;
                        }
                    }

                    if (endSquare.File.ToInt() < 8)
                    {
                        neighbor = PieceAtBoardPosition(fen, endSquare.File.ToInt() + 1, endSquare.Rank);
                        if (char.ToUpper(neighbor) == 'P' && (ColorFromFen(neighbor) != activePlayer))
                        {
                            isNewEnPassantNeeded = true;
                        }
                    }
                }
            }

            if (Char.ToUpper(fenPiece) == 'K') // KING
            {
                // Check if this is a castling move - only time king moves 2 squares
                if (Math.Abs(endSquare.File.ToInt() - startSquare.File.ToInt()) == 2)
                {
                    // Move the associated rook... already expanded the rank
                    int rookFileStart = endSquare.File.ToInt() == 7 ? 8 : 1;
                    int rookFileEnd   = rookFileStart == 1 ? 4 : 6;

                    char rook;
                    fen = FenParser.RemovePiece(fen, rookFileStart, endSquare.Rank, out rook);
                    fen = FenParser.InsertPiece(fen, rook, rookFileEnd, endSquare.Rank);
                }

                // Moving the king removes all rights on all sides
                if (activePlayer == PieceColor.White)
                {
                    whiteCastlingRights = BoardSide.None;
                }
                else
                {
                    blackCastlingRights = BoardSide.None;
                }
            }
            if (Char.ToUpper(fenPiece) == 'R') // ROOK
            {
                // Check if at home position at start
                int homeRank = (pieceColor == PieceColor.White) ? 1 : 8;
                UpdateCastlingRightsIfNeeded(homeRank, startSquare.Rank, startSquare, activePlayer);
            }

            // Remove piece
            char fenChar;

            fen = RemovePiece(fen, startSquare.File.ToInt(), startSquare.Rank, out fenChar);
            if ((fenPiece != fenChar) && !isPromotion)
            {
                throw new InvalidOperationException();
            }

            // Place piece (it might not be the same type (promotions) - TODO
            fen = InsertPiece(fen, fenPiece, endSquare.File.ToInt(), endSquare.Rank);

            // Collapse the rows we touched
            fen = CollapseRank(fen, startSquare.Rank);
            if (startSquare.Rank != endSquare.Rank)
            {
                fen = CollapseRank(fen, endSquare.Rank);
            }

            // Re-tokenize
            fenTokens = TokenizeFEN(fen);

            // castling
            string wcr = TokenizeCastlingRights(whiteCastlingRights, PieceColor.White);
            string bcr = TokenizeCastlingRights(blackCastlingRights, PieceColor.Black);

            if ((whiteCastlingRights == BoardSide.None) && (blackCastlingRights == BoardSide.None))
            {
                fenTokens[2] = "-";
            }
            else
            {
                fenTokens[2] = String.Empty;
                if (whiteCastlingRights != BoardSide.None)
                {
                    fenTokens[2] = wcr;
                }

                if (blackCastlingRights != BoardSide.None)
                {
                    fenTokens[2] = String.Concat(fenTokens[2], bcr);
                }
            }

            // Update the other pieces of the FEN
            // active player
            fenTokens[1] = (activePlayer == PieceColor.White) ? "b" : "w";

            // Did we create a new en-passant target?
            if (isNewEnPassantNeeded)
            {
                // Target is behind pawn
                int         enpassantTargetRank = (activePlayer == PieceColor.White) ? endSquare.Rank - 1 : endSquare.Rank + 1;
                BoardSquare ept = new BoardSquare(endSquare.File, enpassantTargetRank);
                fenTokens[3] = ept.ToString();
            }
            else
            {
                fenTokens[3] = "-";
            }

            // half moves
            halfMoves++;
            if (isCapture || isPawnMove)
            {
                halfMoves = 0;
            }
            fenTokens[4] = halfMoves.ToString();

            // full moves
            if (activePlayer == PieceColor.Black)
            {
                fullMoves++;
            }
            fenTokens[5] = fullMoves.ToString();

            fen = string.Join(" ", fenTokens);
            return(fen);
        }
Example #7
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);
        }
Example #8
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 #9
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);
        }