From c280159171501f4f2479f514e099b7c0de007953 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 27 Jan 2024 22:27:22 +0100 Subject: [PATCH] Refactor pv printing Also fix the case which is currently printing depth 0. fixes #5019 closes https://github.com/official-stockfish/Stockfish/pull/5020 No functional change --- src/search.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++-------- src/search.h | 6 +++++ src/uci.cpp | 58 +---------------------------------------- src/uci.h | 11 -------- 4 files changed, 66 insertions(+), 79 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 87e63c571e2..3f5edbfd90b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "evaluate.h" #include "misc.h" @@ -192,9 +193,7 @@ void Search::Worker::start_searching() { // Send again PV info if we have a new best thread if (bestThread != this) - sync_cout << UCI::pv(*bestThread, main_manager()->tm.elapsed(threads.nodes_searched()), - threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - tbConfig.rootInTB) + sync_cout << main_manager()->pv(*bestThread, threads, tt, bestThread->completedDepth) << sync_endl; sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); @@ -336,10 +335,7 @@ void Search::Worker::iterative_deepening() { // the UI) before a re-search. if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) && mainThread->tm.elapsed(threads.nodes_searched()) > 3000) - sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), - threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - tbConfig.rootInTB) - << sync_endl; + sync_cout << main_manager()->pv(*this, threads, tt, rootDepth) << sync_endl; // In case of failing low/high increase aspiration window and // re-search, otherwise exit the loop. @@ -376,10 +372,7 @@ void Search::Worker::iterative_deepening() { // had time to fully search other root-moves. Thus we suppress this output and // below pick a proven score/PV for this thread (from the previous iteration). && !(threads.abortedSearch && rootMoves[0].uciScore <= VALUE_TB_LOSS_IN_MAX_PLY)) - sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), - threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - tbConfig.rootInTB) - << sync_endl; + sync_cout << main_manager()->pv(*this, threads, tt, rootDepth) << sync_endl; } if (!threads.stop) @@ -1879,6 +1872,61 @@ void SearchManager::check_time(Search::Worker& worker) { worker.threads.stop = worker.threads.abortedSearch = true; } +std::string SearchManager::pv(const Search::Worker& worker, + const ThreadPool& threads, + const TranspositionTable& tt, + Depth depth) const { + std::stringstream ss; + + const auto nodes = threads.nodes_searched(); + const auto& rootMoves = worker.rootMoves; + const auto& pos = worker.rootPos; + size_t pvIdx = worker.pvIdx; + TimePoint time = tm.elapsed(nodes) + 1; + size_t multiPV = std::min(size_t(worker.options["MultiPV"]), rootMoves.size()); + uint64_t tbHits = threads.tb_hits() + (worker.tbConfig.rootInTB ? rootMoves.size() : 0); + + for (size_t i = 0; i < multiPV; ++i) + { + bool updated = rootMoves[i].score != -VALUE_INFINITE; + + if (depth == 1 && !updated && i > 0) + continue; + + Depth d = updated ? depth : std::max(1, depth - 1); + Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore; + + if (v == -VALUE_INFINITE) + v = VALUE_ZERO; + + bool tb = worker.tbConfig.rootInTB && std::abs(v) <= VALUE_TB; + v = tb ? rootMoves[i].tbScore : v; + + if (ss.rdbuf()->in_avail()) // Not at first line + ss << "\n"; + + ss << "info" + << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 + << " score " << UCI::value(v); + + if (worker.options["UCI_ShowWDL"]) + ss << UCI::wdl(v, pos.game_ply()); + + if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact + ss << (rootMoves[i].scoreLowerbound + ? " lowerbound" + : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); + + ss << " nodes " << nodes << " nps " << nodes * 1000 / time << " hashfull " << tt.hashfull() + << " tbhits " << tbHits << " time " << time << " pv"; + + for (Move m : rootMoves[i].pv) + ss << " " << UCI::move(m, pos.is_chess960()); + } + + return ss.str(); +} + // Called in case we have no ponder move before exiting the search, // for instance, in case we stop the search during a fail high at root. // We try hard to have a ponder move to return to the GUI, diff --git a/src/search.h b/src/search.h index b4a65d8e6f6..c8534b40177 100644 --- a/src/search.h +++ b/src/search.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "misc.h" #include "movepick.h" @@ -150,6 +151,11 @@ class SearchManager: public ISearchManager { public: void check_time(Search::Worker& worker) override; + std::string pv(const Search::Worker& worker, + const ThreadPool& threads, + const TranspositionTable& tt, + Depth depth) const; + Stockfish::TimeManagement tm; int callsCnt; std::atomic_bool ponder; diff --git a/src/uci.cpp b/src/uci.cpp index e6107d4713a..d1d69d69752 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "benchmark.h" #include "evaluate.h" @@ -365,63 +366,6 @@ std::string UCI::move(Move m, bool chess960) { return move; } -std::string UCI::pv(const Search::Worker& workerThread, - TimePoint elapsed, - uint64_t nodesSearched, - uint64_t tb_hits, - int hashfull, - bool rootInTB) { - std::stringstream ss; - TimePoint time = elapsed + 1; - const auto& rootMoves = workerThread.rootMoves; - const auto& depth = workerThread.completedDepth; - const auto& pos = workerThread.rootPos; - size_t pvIdx = workerThread.pvIdx; - size_t multiPV = std::min(size_t(workerThread.options["MultiPV"]), rootMoves.size()); - uint64_t tbHits = tb_hits + (rootInTB ? rootMoves.size() : 0); - - - for (size_t i = 0; i < multiPV; ++i) - { - bool updated = rootMoves[i].score != -VALUE_INFINITE; - - if (depth == 1 && !updated && i > 0) - continue; - - Depth d = updated ? depth : std::max(1, depth - 1); - Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore; - - if (v == -VALUE_INFINITE) - v = VALUE_ZERO; - - bool tb = rootInTB && std::abs(v) <= VALUE_TB; - v = tb ? rootMoves[i].tbScore : v; - - if (ss.rdbuf()->in_avail()) // Not at first line - ss << "\n"; - - ss << "info" - << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 - << " score " << value(v); - - if (workerThread.options["UCI_ShowWDL"]) - ss << wdl(v, pos.game_ply()); - - if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact - ss << (rootMoves[i].scoreLowerbound - ? " lowerbound" - : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); - - ss << " nodes " << nodesSearched << " nps " << nodesSearched * 1000 / time << " hashfull " - << hashfull << " tbhits " << tbHits << " time " << time << " pv"; - - for (Move m : rootMoves[i].pv) - ss << " " << move(m, pos.is_chess960()); - } - - return ss.str(); -} - namespace { // The win rate model returns the probability of winning (in per mille units) given an // eval and a game ply. It fits the LTC fishtest statistics rather accurately. diff --git a/src/uci.h b/src/uci.h index cd113b1ad29..9d5f524ad65 100644 --- a/src/uci.h +++ b/src/uci.h @@ -19,7 +19,6 @@ #ifndef UCI_H_INCLUDED #define UCI_H_INCLUDED -#include #include #include #include @@ -37,10 +36,6 @@ namespace Eval::NNUE { enum NetSize : int; } -namespace Search { -class Worker; -} - class Move; enum Square : int; using Value = int; @@ -55,12 +50,6 @@ class UCI { static std::string value(Value v); static std::string square(Square s); static std::string move(Move m, bool chess960); - static std::string pv(const Search::Worker& workerThread, - TimePoint elapsed, - uint64_t nodesSearched, - uint64_t tb_hits, - int hashfull, - bool rootInTB); static std::string wdl(Value v, int ply); static Move to_move(const Position& pos, std::string& str);