From 6b7822119feffd0a27ae5b2a95d3570c9e046090 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" Date: Tue, 25 Jun 2024 01:57:35 +0300 Subject: [PATCH] Limit has_game_cycle() to only upcoming repetition use the original algorithm according to the paper http://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf, which detects accurately if a position has an upcoming repetition. The 'no progress' part of has_game_cycle has been removed, the function has been renamed to upcoming_repetition to reflect this. As a result of this fix, to the best of our knowledge, all PVs for completed iterations that yield a mate or decisive table base score now end in mate or contain a TB position, respectively. passed non-regression STC: https://tests.stockfishchess.org/tests/view/6679fa1d0c2db3fa2dcecbf2 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 63584 W: 16666 L: 16472 D: 30446 Ptnml(0-2): 186, 7552, 16146, 7698, 210 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/667ac965e439ed1c7a9ca042 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 464574 W: 117493 L: 117729 D: 229352 Ptnml(0-2): 311, 52468, 126974, 52214, 320 closes https://github.com/official-stockfish/Stockfish/pull/5432 bench: 1209805 --- src/position.cpp | 20 ++++++++++---------- src/position.h | 2 +- src/search.cpp | 10 ++++------ 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index b46ba029..d374b1c0 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1156,9 +1156,9 @@ bool Position::has_repeated() const { } -// Tests if the position has a move which draws by repetition, -// or an earlier position has a move that directly reaches the current position. -bool Position::has_game_cycle(int ply) const { +// Tests if the position has a move which draws by repetition. +// This function accurately matches the outcome of is_draw() over all legal moves. +bool Position::upcoming_repetition(int ply) const { int j; @@ -1169,10 +1169,16 @@ bool Position::has_game_cycle(int ply) const { Key originalKey = st->key; StateInfo* stp = st->previous; + Key other = originalKey ^ stp->key ^ Zobrist::side; for (int i = 3; i <= end; i += 2) { - stp = stp->previous->previous; + stp = stp->previous; + other ^= stp->key ^ stp->previous->key ^ Zobrist::side; + stp = stp->previous; + + if (other != 0) + continue; Key moveKey = originalKey ^ stp->key; if ((j = H1(moveKey), cuckoo[j] == moveKey) || (j = H2(moveKey), cuckoo[j] == moveKey)) @@ -1188,12 +1194,6 @@ bool Position::has_game_cycle(int ply) const { // For nodes before or at the root, check that the move is a // repetition rather than a move to the current position. - // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in - // the same location, so we have to select which square to check. - if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) - continue; - - // For repetitions before or at the root, require one more if (stp->repetition) return true; } diff --git a/src/position.h b/src/position.h index 154ed652..3cfb87d0 100644 --- a/src/position.h +++ b/src/position.h @@ -156,7 +156,7 @@ class Position { int game_ply() const; bool is_chess960() const; bool is_draw(int ply) const; - bool has_game_cycle(int ply) const; + bool upcoming_repetition(int ply) const; bool has_repeated() const; int rule50_count() const; Value non_pawn_material(Color c) const; diff --git a/src/search.cpp b/src/search.cpp index 188e81f4..6368acc6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -534,9 +534,8 @@ Value Search::Worker::search( // Limit the depth if extensions made it too large depth = std::min(depth, MAX_PLY - 1); - // Check if we have an upcoming move that draws by repetition, or - // if the opponent had an alternative move earlier to this position. - if (!rootNode && alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) + // Check if we have an upcoming move that draws by repetition. + if (!rootNode && alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply)) { alpha = value_draw(this->nodes); if (alpha >= beta) @@ -1447,9 +1446,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, assert(PvNode || (alpha == beta - 1)); assert(depth <= 0); - // Check if we have an upcoming move that draws by repetition, or if - // the opponent had an alternative move earlier to this position. (~1 Elo) - if (alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) + // Check if we have an upcoming move that draws by repetition. (~1 Elo) + if (alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply)) { alpha = value_draw(this->nodes); if (alpha >= beta)