/// <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));
/// <summary> /// Analyzes a position until a specified search limit is exhausted. /// </summary> /// <param name="fen">a FEN</param> /// <param name="nodes"></param> /// <returns></returns> public LC0VerboseMoveStats AnalyzePositionFromFENAndMoves(string startFEN, string movesStr, string endFEN, SearchLimit searchLimit) { Position positionEnd = Position.FromFEN(endFEN); List <LC0VerboseMoveStat> moves = new List <LC0VerboseMoveStat>(); UCISearchInfo searchInfo; int searchValueMilliseconds = (int)((float)searchLimit.Value * 1000.0f); switch (searchLimit.Type) { case SearchLimitType.NodesPerMove: searchInfo = Runner.EvalPositionToNodes(startFEN, movesStr, (int)searchLimit.Value); break; case SearchLimitType.SecondsPerMove: searchInfo = Runner.EvalPositionToMovetime(startFEN, movesStr, searchValueMilliseconds); break; case SearchLimitType.NodesForAllMoves: throw new Exception("NodesForAllMoves not supported for Leela Chess Zero"); case SearchLimitType.SecondsForAllMoves: bool weAreWhite = positionEnd.MiscInfo.SideToMove == SideType.White; searchInfo = Runner.EvalPositionRemainingTime(startFEN, movesStr, weAreWhite, searchLimit.MaxMovesToGo, (int)(searchLimit.Value * 1000), (int)(searchLimit.ValueIncrement * 1000)); break; default: throw new Exception($"Unknown SeachLimit.Type {searchLimit.Type}"); } double elapsed = 0;//engine.EngineProcess.TotalProcessorTime.TotalSeconds - startTime; // no more, we now assume win_percentages is requested LeelaVerboseMoveStats ret = new LeelaVerboseMoveStats(positionEnd, searchInfo.BestMove, elapsed, searchInfo.Nodes, LZPositionEvalLogistic.CentipawnToLogistic2018(searchInfo.Score)); float scoreLogistic = searchInfo.ScoreLogistic; LC0VerboseMoveStats ret = new LC0VerboseMoveStats(positionEnd, searchInfo.BestMove, elapsed, searchInfo.Nodes, scoreLogistic, searchInfo); searchInfo.Infos.Reverse(); foreach (string info in searchInfo.Infos) { if (info.Contains("P:")) { moves.Add(new LC0VerboseMoveStat(ret, info)); } } ret.SetMoves(moves); return(LastAnalyzedPositionStats = ret); }
/// <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); }