private void PrintMoveStatistics(double runtime, Grid grid, SearchResult result, int[] scores, int[] scoreTypes) { Debug.Assert(scores.Length == scoreTypes.Length); log.WriteLine("Done"); // Print the grid before the AI's move to the log file. log.WriteLineToLog(); log.WriteLineToLog("Grid before AI move:"); log.WriteLineToLog(grid.ToString()); log.WriteLineToLog(); // Print node statistics. double cutoffsOnFirstChild = betaCutoffsOnFirstChild * 100D / betaCutoffs; double cutoffsOnOrderedChildren = betaCutoffsOnOrderedChildren * 100D / betaCutoffs; log.Write("Analysed "); WriteWithColor("{0:N0}", ConsoleColor.White, totalNodesSearched); log.WriteLine(" states, including {0:N0} end states.", endNodesSearched); log.WriteLine(" Searched {0:N0} pv-nodes, {1:N0} cut-nodes and {2:N0} all-nodes.", pvNodes, cutNodes, allNodes); log.WriteLine(" {0:N0} cutoffs from lookups.", alphaBetaCutoffs); log.WriteLine(" {0:N0} beta cutoffs ({1:N2}% on first child, {2:N2}% on " + "ordered children).", betaCutoffs, cutoffsOnFirstChild, cutoffsOnOrderedChildren); // Print runtime statistics. double nodesPerMillisecond = Math.Round(totalNodesSearched / runtime, 4); string minutes = (runtime > 60000) ? String.Format(" ({0:N2} minutes)", runtime / 60000.0) : ""; log.Write("Runtime {0:N} ms{1} (", runtime, minutes); WriteWithColor("{0:N}", ConsoleColor.White, nodesPerMillisecond); log.WriteLine(" states / ms)."); log.WriteLine(" Total runtime {0:N} ms.", totalRuntime); // Print the scores of each valid move. log.Write("The move scores are"); for (int i = 0; i < scores.Length; i++) { if (grid.IsValidMove(i)) { log.Write(" {0}:", i); if (scoreTypes[i] == -1) { log.Write("-"); } else { if (scores[i] == infinity) { log.WriteToLog("+\u221E"); Console.ForegroundColor = ConsoleColor.Green; Console.Write("+Inf"); Console.ResetColor(); } else if (scores[i] == -infinity) { log.WriteToLog("-\u221E"); Console.ForegroundColor = ConsoleColor.Red; Console.Write("-Inf"); Console.ResetColor(); } else { log.Write("{0}", scores[i]); } switch (scoreTypes[i]) { case NodeTypeExact: log.Write("E"); break; case NodeTypeLower: log.Write("L"); break; case NodeTypeUpper: log.Write("U"); break; } } } } log.WriteLine(); // Print the score of the chosen move. if (result.score > 0) { WriteLineWithColor(" AI will win latest on move {0}.", ConsoleColor.Green, result.GetDepth()); } else if (result.score < 0) { WriteLineWithColor(" AI will lose on move {0} (assuming perfect play).", ConsoleColor.Red, result.GetDepth()); } else { log.WriteLine(" Move score is {0}.", result.score); } log.WriteLine(" Move is {0:N0}.", finalMove); log.WriteLine("Correct/Incorrect guesses: {0:N0}/{1:N0}", correctGuesses, incorrectGuesses); // Print transposition table statistics. log.WriteLine(); log.WriteLine("Transposition table:"); log.WriteLine(" Size: {0:N0}", TranspositionTable.TableSize); log.WriteLine(" Memory Space: {0:N0} MB", TranspositionTable.MemorySpaceBytes / 1024 / 1024); log.WriteLine(" Shallow Lookups: {0:N0}", shallowTableLookups); log.WriteLine(" Lookups: {0:N0}", tableLookups); log.WriteLine(" Requests: {0:N0}", transpositionTable.Requests); log.WriteLine(" Insertions: {0:N0}", transpositionTable.Insertions); log.WriteLine(" Collisions: {0:N0}", transpositionTable.Collisions); log.WriteLine(" Items: {0:N0} ({1:N4}% full)", transpositionTable.Size, 100.0 * transpositionTable.Size / TranspositionTable.TableSize); transpositionTable.ResetStatistics(); log.WriteLine(); log.WriteLine(); }
private void PerftHelper(Grid grid, int depth, int maxDepth, int[] results, HashSet<ulong> visitedStates) { int player = depth & 1; if (depth >= maxDepth || grid.IsGameOver(player) || visitedStates.Contains(grid.Hash)) { return; } results[depth]++; visitedStates.Add(grid.Hash); for (int i = 0; i < grid.Width; i++) { if (grid.IsValidMove(i)) { grid.Move(i, player); PerftHelper(grid, depth + 1, maxDepth, results, visitedStates); grid.UndoMove(i, player); } } }