From c318cb4ca00ed37c2c3c22b805c6e82602e66df4 Mon Sep 17 00:00:00 2001 From: Sebastian Lague Date: Tue, 25 Jul 2023 15:47:59 +0200 Subject: [PATCH] Fix wrong piecetype in GameMoveHistory --- Chess-Challenge/src/API/Board.cs | 22 ++- .../Application/Core/ChallengeController.cs | 4 +- .../Framework/Application/Helpers/Tester.cs | 17 +- .../src/Framework/Chess/Board/Board.cs | 24 +-- .../src/Framework/Chess/Helpers/FenUtility.cs | 171 ++++++++++-------- 5 files changed, 138 insertions(+), 100 deletions(-) diff --git a/Chess-Challenge/src/API/Board.cs b/Chess-Challenge/src/API/Board.cs index 297cb8f..9a8f585 100644 --- a/Chess-Challenge/src/API/Board.cs +++ b/Chess-Challenge/src/API/Board.cs @@ -25,9 +25,23 @@ namespace ChessChallenge.API /// Create a new board. Note: this should not be used in the challenge, /// use the board provided in the Think method instead. /// - public Board(Chess.Board board) + public Board(Chess.Board boardSource) { - this.board = board; + // Clone board and create game move history + board = new Chess.Board(); + board.LoadPosition(boardSource.StartPositionInfo); + GameMoveHistory = new Move[boardSource.AllGameMoves.Count]; + + for (int i = 0; i < boardSource.AllGameMoves.Count; i ++) + { + Chess.Move move = boardSource.AllGameMoves[i]; + int movePieceType = PieceHelper.PieceType(board.Square[move.StartSquareIndex]); + int capturePieceType = PieceHelper.PieceType(board.Square[move.TargetSquareIndex]); + GameMoveHistory[i] = new Move(move, movePieceType, capturePieceType); + board.MakeMove(move, false); + } + + // Init move gen moveGen = new APIMoveGen(); cachedLegalMoves = Array.Empty(); cachedLegalCaptureMoves = Array.Empty(); @@ -49,11 +63,7 @@ namespace ChessChallenge.API // Init rep history repetitionHistory = new HashSet(board.RepetitionPositionHistory); GameRepetitionHistory = repetitionHistory.ToArray(); - GameRepetitionHistory.Reverse(); repetitionHistory.Remove(board.ZobristKey); - - // Init game moves history - GameMoveHistory = board.AllGameMoves.Select(m => new Move(MoveUtility.GetMoveNameUCI(m), this)).ToArray(); } /// diff --git a/Chess-Challenge/src/Framework/Application/Core/ChallengeController.cs b/Chess-Challenge/src/Framework/Application/Core/ChallengeController.cs index 6ef9b60..e7e089a 100644 --- a/Chess-Challenge/src/Framework/Application/Core/ChallengeController.cs +++ b/Chess-Challenge/src/Framework/Application/Core/ChallengeController.cs @@ -142,9 +142,7 @@ namespace ChessChallenge.Application Move GetBotMove() { - // Board b = new Board(); - // b.LoadPosition(FenUtility.CurrentFen(board)); - API.Board botBoard = new(new(board)); + API.Board botBoard = new(board); try { API.Timer timer = new(PlayerToMove.TimeRemainingMs); diff --git a/Chess-Challenge/src/Framework/Application/Helpers/Tester.cs b/Chess-Challenge/src/Framework/Application/Helpers/Tester.cs index 8217956..3a8789d 100644 --- a/Chess-Challenge/src/Framework/Application/Helpers/Tester.cs +++ b/Chess-Challenge/src/Framework/Application/Helpers/Tester.cs @@ -268,12 +268,13 @@ namespace ChessChallenge.Application static void MiscTest() { Console.WriteLine("Running Misc Tests"); + + // Captures var board = new Chess.Board(); board.LoadPosition("1q3rk1/P5p1/4p2p/2ppP1N1/5Qb1/1PP5/7P/2R2RK1 w - - 0 28"); boardAPI = new(board); Assert(boardAPI.IsWhiteToMove, "Colour to move wrong"); - //var moves = boardAPI.GetLegalMoves(); var captures = boardAPI.GetLegalMoves(true); Assert(captures.Length == 4, "Captures wrong"); int numTested = 0; @@ -308,6 +309,20 @@ namespace ChessChallenge.Application } } Assert(numTested == 4, "Target square wrong"); + + // Game moves + string startPos = "r1bqkbnr/pppppppp/2n5/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 1 2"; + board.LoadPosition(startPos); + board.MakeMove(MoveUtility.GetMoveFromUCIName("e4e5", board), false); + board.MakeMove(MoveUtility.GetMoveFromUCIName("c6e5", board), false); + var b = new Chess.Board(board); + boardAPI = new(b); + Assert(boardAPI.GameMoveHistory[0].MovePieceType is PieceType.Pawn, "Wrong game move history"); + Assert(boardAPI.GameMoveHistory[0].CapturePieceType is PieceType.None, "Wrong game move history"); + Assert(boardAPI.GameMoveHistory[1].MovePieceType is PieceType.Knight, "Wrong game move history"); + Assert(boardAPI.GameMoveHistory[1].CapturePieceType is PieceType.Pawn, "Wrong game move history"); + Assert(boardAPI.GameStartFenString == startPos, "Wrong game start fen"); + Assert(boardAPI.GetFenString() == "r1bqkbnr/pppppppp/8/4n3/8/8/PPPP1PPP/RNBQKBNR w KQkq - 0 3", "Wrong game fen"); } static void MoveGenTest() diff --git a/Chess-Challenge/src/Framework/Chess/Board/Board.cs b/Chess-Challenge/src/Framework/Chess/Board/Board.cs index 3414b68..528d604 100644 --- a/Chess-Challenge/src/Framework/Chess/Board/Board.cs +++ b/Chess-Challenge/src/Framework/Chess/Board/Board.cs @@ -59,7 +59,8 @@ namespace ChessChallenge.Chess public GameState currentGameState; public List AllGameMoves; - public string GameStartFen { get; private set; } + public string GameStartFen => StartPositionInfo.fen; + public FenUtility.PositionInfo StartPositionInfo; // piece count excluding pawns and kings public int totalPieceCountWithoutPawnsAndKings; @@ -72,16 +73,13 @@ namespace ChessChallenge.Chess { if (source != null) { - string fen = FenUtility.CurrentFen(source); - LoadPosition(fen); + LoadPosition(source.StartPositionInfo); - RepetitionPositionHistory = new(source.RepetitionPositionHistory); - AllGameMoves = new(source.AllGameMoves); - - currentGameState = source.currentGameState; - + for (int i = 0; i < source.AllGameMoves.Count; i++) + { + MakeMove(source.AllGameMoves[i], false); + } } - } @@ -470,9 +468,13 @@ namespace ChessChallenge.Chess // Load custom position from fen string public void LoadPosition(string fen) { + LoadPosition(FenUtility.PositionFromFen(fen)); + } + + public void LoadPosition(FenUtility.PositionInfo posInfo) + { + StartPositionInfo = posInfo; Initialize(); - GameStartFen = fen; - FenUtility.PositionInfo posInfo = FenUtility.PositionFromFen(fen); // Load pieces into board array and piece lists for (int squareIndex = 0; squareIndex < 64; squareIndex++) diff --git a/Chess-Challenge/src/Framework/Chess/Helpers/FenUtility.cs b/Chess-Challenge/src/Framework/Chess/Helpers/FenUtility.cs index de2f20a..ce07710 100644 --- a/Chess-Challenge/src/Framework/Chess/Helpers/FenUtility.cs +++ b/Chess-Challenge/src/Framework/Chess/Helpers/FenUtility.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; namespace ChessChallenge.Chess { @@ -11,72 +13,7 @@ namespace ChessChallenge.Chess public static PositionInfo PositionFromFen(string fen) { - PositionInfo loadedPositionInfo = new PositionInfo(); - string[] sections = fen.Split(' '); - - int file = 0; - int rank = 7; - - foreach (char symbol in sections[0]) - { - if (symbol == '/') - { - file = 0; - rank--; - } - else - { - if (char.IsDigit(symbol)) - { - file += (int)char.GetNumericValue(symbol); - } - else - { - int pieceColour = (char.IsUpper(symbol)) ? PieceHelper.White : PieceHelper.Black; - int pieceType = char.ToLower(symbol) switch - { - 'k' => PieceHelper.King, - 'p' => PieceHelper.Pawn, - 'n' => PieceHelper.Knight, - 'b' => PieceHelper.Bishop, - 'r' => PieceHelper.Rook, - 'q' => PieceHelper.Queen, - _ => PieceHelper.None - }; - - loadedPositionInfo.squares[rank * 8 + file] = pieceType | pieceColour; - file++; - } - } - } - - loadedPositionInfo.whiteToMove = (sections[1] == "w"); - - string castlingRights = sections[2]; - loadedPositionInfo.whiteCastleKingside = castlingRights.Contains("K"); - loadedPositionInfo.whiteCastleQueenside = castlingRights.Contains("Q"); - loadedPositionInfo.blackCastleKingside = castlingRights.Contains("k"); - loadedPositionInfo.blackCastleQueenside = castlingRights.Contains("q"); - - if (sections.Length > 3) - { - string enPassantFileName = sections[3][0].ToString(); - if (BoardHelper.fileNames.Contains(enPassantFileName)) - { - loadedPositionInfo.epFile = BoardHelper.fileNames.IndexOf(enPassantFileName) + 1; - } - } - - // Half-move clock - if (sections.Length > 4) - { - int.TryParse(sections[4], out loadedPositionInfo.fiftyMovePlyCount); - } - // Full move number - if (sections.Length > 5) - { - int.TryParse(sections[5], out loadedPositionInfo.moveCount); - } + PositionInfo loadedPositionInfo = new(fen); return loadedPositionInfo; } @@ -273,27 +210,103 @@ namespace ChessChallenge.Chess } } - public class PositionInfo + public readonly struct PositionInfo { - public int[] squares; + public readonly string fen; + public readonly ReadOnlyCollection squares; + // Castling rights - public bool whiteCastleKingside; - public bool whiteCastleQueenside; - public bool blackCastleKingside; - public bool blackCastleQueenside; + public readonly bool whiteCastleKingside; + public readonly bool whiteCastleQueenside; + public readonly bool blackCastleKingside; + public readonly bool blackCastleQueenside; // En passant file (1 is a-file, 8 is h-file, 0 means none) - public int epFile; - public bool whiteToMove; + public readonly int epFile; + public readonly bool whiteToMove; // Number of half-moves since last capture or pawn advance // (starts at 0 and increments after each player's move) - public int fiftyMovePlyCount; + public readonly int fiftyMovePlyCount; // Total number of moves played in the game // (starts at 1 and increments after black's move) - public int moveCount; + public readonly int moveCount; - public PositionInfo() + public PositionInfo(string fen) { - squares = new int[64]; + this.fen = fen; + int[] squarePieces = new int[64]; + + string[] sections = fen.Split(' '); + + int file = 0; + int rank = 7; + + foreach (char symbol in sections[0]) + { + if (symbol == '/') + { + file = 0; + rank--; + } + else + { + if (char.IsDigit(symbol)) + { + file += (int)char.GetNumericValue(symbol); + } + else + { + int pieceColour = (char.IsUpper(symbol)) ? PieceHelper.White : PieceHelper.Black; + int pieceType = char.ToLower(symbol) switch + { + 'k' => PieceHelper.King, + 'p' => PieceHelper.Pawn, + 'n' => PieceHelper.Knight, + 'b' => PieceHelper.Bishop, + 'r' => PieceHelper.Rook, + 'q' => PieceHelper.Queen, + _ => PieceHelper.None + }; + + squarePieces[rank * 8 + file] = pieceType | pieceColour; + file++; + } + } + } + + squares = new(squarePieces); + + whiteToMove = (sections[1] == "w"); + + string castlingRights = sections[2]; + whiteCastleKingside = castlingRights.Contains('K'); + whiteCastleQueenside = castlingRights.Contains('Q'); + blackCastleKingside = castlingRights.Contains('k'); + blackCastleQueenside = castlingRights.Contains('q'); + + // Default values + epFile = 0; + fiftyMovePlyCount = 0; + moveCount = 0; + + if (sections.Length > 3) + { + string enPassantFileName = sections[3][0].ToString(); + if (BoardHelper.fileNames.Contains(enPassantFileName)) + { + epFile = BoardHelper.fileNames.IndexOf(enPassantFileName) + 1; + } + } + + // Half-move clock + if (sections.Length > 4) + { + int.TryParse(sections[4], out fiftyMovePlyCount); + } + // Full move number + if (sections.Length > 5) + { + int.TryParse(sections[5], out moveCount); + } } } }