Help aborted searches remember mate scores

This patch improves mate finding results by protecting mate scores from being forgotten in a later iteration. This applies especially to aborted searches, but also to completed iterations in e.g. `go depth` scenarios. The only visible change to the user is the final UCI info line.

The case of TB wins/losses was excluded from this patch on purpose. The reason is that in some corner cases related to rounding in some DTZ Syzygy EGTBs, later searches may turn a TB loss into a draw, for example. See #5175 and the example
```
info depth 5 seldepth 5 multipv 1 score cp -20000 nodes 122 nps 8714 hashfull 0 tbhits 26 time 14 pv h1b1 a7b6 f6e5 b6b1 e5f4 c8c3 f4g5 d7h7 g5f4 b1f5 f4f5 c3e5 f5g6 e5f4 g6h7 f4g5 h7h8 b8c7 h8h7 c7d6 h7h8 d6e5 h8h7 e5f6 h7h8 g5g7
info depth 6 seldepth 9 multipv 1 score cp 0 nodes 255 nps 18214 hashfull 0 tbhits 26 time 14 pv h1h2 d7d6 f6g5 a7e7
```
from #5414.

Matetrack for patch:
```
Using ./stockfish on matetrack.epd with --nodes 1000000
Engine ID:     Stockfish dev-20260510-dbbc3ed1
Total FENs:    6554
Found mates:   3054
Best mates:    2224
```
This is slightly more/equal compared to master (3040 and 2224), see https://github.com/vondele/matetrack/blob/faf7cf3be95ab7dfdcde6e035de28a30887cd8a5/matetrack1000000.csv#L4368.

Passed STC non-reg:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 108224 W: 27565 L: 27429 D: 53230
Ptnml(0-2): 212, 11755, 30064, 11847, 234
https://tests.stockfishchess.org/tests/view/6a00899e9392f0c31721406c

closes https://github.com/official-stockfish/Stockfish/pull/6816

No functional change
This commit is contained in:
Robert Nurnberg @ elitebook
2026-05-17 20:44:44 +02:00
committed by Joost VandeVondele
parent ffffe1bdb5
commit 1cebdafc14
+21 -5
View File
@@ -273,6 +273,7 @@ bool Search::Worker::iterative_deepening() {
Value alpha, beta;
Value bestValue = -VALUE_INFINITE;
Value lastIterationScore = -VALUE_INFINITE;
Color us = rootPos.side_to_move();
double timeReduction = 1, totBestMoveChanges = 0;
int delta, iterIdx = 0;
@@ -461,33 +462,48 @@ bool Search::Worker::iterative_deepening() {
break;
}
const bool forgottenMate = lastIterationScore != -VALUE_INFINITE
&& is_mate_or_mated(lastIterationScore)
&& (std::abs(rootMoves[0].score) < std::abs(lastIterationScore)
|| rootMoves[0].score_is_bound());
if (!threads.stop)
{
if (lastIterationPV.empty() || rootMoves[0].pv[0] != lastIterationPV[0])
lastBestMoveDepth = rootDepth;
// Do not replace (shorter) mate scores from a previous iteration.
if (!forgottenMate)
{
lastIterationPV = rootMoves[0].pv;
lastIterationScore = rootMoves[0].score;
}
}
const bool abortedLossSearch =
threads.stop && !pvIdx && rootMoves[0].score != -VALUE_INFINITE
&& is_loss(rootMoves[0].score) && !rootMoves[0].score_is_bound();
// 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)
&& !rootMoves[0].score_is_bound())
// We do the same if a search has failed to recover a mate score that was found
// in a previous iteration.
if (abortedLossSearch || (rootMoves[0].score != -VALUE_INFINITE && forgottenMate))
{
// Bring the last best move to the front for best thread selection.
if (!lastIterationPV.empty())
{
Utility::move_to_front(rootMoves, [&lastPV = std::as_const(lastIterationPV)](
const auto& rm) { return rm == lastPV[0]; });
rootMoves[0].pv = lastIterationPV;
rootMoves[0].score = rootMoves[0].uciScore = rootMoves[0].previousScore;
rootMoves[0].score = rootMoves[0].uciScore = lastIterationScore;
rootMoves[0].unset_bound_flags();
if (mainThread)
uciPvSent = false;
}
// For an aborted d1 search we label the loss score as a lower bound.
else
else if (abortedLossSearch)
rootMoves[0].scoreLowerbound = true;
}