예제 #1
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);
            }
        }
        /// <summary>
        /// Overriden virtual method which executes search.
        /// </summary>
        /// <param name="curPositionAndMoves"></param>
        /// <param name="searchLimit"></param>
        /// <param name="gameMoveHistory"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        protected override GameEngineSearchResult DoSearch(PositionWithHistory curPositionAndMoves,
                                                           SearchLimit searchLimit,
                                                           List <GameMoveStat> gameMoveHistory,
                                                           ProgressCallback callback,
                                                           bool verbose)
        {
            if (LastSearch != null && curPositionAndMoves.InitialPosMG != LastSearch.Manager.Context.StartPosAndPriorMoves.InitialPosMG)
            {
                throw new Exception("ResetGame must be called if not continuing same line");
            }

            MCTSearch searchResult;

            // Set up callback passthrough if provided
            MCTSManager.MCTSProgressCallback callbackMCTS = null;
            if (callback != null)
            {
                callbackMCTS = callbackContext => callback((MCTSManager)callbackContext);
            }

            // Possibly use the context of opponent to reuse position evaluations
            MCTSIterator shareContext = null;

            if (OpponentEngine is GameEngineCeresInProcess)
            {
                GameEngineCeresInProcess ceresOpponentEngine = OpponentEngine as GameEngineCeresInProcess;

                if (LastSearch is not null &&
                    LastSearch.Manager.Context.ParamsSearch.ReusePositionEvaluationsFromOtherTree &&
                    ceresOpponentEngine?.LastSearch.Manager != null &&
                    LeafEvaluatorReuseOtherTree.ContextsCompatibleForReuse(LastSearch.Manager.Context, ceresOpponentEngine.LastSearch.Manager.Context))
                {
                    shareContext = ceresOpponentEngine.LastSearch.Manager.Context;

                    // Clear any prior shared context from the shared context
                    // to prevent unlimited backward chaining (keeping unneeded prior contexts alive)
                    shareContext.ClearSharedContext();
                }
            }

            void InnerCallback(MCTSManager manager)
            {
                callbackMCTS?.Invoke(manager);
            }

            // Run the search
            searchResult = RunSearchPossiblyTreeReuse(shareContext, curPositionAndMoves, gameMoveHistory,
                                                      searchLimit, InnerCallback, verbose);

            int scoreCeresCP = (int)Math.Round(EncodedEvalLogistic.LogisticToCentipawn((float)searchResult.Manager.Root.Q), 0);

            MGMove bestMoveMG = searchResult.BestMove;

            int N = (int)searchResult.SearchRootNode.N;

            // Save (do not dispose) last search in case we can reuse it next time
            LastSearch = searchResult;

            isFirstMoveOfGame = false;

            // TODO is the RootNWhenSearchStarted correct because we may be following a continuation (BestMoveRoot)
            GameEngineSearchResultCeres result =
                new GameEngineSearchResultCeres(bestMoveMG.MoveStr(MGMoveNotationStyle.LC0Coordinate),
                                                (float)searchResult.SearchRootNode.Q, scoreCeresCP, searchResult.SearchRootNode.MAvg, searchResult.Manager.SearchLimit, default,