/// <summary> /// Sets up the board using the given FEN string. It's possible not all /// pieces are present. /// </summary> /// <param name="fen">FEN string that describes the position /// (https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation)</param> private void CreateAndPlacePieces(string fen) { Reset(); // String starts on the back rank on the A file then moves Left->Right // Top->Bottom string fenString = fen.Trim(); activePlayer = FenParser.ExtractActivePlayer(fenString); FenParser.ExtractCastlingRights(fenString, ref whiteCastlingRights, ref blackCastlingRights); enPassantValid = FenParser.ExtractEnPassantTarget(fenString, out enPassantTarget); FenParser.ExtractMoveCounts(fenString, ref halfMoveCount, ref fullMoveCount); List <ChessPiece> pieces = FenParser.ExtractPieces(fenString); foreach (ChessPiece fenPiece in pieces) { // For pawns and rooks and kings, deployment matters, check if they're on their home // rank/square and if not, set it to true if (fenPiece.Job == PieceClass.Pawn) { if (PawnHomeRank(fenPiece.Color) != fenPiece.Rank) { fenPiece.Deployed = true; } } else if (fenPiece.Job == PieceClass.Rook) { if (((fenPiece.File.ToInt() != 1) && (fenPiece.File.ToInt() != 8)) || (fenPiece.Rank != RookHomeRank(fenPiece.Color))) { fenPiece.Deployed = true; } } else if (fenPiece.Job == PieceClass.King) { // Both colors should be on the E file and their home rank int homeRank = (fenPiece.Color == PieceColor.White) ? 1 : 8; if ((fenPiece.Rank != homeRank) || (fenPiece.File != new PieceFile('e'))) { fenPiece.Deployed = true; } } if (fenPiece.Color == PieceColor.White) { whitePieces.Add(fenPiece); } else { blackPieces.Add(fenPiece); } } }
/// <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); }