Reduce use of lazyEval
authorJoost VandeVondele <Joost.VandeVondele@gmail.com>
Sun, 7 Nov 2021 10:01:03 +0000 (11:01 +0100)
committerStéphane Nicolet <cassio@free.fr>
Mon, 8 Nov 2021 12:03:52 +0000 (13:03 +0100)
In case the evaluation at root is large, discourage the use of lazyEval.

This fixes https://github.com/official-stockfish/Stockfish/issues/3772
or at least improves it significantly. In this case, poor play with large
odds can be observed, in extreme cases leading to a loss despite large
advantage:

r1bq1b1r/ppp3p1/3p1nkp/n3p3/2B1P2N/2NPB3/PPP2PPP/R3K2R b KQ - 5 9

With this patch the poor move is only considered up to depth 13, in master
up to depth 28.

The patch did not pass at LTC with Elo gainer bounds, but with slightly
positive Elo nevertheless (95% LOS).

STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.50>
Total: 40368 W: 10318 L: 10041 D: 20009
Ptnml(0-2): 103, 4493, 10725, 4750, 113
https://tests.stockfishchess.org/tests/view/61800ad259e71df00dcc420d

LTC:
LLR: -2.94 (-2.94,2.94) <0.50,3.00>
Total: 212288 W: 52997 L: 52692 D: 106599
Ptnml(0-2): 112, 22038, 61549, 22323, 122
https://tests.stockfishchess.org/tests/view/618050d959e71df00dcc426d

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

Bench: 7127040

src/evaluate.cpp
src/search.cpp
src/thread.h

index 2f1d5067d013ce2d0ffee3785aefe78910d689c1..a503b0c8339144d27572f4dd3ec72f5a19a184bc 100644 (file)
@@ -988,7 +988,9 @@ namespace {
 
     // Early exit if score is high
     auto lazy_skip = [&](Value lazyThreshold) {
-        return abs(mg_value(score) + eg_value(score)) > lazyThreshold + pos.non_pawn_material() / 32;
+        return abs(mg_value(score) + eg_value(score)) >   lazyThreshold
+                                                        + std::abs(pos.this_thread()->bestValue) * 5 / 4
+                                                        + pos.non_pawn_material() / 32;
     };
 
     if (lazy_skip(LazyThreshold1))
@@ -1126,6 +1128,7 @@ std::string Eval::trace(Position& pos) {
   std::memset(scores, 0, sizeof(scores));
 
   pos.this_thread()->trend = SCORE_ZERO; // Reset any dynamic contempt
+  pos.this_thread()->bestValue = VALUE_ZERO; // Reset bestValue for lazyEval
 
   v = Evaluation<TRACE>(pos).value();
 
index 090a79319f467797ad9dc5f62e43566cf2cb681a..11d1df326bccf1a63238db6a2e3c01021a81b527 100644 (file)
@@ -286,7 +286,7 @@ void Thread::search() {
   // The latter is needed for statScore and killer initialization.
   Stack stack[MAX_PLY+10], *ss = stack+7;
   Move  pv[MAX_PLY+1];
-  Value bestValue, alpha, beta, delta;
+  Value alpha, beta, delta;
   Move  lastBestMove = MOVE_NONE;
   Depth lastBestMoveDepth = 0;
   MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
index fae866c4f30d21657f1ae396f5277a0b83cf50ee..387937390c18b1381fccc2e18755b69f3f73c2c9 100644 (file)
@@ -64,6 +64,7 @@ public:
   uint64_t nodesLastExplosive;
   uint64_t nodesLastNormal;
   std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
+  Value bestValue;
   int selDepth, nmpMinPly;
   Color nmpColor;
   ExplosionState state;