コード例 #1
0
ファイル: LeafEvaluatorNN.cs プロジェクト: scchess/Ceres
        public void BatchGenerate(MCTSIterator context, Span <MCTSNode> nodes, EvalResultTarget resultTarget)
        {
            try
            {
                if (EvaluatorDef.Location == NNEvaluatorDef.LocationType.Remote)
                {
                    throw new NotImplementedException();
                    //SetBatch(context, nodes);
                    //RunRemote(nodes, resultTarget);
                }
                else
                {
                    SetBatch(context, nodes);
                    RunLocal(nodes, resultTarget);
                }
            }
            catch (Exception exc)
            {
                Console.WriteLine("Error in NodeEvaluatorNN " + exc);
                throw exc;
            }

            NUM_BATCHES_EVALUATED++;
            NUM_POSITIONS_EVALUATED += (ulong)Batch.NumPos;
        }
コード例 #2
0
ファイル: LeafSelectorMulti.cs プロジェクト: scchess/Ceres
        /// <summary>
        /// Constructor for selector over specified MCTSIterator.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="selectorID"></param>
        /// <param name="priorSequence"></param>
        /// <param name="guessNumLeaves"></param>
        public LeafSelectorMulti(MCTSIterator context, int selectorID, PositionWithHistory priorSequence, int guessNumLeaves)
        {
            Debug.Assert(selectorID < ILeafSelector.MAX_SELECTORS);

            if (USE_CUSTOM_THREADPOOL)
            {
                tpm = tpmPool.Value.GetFromPool();
            }

            SelectorID      = selectorID;
            PriorSequence   = priorSequence;
            paramsExecution = context.ParamsSearch.Execution;

            int maxNodesPerBatchForRootPreload = context.ParamsSearch.Execution.RootPreloadDepth > 0 ? MCTSSearchFlow.MAX_PRELOAD_NODES_PER_BATCH : 0;
            int extraLeafsDynamic = 0;

            if (context.ParamsSearch.PaddedBatchSizing)
            {
                extraLeafsDynamic = context.ParamsSearch.PaddedExtraNodesBase + (int)(context.ParamsSearch.PaddedExtraNodesMultiplier * guessNumLeaves);
            }

            leafs = new ListBounded <MCTSNode>(guessNumLeaves + maxNodesPerBatchForRootPreload + extraLeafsDynamic);

            Context = context;
        }
コード例 #3
0
        public MCTSSearchFlow(MCTSManager manager, MCTSIterator context)
        {
            Manager = manager;
            Context = context;

            int numSelectors = context.ParamsSearch.Execution.FlowDirectOverlapped ? 2 : 1;

            batchingManagers = new MCTSBatchParamsManager[numSelectors];

            for (int i = 0; i < numSelectors; i++)
            {
                batchingManagers[i] = new MCTSBatchParamsManager(manager.Context.ParamsSelect.UseDynamicVLoss);
            }

            bool shouldCache = context.EvaluatorDef.CacheMode != Chess.PositionEvalCaching.PositionEvalCache.CacheMode.None;

            string instanceID = "0";

            //Params.Evaluator1 : Params.Evaluator2;
            const bool      LOW_PRIORITY_PRIMARY = false;
            LeafEvaluatorNN nodeEvaluator1       = new LeafEvaluatorNN(context.EvaluatorDef, context.NNEvaluators.Evaluator1, shouldCache,
                                                                       LOW_PRIORITY_PRIMARY, context.Tree.PositionCache, null);// context.ParamsNN.DynamicNNSelectorFunc);

            BlockNNEval1 = new MCTSNNEvaluator(nodeEvaluator1, true);

            if (context.ParamsSearch.Execution.FlowDirectOverlapped)
            {
                // Create a second evaluator (configured like the first) on which to do overlapping.
                LeafEvaluatorNN nodeEvaluator2 = new LeafEvaluatorNN(context.EvaluatorDef, context.NNEvaluators.Evaluator2, shouldCache,
                                                                     false, context.Tree.PositionCache, null);// context.ParamsNN.DynamicNNSelectorFunc);
                BlockNNEval2 = new MCTSNNEvaluator(nodeEvaluator2, true);
            }

            if (context.EvaluatorDef.SECONDARY_NETWORK_ID != null)
            {
                throw new NotImplementedException();
                //NodeEvaluatorNN nodeEvaluatorSecondary = new NodeEvaluatorNN(context.EvaluatorDef, context.ParamsNN.Evaluators.EvaluatorSecondary, false, false, null, null);
                //BlockNNEvalSecondaryNet = new MCTSNNEvaluate(nodeEvaluatorSecondary, false);
            }

            BlockApply = new MCTSApply(context.FirstMoveSampler);

            if (context.ParamsSearch.Execution.RootPreloadDepth > 0)
            {
                rootPreloader = new MCTSRootPreloader();
            }

            if (context.ParamsSearch.Execution.SmartSizeBatches)
            {
                context.NNEvaluators.CalcStatistics(true, 1f);
            }
        }
コード例 #4
0
ファイル: MCTSNode.cs プロジェクト: ekfvoddl3536/Ceres
        /// <summary>
        /// Constructor which creates an MCTSNode wrapper for the raw node at specified index.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="index"></param>
        /// <param name="parent">optionally the parent node</param>
        internal MCTSNode(MCTSIterator context, MCTSNodeStructIndex index, MCTSNode parent = null)
        {
            Debug.Assert(context.Tree.Store.Nodes != null);
            Debug.Assert(index.Index <= context.Tree.Store.Nodes.MaxNodes);

            Context = context;
            Tree    = context.Tree;

            this.parent = parent;
            Span <MCTSNodeStruct> parentArray = context.Tree.Store.Nodes.Span;

            ptr        = (MCTSNodeStruct *)Unsafe.AsPointer(ref parentArray[index.Index]);
            this.index = index;
        }
コード例 #5
0
ファイル: MCTSTree.cs プロジェクト: ekfvoddl3536/Ceres
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="store"></param>
        /// <param name="context"></param>
        /// <param name="maxNodesBound"></param>
        /// <param name="positionCache"></param>
        public MCTSTree(MCTSNodeStore store, MCTSIterator context,
                        int maxNodesBound, int estimatedNumNodes,
                        PositionEvalCache positionCache)
        {
            if (context.ParamsSearch.DrawByRepetitionLookbackPlies > MAX_LENGTH_POS_HISTORY)
            {
                throw new Exception($"DrawByRepetitionLookbackPlies exceeds maximum length of {MAX_LENGTH_POS_HISTORY}");
            }

            Store         = store;
            Context       = context;
            PositionCache = positionCache;

            ChildCreateLocks = new LockSet(128);

            const int ANNOTATION_MIN_CACHE_SIZE = 50_000;
            int       annotationCacheSize       = Math.Min(maxNodesBound, context.ParamsSearch.Execution.NodeAnnotationCacheSize);

            if (annotationCacheSize < ANNOTATION_MIN_CACHE_SIZE &&
                annotationCacheSize < maxNodesBound)
            {
                throw new Exception($"NODE_ANNOTATION_CACHE_SIZE is below minimum size of {ANNOTATION_MIN_CACHE_SIZE}");
            }

            if (maxNodesBound <= annotationCacheSize && !context.ParamsSearch.TreeReuseEnabled)
            {
                // We know with certainty the maximum size, and it will fit inside the cache
                // without purging needed - so just use a simple fixed size cache
                cache = new MCTSNodeCacheArrayFixed(this, maxNodesBound);
            }
            else
            {
                cache = new MCTSNodeCacheArrayPurgeableSet(this, annotationCacheSize, estimatedNumNodes);
            }

            // Populate EncodedPriorPositions with encoded boards
            // corresponding to possible prior moves (before the root of this search)
            EncodedPriorPositions = new List <EncodedPositionBoard>();
            Position[] priorPositions = new Position[9];

            // Get prior positions (last position has highest index)
            priorPositions = PositionHistoryGatherer.DoGetHistoryPositions(PriorMoves, priorPositions, 0, 8, false).ToArray();


            for (int i = priorPositions.Length - 1; i >= 0; i--)
            {
                EncodedPositionBoard thisBoard = EncodedPositionBoard.GetBoard(in priorPositions[i], priorPositions[i].MiscInfo.SideToMove, false);
                EncodedPriorPositions.Add(thisBoard);
            }
        }
コード例 #6
0
        public ListBounded <MCTSNode> Evaluate(MCTSIterator context, ListBounded <MCTSNode> nodes)
        {
            nodes[0].Context.NumNNBatches++;
            nodes[0].Context.NumNNNodes += nodes.Count;

            NUM_EVALUATED += nodes.Count;

            if (resultTarget == LeafEvaluatorNN.EvalResultTarget.PrimaryEvalResult)
            {
                Debug.Assert(nodes[0].EvalResult.IsNull); // null evaluator indicates should have been sent here
            }
            else if (resultTarget == LeafEvaluatorNN.EvalResultTarget.SecondaryEvalResult)
            {
                Debug.Assert(nodes[0].EvalResultSecondary.IsNull); // null evaluator indicates should have been sent here
            }
            Evaluator.BatchGenerate(context, nodes.AsSpan, resultTarget);

            return(nodes);
        }
コード例 #7
0
ファイル: MCTSApply.cs プロジェクト: scchess/Ceres
        /// <summary>
        /// Coordinates (possibly parallelized) application of
        /// evauation results for all nodes in a specified batch.
        /// </summary>
        /// <param name="selectorID"></param>
        /// <param name="batchlet"></param>
        void DoApply(int selectorID, ListBounded <MCTSNode> batchlet)
        {
            DebugVerifyNoDuplicatesAndInFlight(batchlet);

            TOTAL_APPLIED += batchlet.Count;
            if (batchlet.Count == 0)
            {
                return;
            }

            MCTSIterator context = batchlet[0].Context;

            if (batchlet.Count > context.ParamsSearch.Execution.SetPoliciesNumPoliciesPerThread)
            {
                Parallel.Invoke(
                    () => { DoApplySetPolicies(batchlet); },
                    () => { using (new SearchContextExecutionBlock(context)) DoApplyBackup(selectorID, batchlet); });
            }
            else
            {
                DoApplySetPolicies(batchlet);
                DoApplyBackup(selectorID, batchlet);
            }
#if CRASHES
            // The main two operations to be performed are independently and
            // can possibly be performed in parallel
            const int PARALLEL_THRESHOLD = MCTSParamsFixed.APPLY_NUM_POLICIES_PER_THREAD + (MCTSParamsFixed.APPLY_NUM_POLICIES_PER_THREAD / 2);
            if (false && MCTSParamsFixed.APPLY_PARALLEL_ENABLED && batchlet.Count > PARALLEL_THRESHOLD)
            {
                Parallel.Invoke
                (
                    () => DoApplySetPolicies(batchlet),
                    () => DoApplyBackup(batchlet)
                );
            }
            else
            {
                DoApplySetPolicies(batchlet); // must go first
                DoApplyBackup(batchlet);
            }
            //foreach (var node in batchlet.Nodes) node.EvalResult = null;
#endif
        }
コード例 #8
0
ファイル: LeafEvaluatorNN.cs プロジェクト: scchess/Ceres
        /// <summary>
        /// Sets the Batch field with set of positions coming from a specified Span<MCTSNode>.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="nodes"></param>
        /// <returns></returns>
        int SetBatch(MCTSIterator context, Span <MCTSNode> nodes)
        {
            //Console.WriteLine(nodes.Length + " BATCH NODES");
            if (nodes.Length > NNEvaluatorDef.MAX_BATCH_SIZE)
            {
                throw new Exception($"Requested batch is larger than {NNEvaluatorDef.MAX_BATCH_SIZE}");
            }

            if (nodes.Length > 0)
            {
                for (int i = 0; i < nodes.Length; i++)
                {
                    if (EvaluatorDef.PositionTransform == NNEvaluatorDef.PositionTransformType.Mirror)
                    {
                        if (rawPosArray[i].MiscInfo.InfoPosition.MirrorEquivalent)
                        {
                            EncodedPositionWithHistory mirrored = rawPosArray[i].Mirrored;
                            nodes[i].Annotation.CalcRawPosition(nodes[i], ref mirrored);
                        }
                    }
                    else
                    {
                        nodes[i].Annotation.CalcRawPosition(nodes[i], ref rawPosArray[i]);
                    }
                }

                if (EvaluatorDef.Location == NNEvaluatorDef.LocationType.Local)
                {
                    Batch.Set(rawPosArray, nodes.Length);
                }

                if (BatchEvaluatorIndexDynamicSelector != null)
                {
                    Batch.PreferredEvaluatorIndex = (short)BatchEvaluatorIndexDynamicSelector(context);
                }
            }

            return(nodes.Length);
        }
コード例 #9
0
        public static void DumpAllNodes(MCTSIterator context, ref MCTSNodeStruct node,
                                        Base.DataType.Trees.TreeTraversalType type = Base.DataType.Trees.TreeTraversalType.BreadthFirst,
                                        bool childDetail = false)
        {
            int index = 1;

            // Visit all nodes and verify various conditions are true
            node.Traverse(context.Tree.Store,
                          (ref MCTSNodeStruct node) =>
            {
                Console.WriteLine(index + " " + node);
                if (childDetail)
                {
                    int childIndex = 0;
                    foreach (MCTSNodeStructChild childInfo in node.Children)
                    {
                        Console.WriteLine($"  {childIndex++,3} {childInfo}");
                    }
                }
                index++;
                return(true);
            }, type);
        }
コード例 #10
0
 /// <summary>
 /// Constructor (with provided refernece to another MCTSIterator with which to share).
 /// </summary>
 /// <param name="otherContext"></param>
 public LeafEvaluatorReuseOtherTree(MCTSIterator otherContext)
 {
     OtherContext = otherContext;
 }
コード例 #11
0
        void ProcessEPD(int epdNum, EPDEntry epd, bool outputDetail, ObjectPool <object> otherEngines)
        {
            UCISearchInfo otherEngineAnalysis2 = default;

            EPDEntry epdToUse = epd;

            Task RunNonCeres()
            {
                if (Def.ExternalEngineDef != null)
                {
                    object engineObj = otherEngines.GetFromPool();

                    if (engineObj is LC0Engine)
                    {
                        LC0Engine le = (LC0Engine)engineObj;

                        // Run test 2 first since that's the one we dump in detail, to avoid any possible caching effect from a prior run
                        otherEngineAnalysis2 = le.AnalyzePositionFromFEN(epdToUse.FEN, epdToUse.StartMoves, Def.ExternalEngineDef.SearchLimit);
                        //            leelaAnalysis2 = le.AnalyzePositionFromFEN(epdToUse.FEN, new SearchLimit(SearchLimit.LimitType.NodesPerMove, 2)); // **** TEMP
                        otherEngines.RestoreToPool(le);
                    }
                    else
                    {
                        UCIGameRunner runner = (engineObj is UCIGameRunner) ? (engineObj as UCIGameRunner)
            : (engineObj as GameEngineUCI).UCIRunner;
                        string moveType  = Def.ExternalEngineDef.SearchLimit.Type == SearchLimitType.NodesPerMove ? "nodes" : "movetime";
                        int    moveValue = moveType == "nodes" ? (int)Def.ExternalEngineDef.SearchLimit.Value : (int)Def.ExternalEngineDef.SearchLimit.Value * 1000;
                        runner.EvalPositionPrepare();
                        otherEngineAnalysis2 = runner.EvalPosition(epdToUse.FEN, epdToUse.StartMoves, moveType, moveValue, null);
                        otherEngines.RestoreToPool(runner);
                        //          public UCISearchInfo EvalPosition(int engineNum, string fenOrPositionCommand, string moveType, int moveMetric, bool shouldCache = false)
                    }
                }
                return(Task.CompletedTask);
            }

            bool EXTERNAL_CONCURRENT = numConcurrentSuiteThreads > 1;

            Task lzTask = EXTERNAL_CONCURRENT ? Task.Run(RunNonCeres) : RunNonCeres();

            // Comptue search limit
            // If possible, adjust for the fact that LC0 "cheats" by going slightly over node budget
            SearchLimit ceresSearchLimit1 = Def.CeresEngine1Def.SearchLimit;
            SearchLimit ceresSearchLimit2 = Def.CeresEngine2Def?.SearchLimit;

            if (Def.CeresEngine1Def.SearchLimit.Type == SearchLimitType.NodesPerMove &&
                otherEngineAnalysis2 != null &&
                !Def.Engine1Def.SearchParams.FutilityPruningStopSearchEnabled)
            {
                if (Def.CeresEngine1Def.SearchLimit.Type == SearchLimitType.NodesPerMove)
                {
                    ceresSearchLimit1 = new SearchLimit(SearchLimitType.NodesPerMove, otherEngineAnalysis2.Nodes);
                }
                if (Def.CeresEngine1Def.SearchLimit.Type == SearchLimitType.NodesPerMove)
                {
                    ceresSearchLimit2 = new SearchLimit(SearchLimitType.NodesPerMove, otherEngineAnalysis2.Nodes);
                }
            }

            PositionWithHistory pos = PositionWithHistory.FromFENAndMovesSAN(epdToUse.FEN, epdToUse.StartMoves);

            // TODO: should this be switched to GameEngineCeresInProcess?

            // Note that if we are running both Ceres1 and Ceres2 we alternate which search goes first.
            // This prevents any systematic difference/benefit that might come from order
            // (for example if we reuse position evaluations from the other tree, which can benefit only one of the two searches).
            MCTSearch search1 = null;
            MCTSearch search2 = null;

            if (epdNum % 2 == 0 || Def.CeresEngine2Def == null)
            {
                search1 = new MCTSearch();
                search1.Search(evaluatorSet1, Def.Engine1Def.SelectParams, Def.Engine1Def.SearchParams, null, null, null,
                               pos, ceresSearchLimit1, false, DateTime.Now, null, null, true);

                MCTSIterator shareContext = null;
                if (Def.RunCeres2Engine)
                {
                    if (Def.Engine2Def.SearchParams.ReusePositionEvaluationsFromOtherTree)
                    {
                        shareContext = search1.Manager.Context;
                    }

                    search2 = new MCTSearch();
                    search2.Search(evaluatorSet2, Def.Engine2Def.SelectParams, Def.Engine2Def.SearchParams, null, null, shareContext,
                                   pos, ceresSearchLimit2, false, DateTime.Now, null, null, true);
                }
            }
            else
            {
                search2 = new MCTSearch();
                search2.Search(evaluatorSet2, Def.Engine2Def.SelectParams, Def.Engine2Def.SearchParams, null, null, null,
                               pos, ceresSearchLimit2, false, DateTime.Now, null, null, true);

                MCTSIterator shareContext = null;
                if (Def.Engine1Def.SearchParams.ReusePositionEvaluationsFromOtherTree)
                {
                    shareContext = search2.Manager.Context;
                }

                search1 = new MCTSearch();
                search1.Search(evaluatorSet1, Def.Engine1Def.SelectParams, Def.Engine1Def.SearchParams, null, null, shareContext,
                               pos, ceresSearchLimit1, false, DateTime.Now, null, null, true);
            }

            // Wait for LZ analysis
            if (EXTERNAL_CONCURRENT)
            {
                lzTask.Wait();
            }

            Move bestMoveOtherEngine = default;

            if (Def.ExternalEngineDef != null)
            {
                MGPosition thisPosX = PositionWithHistory.FromFENAndMovesUCI(epdToUse.FEN, epdToUse.StartMoves).FinalPosMG;

                MGMove lzMoveMG1 = MGMoveFromString.ParseMove(thisPosX, otherEngineAnalysis2.BestMove);
                bestMoveOtherEngine = MGMoveConverter.ToMove(lzMoveMG1);
            }

            Move bestMoveCeres1 = MGMoveConverter.ToMove(search1.BestMove);

            Move bestMoveCeres2 = search2 == null ? default : MGMoveConverter.ToMove(search2.BestMove);

                                  char CorrectStr(Move move) => epdToUse.CorrectnessScore(move, 10) == 10 ? '+' : '.';

                                  int scoreCeres1      = epdToUse.CorrectnessScore(bestMoveCeres1, 10);
                                  int scoreCeres2      = epdToUse.CorrectnessScore(bestMoveCeres2, 10);
                                  int scoreOtherEngine = epdToUse.CorrectnessScore(bestMoveOtherEngine, 10);

                                  SearchResultInfo result1 = new SearchResultInfo(search1.Manager, search1.BestMove);
                                  SearchResultInfo result2 = search2 == null ? null : new SearchResultInfo(search2.Manager, search2.BestMove);

                                  accCeres1 += scoreCeres1;
                                  accCeres2 += scoreCeres2;

                                  // Accumulate how many nodes were required to find one of the correct moves
                                  // (in the cases where both succeeded)
                                  if (scoreCeres1 > 0 && (search2 == null || scoreCeres2 > 0))
                                  {
                                      accWCeres1 += (scoreCeres1 == 0) ? result1.N : result1.NumNodesWhenChoseTopNNode;
                                      if (search2 != null)
                                      {
                                          accWCeres2 += (scoreCeres2 == 0) ? result2.N : result2.NumNodesWhenChoseTopNNode;
                                      }
                                      numSearchesBothFound++;
                                  }
                                  this.avgOther += scoreOtherEngine;

                                  numSearches++;

                                  float avgCeres1  = (float)accCeres1 / numSearches;
                                  float avgCeres2  = (float)accCeres2 / numSearches;
                                  float avgWCeres1 = (float)accWCeres1 / numSearchesBothFound;
                                  float avgWCeres2 = (float)accWCeres2 / numSearchesBothFound;

                                  float avgOther = (float)this.avgOther / numSearches;

                                  string MoveIfWrong(Move m) => m.IsNull || epdToUse.CorrectnessScore(m, 10) == 10 ? "    " : m.ToString().ToLower();

                                  int diff1 = scoreCeres1 - scoreOtherEngine;

                                  //NodeEvaluatorNeuralNetwork
                                  int evalNumBatches1 = result1.NumNNBatches;
                                  int evalNumPos1     = result1.NumNNNodes;
                                  int evalNumBatches2 = search2 == null ? 0 : result2.NumNNBatches;
                                  int evalNumPos2     = search2 == null ? 0 : result2.NumNNNodes;

                                  string correctMove = null;

                                  if (epdToUse.AMMoves != null)
                                  {
                                      correctMove = "-" + epdToUse.AMMoves[0];
                                  }
                                  else if (epdToUse.BMMoves != null)
                                  {
                                      correctMove = epdToUse.BMMoves[0];
                                  }

                                  float otherEngineTime = otherEngineAnalysis2 == null ? 0 : (float)otherEngineAnalysis2.EngineReportedSearchTime / 1000.0f;

                                  totalTimeOther  += otherEngineTime;
                                  totalTimeCeres1 += (float)search1.TimingInfo.ElapsedTimeSecs;

                                  totalNodesOther += otherEngineAnalysis2 == null ? 0 : (int)otherEngineAnalysis2.Nodes;
                                  totalNodes1     += (int)result1.N;

                                  sumEvalNumPosOther += otherEngineAnalysis2 == null ? 0 : (int)otherEngineAnalysis2.Nodes;
                                  sumEvalNumBatches1 += evalNumBatches1;
                                  sumEvalNumPos1     += evalNumPos1;

                                  if (Def.RunCeres2Engine)
                                  {
                                      totalTimeCeres2    += (float)search2.TimingInfo.ElapsedTimeSecs;
                                      totalNodes2        += (int)result2.N;
                                      sumEvalNumBatches2 += evalNumBatches2;
                                      sumEvalNumPos2     += evalNumPos2;
                                  }

                                  float Adjust(int score, float frac) => score == 0 ? 0 : Math.Max(1.0f, MathF.Round(frac * 100.0f, 0));

                                  string worker1PickedNonTopNMoveStr = result1.PickedNonTopNMoveStr;
                                  string worker2PickedNonTopNMoveStr = result2?.PickedNonTopNMoveStr;

                                  bool ex = otherEngineAnalysis2 != null;
                                  bool c2 = search2 != null;

                                  Writer writer = new Writer(epdNum == 0);

                                  writer.Add("#", $"{epdNum,4}", 6);

                                  if (ex)
                                  {
                                      writer.Add("CEx", $"{avgOther,5:F2}", 7);
                                  }
                                  writer.Add("CC", $"{avgCeres1,5:F2}", 7);
                                  if (c2)
                                  {
                                      writer.Add("CC2", $"{avgCeres2,5:F2}", 7);
                                  }

                                  writer.Add("P", $" {0.001f * avgWCeres1,7:f2}", 9);
                                  if (c2)
                                  {
                                      writer.Add("P2", $" {0.001f * avgWCeres2,7:f2}", 9);
                                  }

                                  if (ex)
                                  {
                                      writer.Add("SEx", $"{scoreOtherEngine,3}", 5);
                                  }
                                  writer.Add("SC", $"{scoreCeres1,3}", 5);
                                  if (c2)
                                  {
                                      writer.Add("SC2", $"{scoreCeres2,3}", 5);
                                  }

                                  if (ex)
                                  {
                                      writer.Add("MEx", $"{otherEngineAnalysis2.BestMove,7}", 9);
                                  }
                                  writer.Add("MC", $"{search1.Manager.BestMoveMG,7}", 9);
                                  if (c2)
                                  {
                                      writer.Add("MC2", $"{search2.Manager.BestMoveMG,7}", 9);
                                  }

                                  writer.Add("Fr", $"{worker1PickedNonTopNMoveStr}{ 100.0f * result1.TopNNodeN / result1.N,3:F0}%", 9);
                                  if (c2)
                                  {
                                      writer.Add("Fr2", $"{worker2PickedNonTopNMoveStr}{ 100.0f * result2?.TopNNodeN / result2?.N,3:F0}%", 9);
                                  }

                                  writer.Add("Yld", $"{result1.NodeSelectionYieldFrac,6:f3}", 9);
                                  if (c2)
                                  {
                                      writer.Add("Yld2", $"{result2.NodeSelectionYieldFrac,6:f3}", 9);
                                  }

                                  // Search time
                                  if (ex)
                                  {
                                      writer.Add("TimeEx", $"{otherEngineTime,7:F2}", 9);
                                  }
                                  writer.Add("TimeC", $"{search1.TimingInfo.ElapsedTimeSecs,7:F2}", 9);
                                  if (c2)
                                  {
                                      writer.Add("TimeC2", $"{search2.TimingInfo.ElapsedTimeSecs,7:F2}", 9);
                                  }

                                  writer.Add("Dep", $"{result1.AvgDepth,5:f1}", 7);
                                  if (c2)
                                  {
                                      writer.Add("Dep2", $"{result2.AvgDepth,5:f1}", 7);
                                  }

                                  // Nodes
                                  if (ex)
                                  {
                                      writer.Add("NEx", $"{otherEngineAnalysis2.Nodes,12:N0}", 14);
                                  }
                                  writer.Add("Nodes", $"{result1.N,12:N0}", 14);
                                  if (c2)
                                  {
                                      writer.Add("Nodes2", $"{result2.N,12:N0}", 14);
                                  }

                                  // Fraction when chose top N
                                  writer.Add("Frac", $"{Adjust(scoreCeres1, result1.FractionNumNodesWhenChoseTopNNode),4:F0}", 6);
                                  if (c2)
                                  {
                                      writer.Add("Frac2", $"{Adjust(scoreCeres2, result2.FractionNumNodesWhenChoseTopNNode),4:F0}", 6);
                                  }

                                  // Score (Q)
                                  if (ex)
                                  {
                                      writer.Add("QEx", $"{otherEngineAnalysis2.ScoreLogistic,6:F3}", 8);
                                  }
                                  writer.Add("QC", $"{result1.Q,6:F3}", 8);
                                  if (c2)
                                  {
                                      writer.Add("QC2", $"{result2.Q,6:F3}", 8);
                                  }

                                  // Num batches&positions
                                  writer.Add("Batches", $"{evalNumBatches1,8:N0}", 10);
                                  writer.Add("NNEvals", $"{evalNumPos1,11:N0}", 13);
                                  if (c2)
                                  {
                                      writer.Add("Batches2", $"{evalNumBatches2,8:N0}", 10);
                                      writer.Add("NNEvals2", $"{evalNumPos2,11:N0}", 13);
                                  }

                                  // Tablebase hits
                                  writer.Add("TBase", $"{(search1.CountSearchContinuations > 0 ? 0 : search1.Manager.CountTablebaseHits),8:N0}", 10);
                                  if (c2)
                                  {
                                      writer.Add("TBase2", $"{(search2.CountSearchContinuations > 0 ? 0 : search2.Manager.CountTablebaseHits),8:N0}", 10);
                                  }

//      writer.Add("EPD", $"{epdToUse.ID,-30}", 32);

                                  if (outputDetail)
                                  {
                                      if (epdNum == 0)
                                      {
                                          Def.Output.WriteLine(writer.ids.ToString());
                                          Def.Output.WriteLine(writer.dividers.ToString());
                                      }
                                      Def.Output.WriteLine(writer.text.ToString());
                                  }

                                  //      MCTSNodeStorageSerialize.Save(worker1.Context.Store, @"c:\temp", "TESTSORE");

                                  search1?.Manager?.Dispose();
                                  if (!object.ReferenceEquals(search1?.Manager, search2?.Manager))
                                  {
                                      search2?.Manager?.Dispose();
                                  }
        }
コード例 #12
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,