/// <summary> /// Analyzes a specified position until a specified limit is exhausted. /// /// TODO: This method is highly redundant (and inferior to?) the next method AnalyzePositionFromFENAndMoves, delete it. /// </summary> /// <param name="fenOrFENAndMoves">a FEN</param> /// <param name="nodes"></param> /// <returns></returns> public UCISearchInfo AnalyzePositionFromFEN(string fenAndMovesString, SearchLimit searchLimit) { List <LC0VerboseMoveStat> moves = new List <LC0VerboseMoveStat>(); Runner.EvalPositionPrepare(); UCISearchInfo searchInfo; if (searchLimit.Type == SearchLimitType.SecondsPerMove) { searchInfo = Runner.EvalPositionToMovetime(fenAndMovesString, (int)(searchLimit.Value * 1000.0f)); } else if (searchLimit.Type == SearchLimitType.NodesPerMove) { searchInfo = Runner.EvalPositionToNodes(fenAndMovesString, (int)searchLimit.Value); } else { throw new Exception("Unknown search limit " + searchLimit.Type); } double elapsed = searchInfo.EngineReportedSearchTime / 1000.0f; // no more, we now assume win_percentages is requested LeelaVerboseMoveStats ret = new LeelaVerboseMoveStats(positionEnd, searchInfo.BestMove, elapsed, searchInfo.Nodes, LZPositionEvalLogistic.CentipawnToLogistic2018(searchInfo.Score)); float scoreConverted = 2.0f * (((float)searchInfo.ScoreCentipawns / 10_000f) - 0.5f); PositionWithHistory pwh = PositionWithHistory.FromFENAndMovesUCI(fenAndMovesString); LC0VerboseMoveStats ret = new LC0VerboseMoveStats(pwh.FinalPosition, searchInfo.BestMove, elapsed, searchInfo.Nodes, scoreConverted, searchInfo); foreach (string info in searchInfo.Infos) { if (info.Contains("P:")) { moves.Add(new LC0VerboseMoveStat(ret, info)); } } ret.SetMoves(moves); // TODO: Someday perhaps make LeelaVerboseMoveStats a subclass of UCISearchInfo so this is more elegant UCISearchInfo uciInfo = new UCISearchInfo(null, ret.BestMove, null); uciInfo.Nodes = ret.NumNodes; uciInfo.EngineReportedSearchTime = (int)(1000.0f * ret.ElapsedTime); uciInfo.ExtraInfo = ret; uciInfo.BestMove = ret.BestMove; return(uciInfo); }
protected override GameEngineSearchResult DoSearch(PositionWithHistory curPositionAndMoves, SearchLimit searchLimit, List <GameMoveStat> gameMoveHistory, ProgressCallback callback, bool verbose) { DoSearchPrepare(); bool weAreWhite = curPositionAndMoves.FinalPosition.MiscInfo.SideToMove == SideType.White; UCISearchInfo gameInfo; switch (searchLimit.Type) { case SearchLimitType.SecondsPerMove: gameInfo = UCIRunner.EvalPositionToMovetime(curPositionAndMoves.FENAndMovesString, (int)(searchLimit.Value * 1000)); break; case SearchLimitType.NodesPerMove: gameInfo = UCIRunner.EvalPositionToNodes(curPositionAndMoves.FENAndMovesString, (int)(searchLimit.Value)); break; case SearchLimitType.NodesForAllMoves: using (new TimingBlock(new TimingStats(), TimingBlock.LoggingType.None)) { gameInfo = UCIRunner.EvalPositionRemainingNodes(curPositionAndMoves.FENAndMovesString, weAreWhite, searchLimit.MaxMovesToGo, (int)(searchLimit.Value), (int)(searchLimit.ValueIncrement)); } break; case SearchLimitType.SecondsForAllMoves: using (new TimingBlock(new TimingStats(), TimingBlock.LoggingType.None)) { gameInfo = UCIRunner.EvalPositionRemainingTime(curPositionAndMoves.FENAndMovesString, weAreWhite, searchLimit.MaxMovesToGo, (int)(searchLimit.Value * 1000), (int)(searchLimit.ValueIncrement * 1000)); } break; default: throw new NotSupportedException($"Unsupported MoveType {searchLimit.Type}"); } float q = EncodedEvalLogistic.CentipawnToLogistic(gameInfo.ScoreCentipawns); return(new GameEngineSearchResult(gameInfo.BestMove, q, gameInfo.ScoreCentipawns, float.NaN, searchLimit, default, 0, (int)gameInfo.Nodes, gameInfo.Depth));