Exemple #1
0
        void OutputUCIInfo(MCTSManager manager, MCTSNode searchRootNode, bool isFinalInfo = false)
        {
            BestMoveInfo best = searchRootNode.BestMoveInfo(false);

            if (numPV == 1)
            {
                UCIWriteLine(UCIInfo.UCIInfoString(manager, searchRootNode, best?.BestMoveNode,
                                                   showWDL: showWDL, scoreAsQ: scoreAsQ));
            }
            else
            {
                // Send top move
                UCIWriteLine(UCIInfo.UCIInfoString(manager, searchRootNode, best.BestMoveNode, 1,
                                                   showWDL: showWDL, useParentN: !perPVCounters, scoreAsQ: scoreAsQ));

                // Send other moves visited
                MCTSNode[] sortedN      = searchRootNode.ChildrenSorted(s => - (float)s.N);
                int        multiPVIndex = 2;
                for (int i = 0; i < sortedN.Length && i < numPV; i++)
                {
                    if (!object.ReferenceEquals(sortedN[i], best.BestMoveNode))
                    {
                        UCIWriteLine(UCIInfo.UCIInfoString(manager, searchRootNode, sortedN[i], multiPVIndex,
                                                           showWDL: showWDL, useParentN: !perPVCounters, scoreAsQ: scoreAsQ));
                        multiPVIndex++;
                    }
                }

                // Finally show moves that had no visits
                float  elapsedTimeSeconds = (float)(DateTime.Now - manager.StartTimeThisSearch).TotalSeconds;
                string timeStr            = $"{ elapsedTimeSeconds * 1000.0f:F0}";
                for (int i = multiPVIndex - 1; i < searchRootNode.NumPolicyMoves; i++)
                {
                    (MCTSNode node, EncodedMove move, FP16 p)info = searchRootNode.ChildAtIndexInfo(i);
                    if (info.node == null)
                    {
                        bool        isWhite = searchRootNode.Annotation.Pos.MiscInfo.SideToMove == SideType.White;
                        EncodedMove moveCorrectPerspective = isWhite ? info.move : info.move.Flipped;
                        string      str = $"info depth 0 seldepth 0 time { timeStr } nodes 1 score cp 0 tbhits 0 "
                                          + $"multipv {multiPVIndex} pv {moveCorrectPerspective.AlgebraicStr} ";
                        UCIWriteLine(str);
                        multiPVIndex++;
                    }
                }
            }
            if (verboseMoveStats && (logLiveStats || isFinalInfo))
            {
                OutputVerboseMoveStats(CeresEngine.Search.SearchRootNode);
            }
        }
Exemple #2
0
        public static List <LC0VerboseMoveStat> BuildStats(MCTSNode searchRootNode)
        {
            List <LC0VerboseMoveStat> stats = new List <LC0VerboseMoveStat>();

            BestMoveInfo best = searchRootNode.BestMoveInfo(false);

            // First process policy moves not yet expanded
            // starting from last one (lowest probability).
            for (int i = searchRootNode.NumPolicyMoves - 1; i >= 0; i--)
            {
                (MCTSNode node, EncodedMove move, FP16 p)info = searchRootNode.ChildAtIndexInfo(i);
                if (info.node == null)
                {
                    LC0VerboseMoveStat stat = BuildStatNotExpanded(searchRootNode, i);
                    stats.Add(stat);
                }
            }

            // Now process moves expanded in order of visit count.
            MCTSNode[] sortedN = searchRootNode.ChildrenSorted(s => (float)s.N + 0.0001f * s.P);
            foreach (MCTSNode node in sortedN)
            {
                if (!object.ReferenceEquals(node, best.BestMoveNode))
                {
                    stats.Add(BuildStatExpanded(node, false));
                }
            }

            // Save the best move for last.
            stats.Add(BuildStatExpanded(best.BestMoveNode, false));

            // Finally, output the search root node.
            stats.Add(BuildStatExpanded(searchRootNode, true));

            return(stats);
        }
Exemple #3
0
        /// <summary>
        /// Writes extensive descriptive information to a specified TextWriter,
        /// including verbose move statistics, principal variation, and move timing information.
        /// </summary>
        /// <param name="searchRootNode"></param>
        /// <param name="writer"></param>
        /// <param name="description"></param>
        public void DumpFullInfo(MGMove bestMove, MCTSNode searchRootNode = null, TextWriter writer = null, string description = null)
        {
            searchRootNode = searchRootNode ?? Root;
            writer         = writer ?? Console.Out;

            int moveIndex = searchRootNode.Tree.Store.Nodes.PriorMoves.Moves.Count;

            writer.WriteLine();
            writer.WriteLine("=================================================================================");
            writer.Write(DateTime.Now + " SEARCH RESULT INFORMATION,  Move = " + ((1 + moveIndex / 2)));
            writer.WriteLine($" Thread = {Thread.CurrentThread.ManagedThreadId}");
            if (description != null)
            {
                writer.WriteLine(description);
            }
            writer.WriteLine();

            writer.WriteLine("Tree root           : " + Context.Root);
            if (searchRootNode != Root)
            {
                writer.WriteLine("Search root         : " + searchRootNode);
            }
            writer.WriteLine();

            MCTSNode[] nodesSortedN = null;
            MCTSNode[] nodesSortedQ = null;

            string bestMoveInfo = "";

            if (searchRootNode.NumChildrenExpanded > 0 &&
                StopStatus != SearchStopStatus.TablebaseImmediateMove &&
                StopStatus != SearchStopStatus.OnlyOneLegalMove)
            {
                MCTSNode[] childrenSortedN = searchRootNode.ChildrenSorted(node => - node.N);
                MCTSNode[] childrenSortedQ = searchRootNode.ChildrenSorted(node => (float)node.Q);
                bool       isTopN          = childrenSortedN[0].Annotation.PriorMoveMG == bestMove;
                bool       isTopQ          = childrenSortedQ[0].Annotation.PriorMoveMG == bestMove;
                if (isTopN && isTopQ)
                {
                    bestMoveInfo = "(TopN and TopQ)";
                }
                else if (isTopN)
                {
                    bestMoveInfo = "(TopN)";
                }
                else if (isTopQ)
                {
                    bestMoveInfo = "(TopQ)";
                }
            }

            // Output position (with history) information.
            writer.WriteLine("Position            : " + searchRootNode.Annotation.Pos.FEN);
            writer.WriteLine("Tree root position  : " + Context.Tree.Store.Nodes.PriorMoves);
            writer.WriteLine("Search stop status  : " + StopStatus);
            writer.WriteLine("Best move selected  : " + bestMove.MoveStr(MGMoveNotationStyle.LC0Coordinate) + " " + bestMoveInfo);
            writer.WriteLine();

            using (new SearchContextExecutionBlock(Context))
            {
                string infoUpdate = UCIInfo.UCIInfoString(this, searchRootNode);
                writer.WriteLine(infoUpdate);

                writer.WriteLine();
                DumpTimeInfo(writer);

                writer.WriteLine();
                searchRootNode.Dump(1, 1, writer: writer);

                writer.WriteLine();
                MCTSPosTreeNodeDumper.DumpPV(searchRootNode, true, writer);
            }
        }
Exemple #4
0
        static void DumpNodeStr(PositionWithHistory priorMoves, MCTSNode node, int depth, int countTimesSeen, bool fullDetail)
        {
            node.Context.Tree.Annotate(node);

            char extraChar = ' ';

            if (node.Terminal == GameResult.Checkmate)
            {
                extraChar = 'C';
            }
            else if (node.Terminal == GameResult.Draw)
            {
                extraChar = 'D';
            }
            else if (countTimesSeen > 1)
            {
                extraChar = countTimesSeen > 9 ? '9' : countTimesSeen.ToString()[0];
            }

            float multiplier = depth % 2 == 0 ? 1.0f : -1.0f;

            float pctOfVisits = node.IsRoot ? 100.0f : (100.0f * node.N / node.Parent.N);

            MCTSNode bestMove = null;

// TODO: someday show this too      MCTSNode nextBestMove = null;
            if (!node.IsRoot)
            {
                MCTSNode[] parentsChildrenSortedQ = node.ChildrenSorted(innerNode => - multiplier * (float)innerNode.Q);
                if (parentsChildrenSortedQ.Length > 0)
                {
                    bestMove = parentsChildrenSortedQ[0];
                }
//        if (parentsChildrenSortedQ.Length > 1) nextBestMove = parentsChildrenSortedQ[1];
            }

            // Depth, move
            Console.Write($"{depth,3}. ");
            Console.Write(extraChar);
            Console.Write($" {node.NumPolicyMoves,3} ");

            Console.Write($"{node.Index,13:N0}");

            string san = node.IsRoot ? "" : MGMoveConverter.ToMove(node.Annotation.PriorMoveMG).ToSAN(in node.Parent.Annotation.Pos);

//      string sanNextBest = node.IsRoot ? "" : MGMoveConverter.ToMove(nextBestMove.Annotation.PriorMoveMG).ToSAN(in node.Parent.Annotation.Pos);
            if (node.Annotation.Pos.MiscInfo.SideToMove == SideType.White)
            {
                Console.Write($"      ");
                Console.Write($"{san,6}");
            }
            else
            {
                Console.Write($"{san,6}");
                Console.Write($"      ");
            }

//      float diffBestNextBestQ = 0;
//      if (nextBestMove != null) diffBestNextBestQ = (float)(bestMove.Q - nextBestMove.Q);
//      Console.Write($"{  (nextBestMove?.Annotation == null ? "" : nextBestMove.Annotation.PriorMoveMG.ToString()),8}");
//      Console.Write($"{diffBestNextBestQ,8:F2}");


            Console.Write($"{node.N,13:N0} ");
            Console.Write($" {pctOfVisits,5:F0}%");
            Console.Write($"   {100.0 * node.P,6:F2}%  ");
            DumpWithColor(multiplier * node.V, $" {multiplier * node.V,6:F3}  ", -0.2f, 0.2f);
//      DumpWithColor(multiplier * node.VSecondary, $" {multiplier * node.VSecondary,6:F3} ", -0.2f, 0.2f);
            double q = multiplier * node.Q;

            DumpWithColor((float)q, $" {q,6:F3} ", -0.2f, 0.2f);

            //      float qStdDev = MathF.Sqrt(node.Ref.VVariance);
            //      if (float.IsNaN(qStdDev))
            //        Console.WriteLine("found negative var");
            //      Console.Write($" +/-{qStdDev,5:F2}  ");

            Console.Write($" {node.WinP,5:F2}/{node.DrawP,5:F2}/{node.LossP,5:F2}  ");

            Console.Write($" {node.WAvg,5:F2}/{node.DAvg,5:F2}/{node.LAvg,5:F2}  ");

            //      Console.Write($"   {node.Ref.QUpdatesWtdAvg,5:F2}  ");
            //      Console.Write($" +/-:{MathF.Sqrt(node.Ref.QUpdatesWtdVariance),5:F2}  ");
            //      Console.Write($" {node.Ref.TrendBonusToP,5:F2}  ");

            Console.Write($" {node.MPosition,3:F0} ");
            Console.Write($" {node.MAvg,3:F0}  ");

            if (fullDetail)
            {
                int numPieces = node.Annotation.Pos.PieceCount;

//        Console.Write($" {PosStr(node.Annotation.Pos)} ");
                Console.Write($" {node.Annotation.Pos.FEN}");
            }


            Console.WriteLine();
        }