Skip to content

Commit

Permalink
Refactor pv printing
Browse files Browse the repository at this point in the history
Also fix the case which is currently printing depth 0.

fixes #5019
closes #5020

No functional change
  • Loading branch information
Disservin authored and vondele committed Feb 3, 2024
1 parent 13eb023 commit 16afec0
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 79 deletions.
70 changes: 59 additions & 11 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <initializer_list>
#include <iostream>
#include <utility>
#include <sstream>

#include "evaluate.h"
#include "misc.h"
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1878,6 +1871,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,
Expand Down
6 changes: 6 additions & 0 deletions src/search.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <cstdint>
#include <memory>
#include <vector>
#include <string>

#include "misc.h"
#include "movepick.h"
Expand Down Expand Up @@ -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;
Expand Down
58 changes: 1 addition & 57 deletions src/uci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <optional>
#include <sstream>
#include <vector>
#include <cstdint>

#include "benchmark.h"
#include "evaluate.h"
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 0 additions & 11 deletions src/uci.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#ifndef UCI_H_INCLUDED
#define UCI_H_INCLUDED

#include <cstdint>
#include <iostream>
#include <string>
#include <unordered_map>
Expand All @@ -37,10 +36,6 @@ namespace Eval::NNUE {
enum NetSize : int;
}

namespace Search {
class Worker;
}

class Move;
enum Square : int;
using Value = int;
Expand All @@ -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);

Expand Down

0 comments on commit 16afec0

Please sign in to comment.