From da82942b541b2a6512189a9bad2284c6f45f3c44 Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:24:43 +0100 Subject: [PATCH 01/10] Add functions to check for decisive scores Thanks to peregrineshahin and robbyrobbyrob for their suggestions. closes https://github.com/official-stockfish/Stockfish/pull/5696 No functional change --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 2e904f40d..ea43017e5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1042,7 +1042,9 @@ moves_loop: // When in check, search starts here { if (bestValue <= futilityValue && !is_decisive(bestValue) && !is_win(futilityValue)) - bestValue = futilityValue; + if (bestValue <= futilityValue && !is_decisive(bestValue) + && !is_win(futilityValue)) + bestValue = futilityValue; continue; } From d5a36a3c92533782d6a74d16c080de0c1538f65d Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 23 Nov 2024 14:37:08 +0300 Subject: [PATCH 02/10] Simplify probCutBeta formula After recent changes to the improving definition, seems like there is no need anymore to keep opponentWorsening in the probCutBeta formula. Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 214272 W: 55566 L: 55541 D: 103165 Ptnml(0-2): 620, 25540, 54817, 25513, 646 https://tests.stockfishchess.org/tests/view/6735243d86d5ee47d953eaea Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 126708 W: 32329 L: 32216 D: 62163 Ptnml(0-2): 68, 13986, 35123, 14119, 58 https://tests.stockfishchess.org/tests/view/67393cf686d5ee47d953ef99 closes https://github.com/official-stockfish/Stockfish/pull/5697 Bench: 983067 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ea43017e5..5209bd073 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -855,7 +855,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search // returns a value much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 187 - 53 * improving - 27 * opponentWorsening; + probCutBeta = beta + 187 - 56 * improving; if (!PvNode && depth > 3 && !is_decisive(beta) // If value from transposition table is lower than probCutBeta, don't attempt From 713000c517c63e6926bdbe1071e647280bc3da32 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Sun, 24 Nov 2024 16:05:04 +0100 Subject: [PATCH 03/10] Same weight for black and white nonPawnCorrection history Since we don't have color dependent parameters in NNUE eval, it also has no sense IMO to have color dependent parameters in correction histories. Ideally a fixed depth search on a single thread should be determistic, so delivering the same result (move) if we just flip colors on the board. Patch replaces 2 parameters (122 and 185) with just one value 154 (= the avg of the two). Passed STC-non regression https://tests.stockfishchess.org/tests/view/6740a63286d5ee47d953f656 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 122336 W: 31499 L: 31372 D: 59465 Ptnml(0-2): 336, 14535, 31301, 14658, 338 Passed LTC-non regression https://tests.stockfishchess.org/tests/view/67419bae86d5ee47d953f7b6 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 101400 W: 25870 L: 25731 D: 49799 Ptnml(0-2): 78, 11109, 28166, 11290, 57 closes https://github.com/official-stockfish/Stockfish/pull/5698 Bench: 1215483 --- src/search.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5209bd073..8ebbef5b1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1434,7 +1434,8 @@ moves_loop: // When in check, search starts here && ((bestValue < ss->staticEval && bestValue < beta) // negative correction & no fail high || (bestValue > ss->staticEval && bestMove))) // positive correction & no fail low { - const auto m = (ss - 1)->currentMove; + const auto m = (ss - 1)->currentMove; + static const int nonPawnWeight = 154; auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); @@ -1443,9 +1444,9 @@ moves_loop: // When in check, search starts here thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 162 / 128; thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 148 / 128; thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)] - << bonus * 122 / 128; + << bonus * nonPawnWeight / 128; thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)] - << bonus * 185 / 128; + << bonus * nonPawnWeight / 128; if (m.is_ok()) (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] << bonus; From 1f9404434dcfd1013e20266a79dfed5d0271294a Mon Sep 17 00:00:00 2001 From: Carlos Esparza Date: Sun, 24 Nov 2024 16:17:42 -0800 Subject: [PATCH 04/10] Simplify picking of evasion moves Sort evasions before we start returning them in next_move() (just like every other kind of move) instead of looking for the biggest element on every call to next_move(). The bench number changes because the old method is not equivalent to a stable sort. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 132064 W: 34318 L: 34204 D: 63542 Ptnml(0-2): 392, 15522, 34106, 15604, 408 https://tests.stockfishchess.org/tests/view/6743fee086d5ee47d953f9ca Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 97542 W: 24899 L: 24757 D: 47886 Ptnml(0-2): 63, 10646, 27193, 10824, 45 https://tests.stockfishchess.org/tests/view/674509cd86d5ee47d953fb96 closes https://github.com/official-stockfish/Stockfish/pull/5700 Bench: 1094825 --- AUTHORS | 1 + src/movepick.cpp | 29 ++++++++++------------------- src/movepick.h | 7 +------ 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/AUTHORS b/AUTHORS index 31a64c17e..ddc53ec02 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,6 +45,7 @@ Bruno de Melo Costa (BM123499) Bruno Pellanda (pellanda) Bryan Cross (crossbr) candirufish +Carlos Esparza Sánchez (ces42) Chess13234 Chris Cain (ceebo) Ciekce diff --git a/src/movepick.cpp b/src/movepick.cpp index df722eceb..96f031717 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -18,11 +18,9 @@ #include "movepick.h" -#include #include #include #include -#include #include "bitboard.h" #include "position.h" @@ -199,19 +197,13 @@ void MovePicker::score() { // Returns the next move satisfying a predicate function. // This never returns the TT move, as it was emitted before. -template +template Move MovePicker::select(Pred filter) { - while (cur < endMoves) - { - if constexpr (T == Best) - std::swap(*cur, *std::max_element(cur, endMoves)); - + for (; cur < endMoves; ++cur) if (*cur != ttMove && filter()) return *cur++; - cur++; - } return Move::none(); } @@ -245,7 +237,7 @@ top: goto top; case GOOD_CAPTURE : - if (select([&]() { + if (select([&]() { // Move losing capture to endBadCaptures to be tried later return pos.see_ge(*cur, -cur->value / 18) ? true : (*endBadCaptures++ = *cur, false); @@ -269,7 +261,7 @@ top: [[fallthrough]]; case GOOD_QUIET : - if (!skipQuiets && select([]() { return true; })) + if (!skipQuiets && select([]() { return true; })) { if ((cur - 1)->value > -7998 || (cur - 1)->value <= quiet_threshold(depth)) return *(cur - 1); @@ -286,7 +278,7 @@ top: [[fallthrough]]; case BAD_CAPTURE : - if (select([]() { return true; })) + if (select([]() { return true; })) return *(cur - 1); // Prepare the pointers to loop over the bad quiets @@ -298,7 +290,7 @@ top: case BAD_QUIET : if (!skipQuiets) - return select([]() { return true; }); + return select([]() { return true; }); return Move::none(); @@ -307,17 +299,16 @@ top: endMoves = generate(pos, cur); score(); + partial_insertion_sort(cur, endMoves, std::numeric_limits::min()); ++stage; [[fallthrough]]; case EVASION : - return select([]() { return true; }); + case QCAPTURE : + return select([]() { return true; }); case PROBCUT : - return select([&]() { return pos.see_ge(*cur, threshold); }); - - case QCAPTURE : - return select([]() { return true; }); + return select([&]() { return pos.see_ge(*cur, threshold); }); } assert(false); diff --git a/src/movepick.h b/src/movepick.h index 0278b70ec..ab4e832fd 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -35,11 +35,6 @@ class Position; // a cut-off first. class MovePicker { - enum PickType { - Next, - Best - }; - public: MovePicker(const MovePicker&) = delete; MovePicker& operator=(const MovePicker&) = delete; @@ -57,7 +52,7 @@ class MovePicker { void skip_quiet_moves(); private: - template + template Move select(Pred); template void score(); From 6a8478c6adaf9fda6b885ea74e510910f5618c41 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 22 Nov 2024 17:13:00 -0800 Subject: [PATCH 05/10] Simplify Prior Capture Countermove Bonus Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 184032 W: 47626 L: 47568 D: 88838 Ptnml(0-2): 590, 21808, 47238, 21714, 666 https://tests.stockfishchess.org/tests/view/67412c7686d5ee47d953f743 Passed Non-regression LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 169218 W: 43395 L: 43323 D: 82500 Ptnml(0-2): 302, 18567, 46791, 18655, 294 https://tests.stockfishchess.org/tests/view/6743b7e086d5ee47d953f9a6 closes https://github.com/official-stockfish/Stockfish/pull/5701 Bench: 1130692 --- src/search.cpp | 12 +++++------- src/search.h | 3 +-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8ebbef5b1..149222fc7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -888,8 +888,7 @@ Value Search::Worker::search( // Prefetch the TT entry for the resulting position prefetch(tt.first_entry(pos.key_after(move))); - ss->currentMove = move; - ss->capturedPiece = captured; + ss->currentMove = move; ss->continuationHistory = &this->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()]; ss->continuationCorrectionHistory = @@ -1138,8 +1137,7 @@ moves_loop: // When in check, search starts here prefetch(tt.first_entry(pos.key_after(move))); // Update the current move (this must be done after singular extension search) - ss->currentMove = move; - ss->capturedPiece = pos.piece_on(move.to_sq()); + ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; ss->continuationCorrectionHistory = @@ -1403,7 +1401,8 @@ moves_loop: // When in check, search starts here else if (priorCapture && prevSq != SQ_NONE) { // bonus for prior countermoves that caused the fail low - Piece capturedPiece = (ss - 1)->capturedPiece; + Piece capturedPiece = pos.captured_piece(); + assert(capturedPiece != NO_PIECE); thisThread->captureHistory[pos.piece_on(prevSq)][prevSq][type_of(capturedPiece)] << stat_bonus(depth) * 2; } @@ -1653,8 +1652,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) prefetch(tt.first_entry(pos.key_after(move))); // Update the current move - ss->currentMove = move; - ss->capturedPiece = pos.piece_on(move.to_sq()); + ss->currentMove = move; ss->continuationHistory = &thisThread ->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()]; diff --git a/src/search.h b/src/search.h index 7868f6078..b618855b9 100644 --- a/src/search.h +++ b/src/search.h @@ -66,7 +66,6 @@ struct Stack { CorrectionHistory* continuationCorrectionHistory; int ply; Move currentMove; - Piece capturedPiece; Move excludedMove; Value staticEval; int statScore; @@ -357,4 +356,4 @@ class Worker { } // namespace Stockfish -#endif // #ifndef SEARCH_H_INCLUDED \ No newline at end of file +#endif // #ifndef SEARCH_H_INCLUDED From e8d2ba194a563b8c8dc1b9ae603b6b9a45c93567 Mon Sep 17 00:00:00 2001 From: xu-shawn <50402888+xu-shawn@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:17:58 -0800 Subject: [PATCH 06/10] Add Leela Data Attribution closes https://github.com/official-stockfish/Stockfish/pull/5705 No functional change --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 25da319d5..621f1d130 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,11 @@ where the source code can be found) to generate the exact binary you are distributing. If you make any changes to the source code, these changes must also be made available under GPL v3. +## Acknowledgements + +Stockfish uses neural networks trained on [data provided by the Leela Chess Zero +project][lc0-data-link], which is made available under the [Open Database License][odbl-link] (ODbL). + [authors-link]: https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS [build-link]: https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml @@ -144,6 +149,8 @@ also be made available under GPL v3. [wiki-uci-link]: https://github.com/official-stockfish/Stockfish/wiki/UCI-&-Commands [wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage [worker-link]: https://github.com/official-stockfish/fishtest/wiki/Running-the-worker +[lc0-data-link]: https://storage.lczero.org/files/training_data +[odbl-link]: https://opendatacommons.org/licenses/odbl/odbl-10.txt [build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github [commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge From afaf3a0f2a06918e4c046e27743cbe71befb3216 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 3 Dec 2024 09:18:27 +0300 Subject: [PATCH 07/10] Refine statscore for captures Continuation of previous attempts there. Now instead of using capture history with a static offset also add the value of the captured piece in the same way at it is used in movepicker. Passed STC: https://tests.stockfishchess.org/tests/view/674aa3d386d5ee47d95404aa LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 116480 W: 30433 L: 29999 D: 56048 Ptnml(0-2): 361, 13720, 29662, 14118, 379 Passed LTC: https://tests.stockfishchess.org/tests/view/674c4b2d86d5ee47d954073f LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 133542 W: 34365 L: 33847 D: 65330 Ptnml(0-2): 78, 14585, 36934, 15089, 85 closes https://github.com/official-stockfish/Stockfish/pull/5706 Bench: 934447 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 149222fc7..3ce30b813 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1180,7 +1180,10 @@ moves_loop: // When in check, search starts here r -= 1879; if (capture) - ss->statScore = 0; + ss->statScore = + 7 * int(PieceValue[pos.captured_piece()]) + + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())] + - 5000; else ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] From a8b6bf1b1a978775ad15ae677d8d425ccd05304b Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 7 Dec 2024 15:28:02 -0800 Subject: [PATCH 08/10] Small Major/Minor piece key simplification/optimization. closes https://github.com/official-stockfish/Stockfish/pull/5710 No functional change --- src/position.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index bab7a1fca..1b1c0269f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -360,7 +360,7 @@ void Position::set_state() const { { st->nonPawnMaterial[color_of(pc)] += PieceValue[pc]; - if (type_of(pc) == QUEEN || type_of(pc) == ROOK) + if (type_of(pc) >= ROOK) st->majorPieceKey ^= Zobrist::psq[pc][s]; else @@ -759,7 +759,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->nonPawnMaterial[them] -= PieceValue[captured]; st->nonPawnKey[them] ^= Zobrist::psq[captured][capsq]; - if (type_of(captured) == QUEEN || type_of(captured) == ROOK) + if (type_of(captured) >= ROOK) st->majorPieceKey ^= Zobrist::psq[captured][capsq]; else @@ -844,7 +844,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion] - 1] ^ Zobrist::psq[pc][pieceCount[pc]]; - if (promotionType == QUEEN || promotionType == ROOK) + if (promotionType >= ROOK) st->majorPieceKey ^= Zobrist::psq[promotion][to]; else @@ -871,7 +871,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; } - else if (type_of(pc) == QUEEN || type_of(pc) == ROOK) + else if (type_of(pc) >= ROOK) st->majorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; else From cf10644d6e2592e663e48b3d41dae07e7294166e Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Sun, 8 Dec 2024 22:24:29 +0100 Subject: [PATCH 09/10] Fix duplicate code (#5711) closes https://github.com/official-stockfish/Stockfish/pull/5711 No functional change --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3ce30b813..e352c96e3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1041,9 +1041,7 @@ moves_loop: // When in check, search starts here { if (bestValue <= futilityValue && !is_decisive(bestValue) && !is_win(futilityValue)) - if (bestValue <= futilityValue && !is_decisive(bestValue) - && !is_win(futilityValue)) - bestValue = futilityValue; + bestValue = futilityValue; continue; } From b822fdf2f2f00758c794cb61a25a044424d2bc0a Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 24 Nov 2024 16:29:20 -0800 Subject: [PATCH 10/10] Tune histories Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 268736 W: 70080 L: 69421 D: 129235 Ptnml(0-2): 831, 31795, 68460, 32448, 834 https://tests.stockfishchess.org/tests/view/6750778886d5ee47d9540e7c Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 505356 W: 129145 L: 127868 D: 248343 Ptnml(0-2): 307, 54901, 140959, 56230, 281 https://tests.stockfishchess.org/tests/view/675367de86d5ee47d9541536 closes https://github.com/official-stockfish/Stockfish/pull/5712 Bench: 1148169 --- src/history.h | 2 +- src/search.cpp | 78 ++++++++++++++++++++++++++------------------------ src/search.h | 5 ++++ 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/history.h b/src/history.h index 8d14a7a7c..17ab3481c 100644 --- a/src/history.h +++ b/src/history.h @@ -138,7 +138,7 @@ using LowPlyHistory = Stats; // PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] -using PieceToHistory = Stats; +using PieceToHistory = Stats; // ContinuationHistory is the combined history of a given pair of moves, usually // the current one given a previous one. The nested history table is based on diff --git a/src/search.cpp b/src/search.cpp index e352c96e3..9a0a9d046 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -275,7 +275,7 @@ void Search::Worker::iterative_deepening() { int searchAgainCounter = 0; - lowPlyHistory.fill(0); + lowPlyHistory.fill(106); // Iterative deepening loop until requested to stop or the target depth is reached while (++rootDepth < MAX_PLY && !threads.stop @@ -500,10 +500,10 @@ void Search::Worker::iterative_deepening() { // Reset histories, usually before a new game void Search::Worker::clear() { - mainHistory.fill(0); - lowPlyHistory.fill(0); - captureHistory.fill(-758); - pawnHistory.fill(-1158); + mainHistory.fill(61); + lowPlyHistory.fill(106); + captureHistory.fill(-598); + pawnHistory.fill(-1181); pawnCorrectionHistory.fill(0); majorPieceCorrectionHistory.fill(0); minorPieceCorrectionHistory.fill(0); @@ -518,7 +518,7 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-645); + h->fill(-427); for (size_t i = 1; i < reductions.size(); ++i) reductions[i] = int((19.43 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); @@ -538,7 +538,7 @@ Value Search::Worker::search( // Dive into quiescence search when the depth reaches zero if (depth <= 0) - return qsearch < PvNode ? PV : NonPV > (pos, ss, alpha, beta); + return qsearch(pos, ss, alpha, beta); // Limit the depth if extensions made it too large depth = std::min(depth, MAX_PLY - 1); @@ -644,13 +644,13 @@ Value Search::Worker::search( { // Bonus for a quiet ttMove that fails high (~2 Elo) if (!ttCapture) - update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth)); + update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth) * 747 / 1024); // Extra penalty for early quiet moves of // the previous ply (~1 Elo on STC, ~2 Elo on LTC) if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 2 && !priorCapture) update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - -stat_malus(depth + 1)); + -stat_malus(depth + 1) * 1091 / 1024); } // Partial workaround for the graph history interaction problem @@ -762,10 +762,10 @@ Value Search::Worker::search( if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1831, 1428) + 623; - thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; + thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1340 / 1024; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] - << bonus; + << bonus * 1159 / 1024; } // Set up the improving flag, which is true if current static evaluation is @@ -909,7 +909,7 @@ Value Search::Worker::search( if (value >= probCutBeta) { - thisThread->captureHistory[movedPiece][move.to_sq()][type_of(captured)] << 1300; + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(captured)] << 1226; // Save ProbCut data into transposition table ttWriter.write(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, @@ -1216,8 +1216,8 @@ moves_loop: // When in check, search starts here value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); // Post LMR continuation history updates (~1 Elo) - int bonus = 2 * (value >= beta) * stat_bonus(newDepth); - update_continuation_histories(ss, movedPiece, move.to_sq(), bonus); + int bonus = (value >= beta) * stat_bonus(newDepth); + update_continuation_histories(ss, movedPiece, move.to_sq(), bonus * 1427 / 1024); } } @@ -1379,24 +1379,25 @@ moves_loop: // When in check, search starts here // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (117 * (depth > 5) + 39 * !allNode + 168 * ((ss - 1)->moveCount > 8) - + 115 * (!ss->inCheck && bestValue <= ss->staticEval - 108) - + 119 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 83)); + int bonusScale = (117 * (depth > 5) + 39 * !allNode + 168 * ((ss - 1)->moveCount > 8) + + 115 * (!ss->inCheck && bestValue <= ss->staticEval - 108) + + 119 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 83)); // Proportional to "how much damage we have to undo" - bonus += std::min(-(ss - 1)->statScore / 113, 300); + bonusScale += std::min(-(ss - 1)->statScore / 113, 300); - bonus = std::max(bonus, 0); + bonusScale = std::max(bonusScale, 0); + + const int scaledBonus = stat_bonus(depth) * bonusScale / 32; update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - stat_bonus(depth) * bonus / 93); - thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] - << stat_bonus(depth) * bonus / 179; + scaledBonus * 416 / 1024); + thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << scaledBonus * 212 / 1024; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] - << stat_bonus(depth) * bonus / 24; + << scaledBonus * 1073 / 1024; } else if (priorCapture && prevSq != SQ_NONE) @@ -1410,7 +1411,7 @@ moves_loop: // When in check, search starts here // Bonus when search fails low and there is a TT move else if (ttData.move && !allNode) - thisThread->mainHistory[us][ttData.move.from_to()] << stat_bonus(depth) * 23 / 100; + thisThread->mainHistory[us][ttData.move.from_to()] << stat_bonus(depth) * 287 / 1024; if (PvNode) bestValue = std::min(bestValue, maxValue); @@ -1808,30 +1809,30 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - update_quiet_histories(pos, ss, workerThread, bestMove, bonus); + update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 1131 / 1024); // Decrease stats for all non-best quiet moves for (Move move : quietsSearched) - update_quiet_histories(pos, ss, workerThread, move, -malus); + update_quiet_histories(pos, ss, workerThread, move, -malus * 1028 / 1024); } else { // Increase stats for the best move in case it was a capture move captured = type_of(pos.piece_on(bestMove.to_sq())); - captureHistory[moved_piece][bestMove.to_sq()][captured] << bonus; + captureHistory[moved_piece][bestMove.to_sq()][captured] << bonus * 1291 / 1024; } // Extra penalty for a quiet early move that was not a TT move in // previous ply when it gets refuted. if (prevSq != SQ_NONE && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit) && !pos.captured_piece()) - update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * 919 / 1024); // Decrease stats for all non-best capture moves for (Move move : capturesSearched) { moved_piece = pos.moved_piece(move); captured = type_of(pos.piece_on(move.to_sq())); - captureHistory[moved_piece][move.to_sq()][captured] << -malus; + captureHistory[moved_piece][move.to_sq()][captured] << -malus * 1090 / 1024; } } @@ -1839,16 +1840,16 @@ void update_all_stats(const Position& pos, // Updates histories of the move pairs formed by moves // at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { + static constexpr std::array conthist_bonuses = { + {{1, 1024}, {2, 571}, {3, 339}, {4, 500}, {6, 592}}}; - bonus = bonus * 50 / 64; - - for (int i : {1, 2, 3, 4, 6}) + for (const auto [i, weight] : conthist_bonuses) { // Only update the first 2 continuation histories if we are in check if (ss->inCheck && i > 2) break; if (((ss - i)->currentMove).is_ok()) - (*(ss - i)->continuationHistory)[pc][to] << bonus / (1 + (i == 3)); + (*(ss - i)->continuationHistory)[pc][to] << bonus * weight / 1024; } } @@ -1858,14 +1859,15 @@ void update_quiet_histories( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { Color us = pos.side_to_move(); - workerThread.mainHistory[us][move.from_to()] << bonus; - if (ss->ply < LOW_PLY_HISTORY_SIZE) - workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus; + workerThread.mainHistory[us][move.from_to()] << bonus; // Untuned to prevent duplicate effort - update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus); + if (ss->ply < LOW_PLY_HISTORY_SIZE) + workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 874 / 1024; + + update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 853 / 1024); int pIndex = pawn_structure_index(pos); - workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus / 2; + workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus * 628 / 1024; } } diff --git a/src/search.h b/src/search.h index b618855b9..e9a7943d1 100644 --- a/src/search.h +++ b/src/search.h @@ -351,6 +351,11 @@ class Worker { friend class SearchManager; }; +struct ConthistBonus { + int index; + int weight; +}; + } // namespace Search