From 32a655e60e6fa1e5602f2b0bec9b4c5b39daee97 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:17:55 +0300 Subject: [PATCH] Fix issue #5023 --- src/search.cpp | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6a464961a61..f93a4ce8ea7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -105,6 +105,7 @@ struct Skill { Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); +bool possibleFortress(Stack* ss); void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_quiet_stats( @@ -1184,7 +1185,14 @@ Value Search::Worker::search( newDepth += doDeeperSearch - doShallowerSearch; if (newDepth > d) + { + if (possibleFortress(ss)) + { + newDepth = 2 * newDepth / 3; + assert(newDepth >= 1); + } value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); + } // Post LMR continuation history updates (~1 Elo) int bonus = value <= alpha ? -stat_malus(newDepth) @@ -1202,8 +1210,12 @@ Value Search::Worker::search( if (!ttMove) r += 2; + if (newDepth > 1 && possibleFortress(ss)) + newDepth = 2 * newDepth / 3; + // Note that if expected reduction is high, we reduce search depth by 1 here (~9 Elo) - value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3), !cutNode); + value = -search(pos, ss + 1, -(alpha + 1), -alpha, + newDepth - (r > 3 && !possibleFortress(ss)), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail high, @@ -1373,7 +1385,6 @@ Value Search::Worker::search( return bestValue; } - // Quiescence search function, which is called by the main search // function with zero depth, or recursively with further decreasing depth per call. // (~155 Elo) @@ -1812,6 +1823,24 @@ void update_quiet_stats( } } +// Detects a possible fortress by checking weither any player can keep repeating moves +// this fixes a fortress search explosion in case staticEval of the fortress is way off +bool possibleFortress(Stack* ss) { + const bool fortressUs = + (ss - 4)->currentMove.is_ok() + && (ss - 6)->currentMove + == Move((ss - 4)->currentMove.to_sq(), (ss - 4)->currentMove.from_sq()) + && (ss - 6)->currentMove == (ss - 2)->currentMove; + + const bool fortressThem = + (ss - 5)->currentMove.is_ok() + && (ss - 7)->currentMove + == Move((ss - 5)->currentMove.to_sq(), (ss - 5)->currentMove.from_sq()) + && (ss - 7)->currentMove == (ss - 3)->currentMove; + + return fortressUs || fortressThem; +} + // When playing with strength handicap, choose the best move among a set of RootMoves // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. Move Skill::pick_best(const RootMoves& rootMoves, size_t multiPV) {