public void RunPerft(string fen, int depth, long moveCount) { var gameState = new GameState(fen, _zobristHash); var perftData = new PerftData(); var count = gameState.RunPerftRecursively(_moveData, _zobristHash, perftData, 1, depth); Assert.That(count, Is.EqualTo(moveCount)); }
internal void Perft(int depth) { var perftData = new PerftData(); var stopwatch = new Stopwatch(); stopwatch.Start(); var count = _gameState.RunPerftRecursively(_moveData, _zobristHash, perftData, 1, depth); stopwatch.Stop(); _logger.InfoFormat("Total Nodes: {0} {1} mS Elapsed: {2}", count, Environment.NewLine, stopwatch.ElapsedMilliseconds); _logger.DebugFormat("Total Nodes: {0} Total Captures {1} Total Checks {2} Total EnPassants {3} Total OO Castles {4} Total OOO Castles {5} Total Promotions {6}", count, perftData.TotalCaptures, perftData.TotalChecks, perftData.TotalEnpassants, perftData.TotalOOCastles, perftData.TotalOOOCastles, perftData.TotalPromotions); }
/// <summary> /// Runs similar to perft against a gamestate. Returns the total number of nodes that each move from the starting position makes. This is used when troublshooting move generation /// </summary> /// <param name="gameState">The gamestate being tested</param> /// <param name="moveData">The moveData to use</param> /// <param name="zobristHash">The zobrist hash data to use</param> /// <param name="perftData">The perft data. This is used if collection additional information about perft</param> /// <param name="ply">The ply to start the search on</param> /// <param name="depth">The depth of perft to run</param> internal static void CalculateDivide(this GameState gameState, MoveData moveData, ZobristHash zobristHash, PerftData perftData, int ply, int depth) { var sb = new StringBuilder(_divideOutput); ulong count = 0; gameState.GenerateMoves(MoveGenerationMode.All, ply, moveData); foreach (var move in gameState.Moves[ply]) { gameState.MakeMove(move, zobristHash); if (!gameState.IsOppositeSideKingAttacked(moveData)) { ulong moveCount = RunPerftRecursively(gameState, moveData, zobristHash, perftData, ply + 1, depth - 1); sb.AppendFormat("{0}{1} {2}{3}", MoveUtility.RankAndFile[move.GetFromMove()], MoveUtility.RankAndFile[move.GetToMove()], moveCount, Environment.NewLine); count += moveCount; } gameState.UnMakeMove(move); } sb.AppendFormat("Total Nodes: {0}", count); _logger.InfoFormat(sb.ToString()); }
/// <summary> /// Runs perft (Performance Test) against a gamestate. This is used to verify that all moves are being generated correctly. /// </summary> /// <param name="gameState">The gamestate being tested</param> /// <param name="moveData">The moveData to use</param> /// <param name="zobristHash">The zobristHash data to use</param> /// <param name="perftData">The perft data. This is used if collection additional information about perft</param> /// <param name="ply">The ply to start the search on</param> /// <param name="depth">The depth of perft to run</param> /// <returns>A count of the total number of nodes at depth 0</returns> internal static ulong RunPerftRecursively(this GameState gameState, MoveData moveData, ZobristHash zobristHash, PerftData perftData, int ply, int depth) { if (depth == 0) { return(1); } ulong count = 0; gameState.GenerateMoves(MoveGenerationMode.All, ply, moveData); foreach (var move in gameState.Moves[ply]) { #if DEBUG var boardArray = new uint[64]; var previousHash = gameState.HashKey; for (var i = 0; i < boardArray.Length - 1; i++) { boardArray[i] = gameState.BoardArray[i]; } if (_logger.IsTraceEnabled) { _logger.TraceFormat( "MoveHash {0} GameState Move From {1} To {2} MovingPiece {3} CapturedPiece {4} PromotedPeice {5}", move.GetHashCode(), move.GetFromMove().ToRankAndFile(), move.GetToMove().ToRankAndFile(), move.GetMovingPiece(), move.GetCapturedPiece(), move.GetPromotedPiece()); _logger.TraceFormat("GameState All Bitboards Before Move {0} {1}", Environment.NewLine, gameState.ConvertBitBoardsToConsoleOutput()); _logger.TraceFormat("GameState BoardArray Before Move {0} {1}", Environment.NewLine, gameState.ConvertBoardArrayToConsoleOutput()); } #endif gameState.MakeMove(move, zobristHash); #if DEBUG if (_logger.IsTraceEnabled) { _logger.TraceFormat("MoveHash {0} GameState All Bitboards After Move {1} {2}", move.GetHashCode(), Environment.NewLine, gameState.ConvertBitBoardsToConsoleOutput()); _logger.TraceFormat("MoveHash {0} GameState BoardArray After Move {1} {2}", move.GetHashCode(), Environment.NewLine, gameState.ConvertBoardArrayToConsoleOutput()); } #endif if (!gameState.IsOppositeSideKingAttacked(moveData)) { count += RunPerftRecursively(gameState, moveData, zobristHash, perftData, ply + 1, depth - 1); #if DEBUG if (depth == 1) { if (move.IsPieceCaptured()) { perftData.TotalCaptures++; } if (move.IsEnPassant()) { perftData.TotalEnpassants++; } if (move.IsPromotion()) { perftData.TotalPromotions++; } if (move.IsCastleOO()) { perftData.TotalOOCastles++; } if (move.IsCastleOOO()) { perftData.TotalOOOCastles++; } if (gameState.IsCurrentSideKingAttacked(moveData)) { perftData.TotalChecks++; } } #endif } gameState.UnMakeMove(move); #if DEBUG if (_logger.IsTraceEnabled) { _logger.TraceFormat("MoveHash {0} GameState All Bitboards After UnMakeMove {1} {2}", move.GetHashCode(), Environment.NewLine, gameState.ConvertBitBoardsToConsoleOutput()); _logger.TraceFormat("MoveHash {0} GameState BoardArray After UnMakeMove {1} {2}", move.GetHashCode(), Environment.NewLine, gameState.ConvertBoardArrayToConsoleOutput()); } for (var i = 0; i < boardArray.Length - 1; i++) { Debug.Assert(boardArray[i] == gameState.BoardArray[i]); } Debug.Assert(previousHash == gameState.HashKey); #endif } return(count); }