fixes https://github.com/official-stockfish/Stockfish/issues/6328
Before calling search(depth), the patch ensures that depth is at least 1
whenever we encounter a decisive score in the transposition table (TT). This
prevents search(depth) from being executed by qsearch, which would otherwise
ignore that information. Typically, decisive TT hits occur when analyzing a
mating sequence backward. Due to the nature of Iterative Deepening (IID), such
scores are usually first found at depth 0. Without this patch, valuable
information can be lost because qsearch may overwrite the TT entry by replacing
the value with a static evaluation, even though the node was already processed
at a higher depth. This is also why the engine sometimes loses track of an
already discovered mate.
Using ..\sf\patch.exe on matetrack.epd with --nodes 1000000
Engine ID: Stockfish dev-20251015-nogit
Total FENs: 6554
Found mates: 3437
Best mates: 2438
Using ..\sf\master.exe on matetrack.epd with --nodes 1000000
Engine ID: Stockfish dev-20251015-nogit
Total FENs: 6554
Found mates: 3337
Best mates: 2407
Passed STC
https://tests.stockfishchess.org/tests/view/68fa3fa7637acd2a11e72a79
LLR: 3.55 (-2.94,2.94) <0.00,2.00>
Total: 134144 W: 34960 L: 34471 D: 64713
Ptnml(0-2): 376, 14199, 37459, 14636, 402
Failed LTC
https://tests.stockfishchess.org/tests/view/68ffc1b5637acd2a11e73377
LLR: -3.10 (-2.94,2.94) <0.50,2.50>
Total: 75360 W: 19423 L: 19519 D: 36418
Ptnml(0-2): 38, 7553, 22605, 7435, 49
closes https://github.com/official-stockfish/Stockfish/pull/6386
bench: 2530552
Change is functional because now we verify for stalemate also on captures and when not giving check.
Green STC test on stalemate-book
https://tests.stockfishchess.org/tests/view/682d878f6ec7634154f9ad2f
Elo: 2.29 ± 1.3 (95%) LOS: 100.0%
Total: 10000 W: 4637 L: 4571 D: 792
Ptnml(0-2): 2, 132, 4664, 202, 0
nElo: 12.42 ± 6.8 (95%) PairsRatio: 1.51
Green LTC test on stalemate-book
https://tests.stockfishchess.org/tests/view/682daa2d6ec7634154f9ad67
Elo: 0.80 ± 0.8 (95%) LOS: 96.9%
Total: 10000 W: 4727 L: 4704 D: 569
Ptnml(0-2): 0, 64, 4849, 87, 0
nElo: 6.51 ± 6.8 (95%) PairsRatio: 1.36
Passed non-regression test @ LTC
https://tests.stockfishchess.org/tests/view/682dd10d6ec7634154f9adb3
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 148512 W: 38135 L: 38046 D: 72331
Ptnml(0-2): 55, 15759, 42558, 15810, 74
N.B.: The unique concern I have, is that due changes in future a negative SEE
capture see might be returned in GOOD_CAPTURE stage. In this case the assert in
can_move_king_or_pawn() will trigger since we must guarantee that all moves
(also quiets) are generated in movepicker when calling can_move_king_or_pawn().
closes https://github.com/official-stockfish/Stockfish/pull/6088
bench: 2178135
Currently SF’s quiescence search like most alpha-beta based engines
doesn’t verify for stalemate because doing it each leaf position is to
expensive and costs elo. However in certain positions this creates a
blindspot for SF, not recognizing soon enough that the opponent can
reach a stalemate by sacrifycing his last mobile heavy piece(s). This
tactical motif & it’s measure are similar to zugzwang & verification
search: the measure itself does not gain elo, but prevents SF from
loosing/drawing games in an awkward way.
The fix consists of 3 measures:
1. Make qsearch verify for stalemate on transitions to pure KP-material
for the side to move with our last Rook/Queen just been captured. In
fact this is the scenario where stalemate happens with highest
frequency. The stalemate-verification itself is optimized by merely
checking for pawn pushes & king mobility (captures were already
tried by qssearch)
2. Another culprit for the issue figured out to be SEE based pruning for
checks in step 14. Here often the move forcing the stalemate (or
forcing the opponent to not retake) get pruned away and it need to
much time to reach enough depth. To encounter this we verify
following conditions:
- a) side to move is happy with a draw (alpha < 0)
- b) we are about to sacrify our last heavy & unique mobile piece in
this position.
- c) this piece doesn’t move away from our kingring giving the king a
new square to move.
When all 3 conditions meet we don’t prune the move, because there
is a good chance that capturing the piece means stalemate.
3. Store terminal nodes (mates & stalemates) in TT with higher depth
than searched depth. This prevents SF from:
- reanalyzing the node (=trying to generate legal moves) in vain at
each iterative deepening step.
- overwriting an already correct draw-evaluation from a previous shallow
normal search by a qsearch which doesn’t recognize stalemate and might
store a verry erratic evaluation.
This is due to the 4 constant in the TT-overwrite condition: d -
DEPTH_ENTRY_OFFSET + 2 * pv > depth8 – 4 which allows qs to override
entries made by normal searches with depth <=4.
This 3hrd measure however is not essential for fixing the issue, but
tests (one of vdv & one of mine) seem to suggest that this measure
brings some small benefit.
Another other position where SF benefits from this fix is for instance
Position FEN 8/8/8/1B6/6p1/8/3K1Ppp/3N2kr w - - 0 1 bm f4 +M9
P.S.: Also this issue higly depends on the used net, how good the net is
at evaluate such mobility restricted positions. SF16 was pretty good in
solving 2rr4/5pBk/PqP3p1/1N3pPp/1PQ1bP1P/8/3R4/R4K2 b - - 0 40 bm Rxc6
(< 1 second) while SF16_1 with introduction of the dual net needs about
1,5 minutes and SF17.1 requires 3 minutes to find the drawing move Rxc6.
P.S.2: Using more threads produces indeterminism & using high hash
pressure makes SF reevaluate explored positions more often which makes
it more likely to solve the position. To have stable meaningful results
I tested therfore with one single thread and low hash pressure.
Preliminary LTC test at 30k games
https://tests.stockfishchess.org/tests/view/67ece7a931d7cf8afdc44e18 Elo: 0.04 ± 2.0 (95%) LOS: 51.7%
Total: 24416 W: 6226 L: 6223 D: 11967
Ptnml(0-2): 12, 2497, 7185, 2504, 10
nElo: 0.09 ± 4.4 (95%) PairsRatio: 1.00
Passed LTC no-regression sprt
https://tests.stockfishchess.org/tests/view/67ee8e4631d7cf8afdc452fb
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 401556 W: 101612 L: 101776 D: 198168
Ptnml(0-2): 152, 42241, 116170, 42049, 166
closes https://github.com/official-stockfish/Stockfish/pull/5983
fixes https://github.com/official-stockfish/Stockfish/issues/5899
Bench: 1721673
This condition can never be true, it's superfluous.
It never triggers even with a bench 16 1 20 run.
To met the condition it would imply that the previous recapture was done
by a higher rated piece than a Queen.
This is only the case when the King recaptures and that's already
handled in line 1161: (return (attackers & ~pieces(stm)) ? res ^ 1).
closes https://github.com/official-stockfish/Stockfish/pull/5839
No functional change
Since it's introduction at probcut step the nnue network has changed
substantially and now it no longer seems useful.
Passed non-regression test at STC
https://tests.stockfishchess.org/tests/view/675fe27986d5ee47d9542d86
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 118656 W: 30732 L: 30609 D: 57315
Ptnml(0-2): 319, 12767, 33049, 12858, 335
N.B.: It may be useful to reintroduce it here at probcut if we know that
a node that was cut away previously now has to be explored. Exploring
new ground will deliver no tt-hits so in this case the hint for common
parent position might be useful.
No functional change
Current condition is convoluted and hard to understand because of
several negations. Also added 2 comments to make the concept behind the
condition better understandable.
closes https://github.com/official-stockfish/Stockfish/pull/5685
No functional change
fixes the lowerbound/upperbound output by avoiding
scores outside the alpha,beta bracket. Since SF search
uses fail-soft we can't simply take the returned value
as score.
closes https://github.com/official-stockfish/Stockfish/pull/4259
No functional change
fixes the lowerbound/upperbound output by taking the alpha,beta bracket
into account also if a bestThread is selected that is different from the master thread.
Instead of keeping track which bounds where used in the specific search,
in this version we simply store the quality (exact, upperbound,
lowerbound) of the score along with the actual score as information on
rootMove.
closes https://github.com/official-stockfish/Stockfish/pull/4239
No functional change
Currently on a normal bench run in ~0,7% of cases 'improving' is set to
true although the static eval isn't improving at all, just keeping
equal. It looks like the strict gt-operator is more appropriate here,
since it returns to 'improving' its literal meaning without sideffects.
STC {-1.00,3.00} failed yellow:
https://tests.stockfishchess.org/tests/view/5e1ec38c8fd5f550e4ae1c28
LLR: -2.93 (-2.94,2.94) {-1.00,3.00}
Total: 53155 W: 10170 L: 10109 D: 32876
Ptnml(0-2): 863, 6282, 12251, 6283, 892
non-regression LTC passed:
https://tests.stockfishchess.org/tests/view/5e1f1c0d8fd5f550e4ae1c41
LLR: 2.98 (-2.94,2.94) {-1.50,0.50}
Total: 23961 W: 3114 L: 3018 D: 17829
Ptnml(0-2): 163, 2220, 7114, 2298, 170
CLoses https://github.com/official-stockfish/Stockfish/pull/2496
bench: 4561386
This helps resolving consecutive FH's during aspiration more efficiently
STC:
http://tests.stockfishchess.org/tests/view/5bc857920ebc592439f85765
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 4992 W: 1134 L: 980 D: 2878 Elo +10.72
LTC:
http://tests.stockfishchess.org/tests/view/5bc868050ebc592439f857ef
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 8123 W: 1363 L: 1210 D: 5550 Elo +6.54
No-Regression test with 8 threads, tc=15+0.15:
http://tests.stockfishchess.org/tests/view/5bc874ca0ebc592439f85938
LLR: 2.94 (-2.94,2.94) [-3.00,1.00]
Total: 24740 W: 3977 L: 3863 D: 16900 Elo +1.60
This was a cooperation between me and Michael Stembera:
-me recognizing SF having problems with resolving FH's efficiently at
high depths, thus starting some tests based on consecutive FH's.
-mstembera picking up the idea with first success at STC & LTC (so full
credits to him!)
-me suggesting how to resolve the issues pinpointed by S.G on PR #1768
and finally restricting the logic to the main thread so that it don't
regresses at multi-thread.
bench: 3314347
Currently we update (track up) the pv even in the fail high case.
However most times in such cases the pv in the ply below remains unset
because there we have value == alpha and so finally we see truncated
pv's (=just one move) in fail high cases.
Of course tracking down these pv's (+sending them to the gui) comes at a
certian cost, but no-regression tests passed:
STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 16300 W: 3556 L: 3424 D: 9320
http://tests.stockfishchess.org/tests/view/5b9b73500ebc592cf275ea92
LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 202411 W: 32734 L: 32897 D: 136780
http://tests.stockfishchess.org/tests/view/5b9baed10ebc592cf275ef6d
N.B.: Digging also into qsearch was tried in another version but seemed
not to pass the tests. This means that we don't always will get a pv
until the very tips.
No functional change
The '- 1' subtrahend was introduced for guarding against null move
search at root, which would be nonsense. But this is actually already
guaranteed by the !PvNode condition. This followed from the discussion
in pull request 1609: https://github.com/official-stockfish/Stockfish/pull/1609
No functional change
1. avoid recursive call of verification.
For the interested side to move recursion makes no sense.
For the other side it could make sense in case of mutual zugzwang,
but I was not able to figure out any concrete problematic position.
Allows the removal of 2 local variables.
2. avoid further reduction by removing R += ONE_PLY;
Benchmark with zugzwang-suite (see #1338), max 45 secs per position:
Patch solves 33 out of 37
Master solves 31 out of 37
STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 76188 W: 13866 L: 13840 D: 48482
http://tests.stockfishchess.org/tests/view/5a5612ed0ebc590297da516c
LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 40479 W: 5247 L: 5152 D: 30080
http://tests.stockfishchess.org/tests/view/5a56f7d30ebc590299e4550e
bench: 5340015
Results for 20 tests for each version (pgo-builds):
Base Test Diff
Mean 2110519 2118116 -7597
StDev 8727 4906 10112
p-value: 0,774
speedup: 0,004
Further verified for no regression:
http://tests.stockfishchess.org/tests/view/5885abd10ebc5915193f79e6
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 21786 W: 3959 L: 3840 D: 13987
No functional change
Correct pinners calculation and fix bug with pinned
pieces giving check. With this patch 'pinners' only
returns sliders with exactly one defensive piece between
the slider and the attacked square (in other words, pinners
returns exact pinners).
This was a co-operation between Marco Costalba,
Stéphane Nicolet and me.
Special thanks to Ronald de Man for reporting the bug with
pinned pieces giving check, discussed here:
https://groups.google.com/forum/?fromgroups=#!topic/fishcooking/S_4E_Xs5HaE
STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 132118 W: 23578 L: 23645 D: 84895
LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 36424 W: 4770 L: 4670 D: 26984
bench: 6272231
This is the most compact and neatest version
is was able to produce.
On normal builds I have a small slowdown:
normal builds base vs. simplification (gcc 4.8.1 Win7-64 i7-3770 @ 3.4GHz x86-64-modern)
Results for 20 tests for each version:
Base Test Diff
Mean 1974744 1969333 5411
StDev 11825 10281 5874
p-value: 0,178
speedup: -0,003
On pgo-builds however I measure a nice 1.1% speedup
pgo-builds base vs. simplification
Results for 20 tests for each version:
Base Test Diff
Mean 1974119 1995444 -21325
StDev 8703 5717 4623
p-value: 1
speedup: 0,011
No functional change.
Don't allow pinned pieces to attack the exchange-square as long all
pinners (this includes also potential ones) are on their original
square.
As soon a pinner moves to the exchange-square or get captured on it, we
fall back to standard SEE behaviour.
This correctly handles the majority of cases with absolute pins.
bench: 6883133