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 official-stockfish#5019
closes official-stockfish#5020

No functional change
  • Loading branch information
Disservin authored and Viren6 committed Feb 12, 2024
1 parent fd6c4ef commit c280159
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 @@ -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,
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 c280159

Please sign in to comment.