mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 12:47:08 +00:00
[bugfix] Respect bound flags for searched moves in multiPV analysis
Master in `pv()` incorrectly assumes that any root move with `i != pvIdx` has an exact score. This can lead to the suppression of bound flags for (a) root moves that have been partially searched in an iteration but do not end up to be best move, or (b) when an aborted search for `pvIdx > 0` leads to a new best move. This latter case is the cause for issue #6783. Fixes #6783. The bug in its incarnation (a) was independently discovered by @ciekce a couple of weeks ago, and we have jointly discussed a patch that fixes it. He is therefore a co-author of the bugfix part of this patch. While at it, we also fix three minor oversights in the PV roll-back for aborted searches with a loss score: * We only roll back the PV if the aborted search would report an exact score. * Master inadvertently may have reported a bound for the rolled-back score+PV. * We ensure a final UCI info line with the correct search depth and searched nodes statistics etc. closes https://github.com/official-stockfish/Stockfish/pull/6784 No functional change Co-Authored-By: Ciekce <44617491+Ciekce@users.noreply.github.com>
This commit is contained in:
committed by
Joost VandeVondele
co-authored by
Ciekce
parent
2be0b2cdb4
commit
3e3e976fdf
+19
-19
@@ -469,10 +469,11 @@ bool Search::Worker::iterative_deepening() {
|
||||
lastIterationPV = rootMoves[0].pv;
|
||||
}
|
||||
|
||||
// A mated-in/TB-loss score from an aborted search cannot be trusted: the loss
|
||||
// could be delayed or refuted upon exploring the remaining root-moves.
|
||||
// An exact mated-in/TB-loss score from an aborted search cannot be trusted: the
|
||||
// loss could be delayed or refuted upon exploring the remaining root-moves.
|
||||
// Thus here we roll back to the score from the previous iteration.
|
||||
else if (!pvIdx && rootMoves[0].score != -VALUE_INFINITE && is_loss(rootMoves[0].score))
|
||||
else if (!pvIdx && rootMoves[0].score != -VALUE_INFINITE && is_loss(rootMoves[0].score)
|
||||
&& !rootMoves[0].scoreLowerbound && !rootMoves[0].scoreUpperbound)
|
||||
{
|
||||
// Bring the last best move to the front for best thread selection.
|
||||
if (!lastIterationPV.empty())
|
||||
@@ -483,11 +484,11 @@ bool Search::Worker::iterative_deepening() {
|
||||
rootMoves[0].score = rootMoves[0].uciScore = rootMoves[0].previousScore;
|
||||
|
||||
if (mainThread)
|
||||
uciPvSent = true;
|
||||
uciPvSent = false;
|
||||
}
|
||||
// For an aborted d1 search we label the loss score as inexact.
|
||||
else if (!rootMoves[0].scoreLowerbound)
|
||||
rootMoves[0].scoreUpperbound = true;
|
||||
// For an aborted d1 search we label the loss score as a lower bound.
|
||||
else
|
||||
rootMoves[0].scoreLowerbound = true;
|
||||
}
|
||||
|
||||
// Have we found a "mate in x" after a completed iteration?
|
||||
@@ -2143,31 +2144,29 @@ void SearchManager::pv(Search::Worker& worker,
|
||||
const auto nodes = threads.nodes_searched();
|
||||
auto& rootMoves = worker.rootMoves;
|
||||
auto& pos = worker.rootPos;
|
||||
size_t pvIdx = worker.pvIdx;
|
||||
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;
|
||||
bool usePreviousScore = rootMoves[i].score == -VALUE_INFINITE;
|
||||
|
||||
if (depth == 1 && !updated && i > 0)
|
||||
if (depth == 1 && usePreviousScore && i > 0)
|
||||
continue;
|
||||
|
||||
Depth d = updated ? depth : std::max(1, depth - 1);
|
||||
Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore;
|
||||
Depth d = usePreviousScore ? std::max(1, depth - 1) : depth;
|
||||
Value v = usePreviousScore ? rootMoves[i].previousScore : rootMoves[i].uciScore;
|
||||
|
||||
if (v == -VALUE_INFINITE)
|
||||
v = VALUE_ZERO;
|
||||
|
||||
bool tb = worker.tbConfig.rootInTB && std::abs(v) <= VALUE_TB;
|
||||
v = tb ? rootMoves[i].tbScore : v;
|
||||
bool isTBScore = worker.tbConfig.rootInTB && std::abs(v) <= VALUE_TB;
|
||||
v = isTBScore ? rootMoves[i].tbScore : v;
|
||||
|
||||
bool isExact = i != pvIdx || tb || !updated; // tablebase- and previous-scores are exact
|
||||
|
||||
// Potentially correct and extend the PV, and in exceptional cases v
|
||||
// Potentially correct and extend the PV, and in exceptional cases v.
|
||||
// Bound flags indicate an unreliable PV, also when we usePreviousScore.
|
||||
if (is_decisive(v) && std::abs(v) < VALUE_MATE_IN_MAX_PLY
|
||||
&& ((!rootMoves[i].scoreLowerbound && !rootMoves[i].scoreUpperbound) || isExact))
|
||||
&& ((!rootMoves[i].scoreLowerbound && !rootMoves[i].scoreUpperbound) || isTBScore))
|
||||
syzygy_extend_pv(worker.options, worker.limits, pos, rootMoves[i], v);
|
||||
|
||||
std::string pv;
|
||||
@@ -2191,7 +2190,8 @@ void SearchManager::pv(Search::Worker& worker,
|
||||
info.score = {v, pos};
|
||||
info.wdl = wdl;
|
||||
|
||||
if (!isExact)
|
||||
// TB and previous scores are exact, even though their bound flags may say otherwise.
|
||||
if (!(isTBScore || usePreviousScore))
|
||||
info.bound = bound;
|
||||
|
||||
TimePoint time = std::max(TimePoint(1), tm.elapsed_time());
|
||||
|
||||
Reference in New Issue
Block a user