/// <summary> /// Given a FEN, return a list of ChessPiece objects descibed by the FEN /// </summary> /// <param name="fen">FEN input</param> /// <returns>ChessPieces contained in the FEN description</returns> public static List <ChessPiece> ExtractPieces(string fen) { string[] fenTokens = TokenizeFEN(fen); // The first part describes the pieces... string fenString = fenTokens[0]; // Create a new list for the pieces we'll find List <ChessPiece> pieces = new List <ChessPiece>(); int currentRank = 8; // 8 - back rank for Black int currentFile = 1; // A int index = 0; // Token can be 1-8 characters long per rank plus separators, so not long while (index < fenString.Length) { char fenChar = fenString[index++]; // Save the current char if (Char.IsLetter(fenChar)) { // Uppercase is white, lower black PieceColor color = Char.IsUpper(fenChar) ? PieceColor.White : PieceColor.Black; // Create the new ChessPiece object at the given location ChessPiece newPiece = new ChessPiece(color, ChessBoard.PieceClassFromFen(fenChar), new PieceFile(currentFile), currentRank); // Need to update deployed for pawns not on their home ranks if (newPiece.Job == PieceClass.Pawn) { int homeRank = (newPiece.Color == PieceColor.White) ? 2 : 7; if (newPiece.Rank != homeRank) { newPiece.Deployed = true; } } pieces.Add(newPiece); // Add it to our total list of pieces currentFile++; } else if (Char.IsDigit(fenChar)) { // Digits represent empty squares and are RLE (run length encoded) like a bitmap // advance File the amount of the spaces specified currentFile += (Convert.ToUInt16(fenChar) - Convert.ToUInt16('0')); } else if (fenChar == '/') { // The '/' denotes the end of a rank on the chess board // decrement Rank and reset the File (like a newline) currentRank--; currentFile = 1; } } // Return all of the pieces found return(pieces); }
/// <summary> /// Invoked when the chess engine has responded with a move to apply to the /// local board /// </summary> private void OnEngineBestMoveResponse() { thinkingIndex = 0; // reset index counter for simple progress text // Get the best move from the engine string bestMove = engine.BestMove; if ((String.Compare(bestMove, "(none)") == 0) || // Stockfish (and converted ones) (String.Compare(bestMove, "a1a1") == 0) || // Rybka (board.HalfMoveCount >= HalfMovesUntilDraw)) // Propably spinning on self play or just a draw { if (board.HalfMoveCount >= HalfMovesUntilDraw) { Debug.WriteLine("Draw by 50 moves rule..."); } GameOverHandler(); } else if (GetInputState() == InputState.WaitingOnOpponentMove) { // Extract the board location from the move string PieceFile startFile = new PieceFile(bestMove[0]); int startRank = Convert.ToInt16(bestMove[1]) - Convert.ToInt16('0'); PieceFile destFile = new PieceFile(bestMove[2]); int destRank = Convert.ToInt16(bestMove[3]) - Convert.ToInt16('0'); ChessPiece foundPiece = board.FindPieceAt(startFile, startRank); MoveInformation moveInfo = new MoveInformation( new BoardSquare(startFile, startRank), new BoardSquare(destFile, destRank), foundPiece.Deployed, board.CurrentFEN); moveInfo.Color = foundPiece.Color; moveInfo.CastlingRights = board.ActivePlayerCastlingRights; // When coming from the engine, we get the promotion detection for free if (bestMove.Length == 5) { // Applied on the next move PieceClass promotionJob = ChessBoard.PieceClassFromFen(bestMove[4]); board.PromotePiece(startFile, startRank, destFile, destRank, promotionJob, ref moveInfo); } // Move the piece on the board, and add it to the official moves list board.MovePiece(ref moveInfo); // trigger a redraw view.Invalidate(); // Apply the move the engine just gave us with the engine (update it's own move) UpdateEnginePosition(); } }