Ejemplo n.º 1
0
        public static string UCIInfoString(MCTSManager manager)
        {
            float elapsedTimeSeconds = (float)(DateTime.Now - manager.StartTimeThisSearch).TotalSeconds;

            float scoreCentipawn = MathF.Round(EncodedEvalLogistic.LogisticToCentipawn((float)manager.Root.Q), 0);
            float nps            = manager.NumStepsTakenThisSearch / elapsedTimeSeconds;

            SearchPrincipalVariation pv;

            using (new SearchContextExecutionBlock(manager.Context))
            {
                pv = new SearchPrincipalVariation(manager.Root);
            }

            //info depth 12 seldepth 27 time 30440 nodes 51100 score cp 105 hashfull 241 nps 1678 tbhits 0 pv e6c6 c5b4 d5e4 d1e1
            int selectiveDepth = pv.Nodes.Count - 1;
            int depth          = (int)MathF.Round(manager.Context.AvgDepth, 0);

            // TODO: tb, hashfull
            string infoUpdate = $"info depth {depth} seldepth {selectiveDepth} time {elapsedTimeSeconds * 1000.0f:F0} "
                                + $"nodes {manager.Root.N:F0} score cp {scoreCentipawn:F0} tbhits {manager.CountTablebaseHits} nps {nps:F0} "
                                + $"pv {pv.ShortStr()} string M= {manager.Root.MAvg:F0}";

            return(infoUpdate);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Overridden virtual method that executs the search
        /// by issuing UCI commands to the LC0 engine with appropriate search limit parameters.
        /// </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)
        {
            DoSearchPrepare();

            if (SetupAction != null)
            {
                SetupAction();
            }

            string fen     = curPositionAndMoves.InitialPosition.FEN;
            string endFEN  = curPositionAndMoves.FinalPosition.FEN;
            string moveStr = curPositionAndMoves.MovesStr;

            // Run the analysis
            LC0VerboseMoveStats lc0Analysis = LC0Engine.AnalyzePositionFromFENAndMoves(fen, moveStr, endFEN, searchLimit);

            if (verbose)
            {
                lc0Analysis.Dump();
            }

            float scoreLC0 = (int)MathF.Round(EncodedEvalLogistic.LogisticToCentipawn(lc0Analysis.SearchEvalLogistic), 0);

            // TODO: can we somehow correctly set the staring N arugment here?
            return(new GameEngineSearchResult(lc0Analysis.BestMove, lc0Analysis.SearchEvalLogistic, scoreLC0, float.NaN,
                                              searchLimit, default, 0, (int)lc0Analysis.NumNodes, (int)lc0Analysis.UCIInfo.Depth));
Ejemplo n.º 3
0
        public static string UCIInfoString(MCTSManager manager, MCTSNode bestMoveRoot = null)
        {
            // If no override bestMoveRoot was specified
            // then it is assumed the move chosen was from the root (not an instamove)
            if (bestMoveRoot == null)
            {
                bestMoveRoot = manager.Root;
            }

            bool wasInstamove = manager.Root != bestMoveRoot;

            float elapsedTimeSeconds = wasInstamove ? 0 : (float)(DateTime.Now - manager.StartTimeThisSearch).TotalSeconds;

            float scoreCentipawn = MathF.Round(EncodedEvalLogistic.LogisticToCentipawn((float)bestMoveRoot.Q), 0);
            float nps            = manager.NumStepsTakenThisSearch / elapsedTimeSeconds;

            SearchPrincipalVariation pv;

            using (new SearchContextExecutionBlock(manager.Context))
            {
                pv = new SearchPrincipalVariation(bestMoveRoot);
            }

            //info depth 12 seldepth 27 time 30440 nodes 51100 score cp 105 hashfull 241 nps 1678 tbhits 0 pv e6c6 c5b4 d5e4 d1e1
            int selectiveDepth        = pv.Nodes.Count - 1;
            int depthOfBestMoveInTree = wasInstamove ? bestMoveRoot.Depth : 0;
            int depth = (int)MathF.Round(manager.Context.AvgDepth - depthOfBestMoveInTree, 0);


            if (wasInstamove)
            {
                // Note that the correct tablebase hits cannot be easily calculated and reported
                return($"info depth {depth} seldepth {selectiveDepth} time 0 "
                       + $"nodes {bestMoveRoot.N:F0} score cp {scoreCentipawn:F0} tbhits {manager.CountTablebaseHits} nps 0 "
                       + $"pv {pv.ShortStr()} string M= {bestMoveRoot.MAvg:F0} instamove");
            }
            else
            {
                return($"info depth {depth} seldepth {selectiveDepth} time {elapsedTimeSeconds * 1000.0f:F0} "
                       + $"nodes {manager.Root.N:F0} score cp {scoreCentipawn:F0} tbhits {manager.CountTablebaseHits} nps {nps:F0} "
                       + $"pv {pv.ShortStr()} string M= {manager.Root.MAvg:F0}");
            }
        }
Ejemplo n.º 4
0
        /// <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,
Ejemplo n.º 5
0
        /// <summary>
        /// Returns an UCI info string appropriate for a given search state.
        /// </summary>
        /// <param name="manager"></param>
        /// <param name="overrideRootMove"></param>
        /// <returns></returns>
        public static string UCIInfoString(MCTSManager manager,
                                           MCTSNode overrideRootMove           = null,
                                           MCTSNode overrideBestMoveNodeAtRoot = null,
                                           int?multiPVIndex = null,
                                           bool useParentN  = true,
                                           bool showWDL     = false,
                                           bool scoreAsQ    = false)
        {
            if (manager.TablebaseImmediateBestMove != default)
            {
                if (multiPVIndex.HasValue && multiPVIndex != 1)
                {
                    return(null);
                }
                else
                {
                    return(OutputUCIInfoTablebaseImmediate(manager, overrideRootMove ?? manager.Root, scoreAsQ));
                }
            }

            bool wasInstamove = manager.Root != overrideRootMove;

            // If no override bestMoveRoot was specified
            // then it is assumed the move chosen was from the root (not an instamove)
            MCTSNode thisRootNode = overrideRootMove ?? manager.Root;

            if (thisRootNode.NumPolicyMoves == 0)
            {
                // Terminal position, nothing to output
                return(null);
            }

            float elapsedTimeSeconds = wasInstamove ? 0 : (float)(DateTime.Now - manager.StartTimeThisSearch).TotalSeconds;

            // Get the principal variation (the first move of which will be the best move)
            SearchPrincipalVariation pv;

            using (new SearchContextExecutionBlock(manager.Context))
            {
                pv = new SearchPrincipalVariation(thisRootNode, overrideBestMoveNodeAtRoot);
            }

            MCTSNode bestMoveNode = pv.Nodes.Count > 1 ? pv.Nodes[1] : pv.Nodes[0];

            // The score displayed corresponds to
            // the Q (average visit value) of the move to be made.
            float scoreToShow;

            if (scoreAsQ)
            {
                scoreToShow = MathF.Round((float)-bestMoveNode.Q * 1000, 0);
            }
            else
            {
                scoreToShow = MathF.Round(EncodedEvalLogistic.LogisticToCentipawn((float)-bestMoveNode.Q), 0);
            }

            float nps = manager.NumStepsTakenThisSearch / elapsedTimeSeconds;

            //info depth 12 seldepth 27 time 30440 nodes 51100 score cp 105 hashfull 241 nps 1678 tbhits 0 pv e6c6 c5b4 d5e4 d1e1
            int selectiveDepth        = pv.Nodes.Count;
            int depthOfBestMoveInTree = wasInstamove ? thisRootNode.Depth : 0;
            int depth = 1 + (int)MathF.Round(manager.Context.AvgDepth - depthOfBestMoveInTree, 0);

            string pvString = multiPVIndex.HasValue ? $"multipv {multiPVIndex} pv {pv.ShortStr()}"
                                              : $"pv {pv.ShortStr()}";

            int n = thisRootNode.N;

            if (!useParentN && overrideBestMoveNodeAtRoot != null)
            {
                n = overrideBestMoveNodeAtRoot.N;
            }

            //score cp 27 wdl 384 326 290
            string strWDL = "";

            if (showWDL)
            {
                // Note that win and loss inverted to reverse perspective.
                strWDL = $" wdl {Math.Round(bestMoveNode.LossP * 1000)} "
                         + $"{Math.Round(bestMoveNode.DrawP * 1000)} "
                         + $"{Math.Round(bestMoveNode.WinP * 1000)}";
            }

            if (wasInstamove)
            {
                // Note that the correct tablebase hits cannot be easily calculated and reported
                return($"info depth {depth} seldepth {selectiveDepth} time 0 "
                       + $"nodes {n:F0} score cp {scoreToShow}{strWDL} tbhits {manager.CountTablebaseHits} nps 0 "
                       + $"{pvString} string M= {thisRootNode.MAvg:F0} ");
            }
            else
            {
                return($"info depth {depth} seldepth {selectiveDepth} time {elapsedTimeSeconds * 1000.0f:F0} "
                       + $"nodes {n:F0} score cp {scoreToShow}{strWDL} tbhits {manager.CountTablebaseHits} nps {nps:F0} "
                       + $"{pvString} string M= {thisRootNode.MAvg:F0}");
            }
        }