From 8d517bddfffffd9d292fb5362ce452e5eb056ce9 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 9 Jan 2025 20:22:41 -0800 Subject: [PATCH 01/24] Simplify accumulator updates AMD Ryzen 5 7600X ``` sf_base = 1902646 +/- 2114 (95%) sf_test = 1920873 +/- 2515 (95%) diff = 18227 +/- 3067 (95%) speedup = 0.95800% +/- 0.161% (95%) ``` Ryzen 9 5950X ``` sf_base = 1413387 +/- 3592 (95%) sf_test = 1437893 +/- 3355 (95%) diff = 24505 +/- 4669 (95%) speedup = 1.73380% +/- 0.330% (95%) ``` Intel Core i7-6700K ``` sf_base = 912476 +/- 1863 (95%) sf_test = 921864 +/- 2042 (95%) diff = 9388 +/- 3333 (95%) speedup = 1.02893% +/- 0.365% (95%) ``` Raspberry Pi 5 ``` sf_base = 260993 +/- 1508 (95%) sf_test = 262912 +/- 1746 (95%) diff = 1918 +/- 1221 (95%) speedup = 0.73504% +/- 0.468% (95%) ``` Passed Non-regression STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 23072 W: 6041 L: 5813 D: 11218 Ptnml(0-2): 61, 2435, 6319, 2657, 64 https://tests.stockfishchess.org/tests/view/6780a0ca9168c8bf30927757 closes https://github.com/official-stockfish/Stockfish/pull/5759 No functional change --- src/nnue/nnue_feature_transformer.h | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index b047f62c4..8649d9521 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -472,9 +472,8 @@ class FeatureTransformer { return st; } - // It computes the accumulator of the next position, or updates the - // current position's accumulator if CurrentOnly is true. - template + // Computes the accumulator of the next position. + template void update_accumulator_incremental(const Position& pos, StateInfo* computed) const { assert((computed->*accPtr).computed[Perspective]); assert(computed->next != nullptr); @@ -493,16 +492,10 @@ class FeatureTransformer { // feature set's update cost calculation to be correct and never allow // updates with more added/removed features than MaxActiveDimensions. FeatureSet::IndexList removed, added; + FeatureSet::append_changed_indices(ksq, computed->next->dirtyPiece, removed, + added); - if constexpr (CurrentOnly) - for (StateInfo* st = pos.state(); st != computed; st = st->previous) - FeatureSet::append_changed_indices(ksq, st->dirtyPiece, removed, - added); - else - FeatureSet::append_changed_indices(ksq, computed->next->dirtyPiece, - removed, added); - - StateInfo* next = CurrentOnly ? pos.state() : computed->next; + StateInfo* next = computed->next; assert(!(next->*accPtr).computed[Perspective]); #ifdef VECTOR @@ -665,8 +658,8 @@ class FeatureTransformer { (next->*accPtr).computed[Perspective] = true; - if (!CurrentOnly && next != pos.state()) - update_accumulator_incremental(pos, next); + if (next != pos.state()) + update_accumulator_incremental(pos, next); } template @@ -844,7 +837,7 @@ class FeatureTransformer { StateInfo* oldest = try_find_computed_accumulator(pos); if ((oldest->*accPtr).computed[Perspective] && oldest != pos.state()) - update_accumulator_incremental(pos, oldest); + update_accumulator_incremental(pos, oldest); else update_accumulator_refresh_cache(pos, cache); } @@ -858,7 +851,7 @@ class FeatureTransformer { if ((oldest->*accPtr).computed[Perspective] && oldest != pos.state()) // Start from the oldest computed accumulator, update all the // accumulators up to the current position. - update_accumulator_incremental(pos, oldest); + update_accumulator_incremental(pos, oldest); else update_accumulator_refresh_cache(pos, cache); } From 921361829a1c09d289e973a507652ec7be9dc796 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 10 Jan 2025 09:11:31 -0800 Subject: [PATCH 02/24] Simplify away capthist bonus in Probcut The explicit bonus has been obsoleted with the introduction of #5695 Passed Non-regression STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 132832 W: 34519 L: 34403 D: 63910 Ptnml(0-2): 430, 15754, 33931, 15872, 429 https://tests.stockfishchess.org/tests/view/678158c49168c8bf30927834 Passed Non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 123492 W: 31426 L: 31309 D: 60757 Ptnml(0-2): 79, 13705, 34051, 13842, 69 https://tests.stockfishchess.org/tests/view/6782b07e6ddf09c0b4b6dbb7 closes https://github.com/official-stockfish/Stockfish/pull/5761 Bench: 1180439 --- src/search.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 61425acdc..47e7f4bc3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -861,7 +861,6 @@ Value Search::Worker::search( assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &thisThread->captureHistory); - Piece captured; while ((move = mp.next_move()) != Move::none()) { @@ -875,10 +874,6 @@ Value Search::Worker::search( assert(pos.capture_stage(move)); - movedPiece = pos.moved_piece(move); - captured = pos.piece_on(move.to_sq()); - - // Prefetch the TT entry for the resulting position prefetch(tt.first_entry(pos.key_after(move))); @@ -903,8 +898,6 @@ Value Search::Worker::search( if (value >= probCutBeta) { - 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, depth - 3, move, unadjustedStaticEval, tt.generation()); From b84c8807a381cd48f5c3dfe1e649a0a727dc56fa Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 11 Jan 2025 14:49:09 -0800 Subject: [PATCH 03/24] Optimize attackers_to() https://tests.stockfishchess.org/tests/view/6782decb6ddf09c0b4b6e1b0 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 105920 W: 27571 L: 27181 D: 51168 Ptnml(0-2): 284, 10808, 30403, 11164, 301 - If we only need to know if attackers exist we can skip some calculations. - Also calculating slider/magic attackers first is better because the double lookup is slow due to memory latency. - I also included a couple of very minor cleanups in search that probably don't warrant their own PR but I can open separately if that's better. closes https://github.com/official-stockfish/Stockfish/pull/5762 No functional change --- src/position.cpp | 27 ++++++++++++++++++--------- src/position.h | 1 + src/search.cpp | 8 ++++---- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 60b7d7d3f..9d883c92b 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -493,14 +493,23 @@ void Position::update_slider_blockers(Color c) const { // Slider attacks use the occupied bitboard to indicate occupancy. Bitboard Position::attackers_to(Square s, Bitboard occupied) const { - return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN)) - | (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN)) - | (attacks_bb(s) & pieces(KNIGHT)) - | (attacks_bb(s, occupied) & pieces(ROOK, QUEEN)) + return (attacks_bb(s, occupied) & pieces(ROOK, QUEEN)) | (attacks_bb(s, occupied) & pieces(BISHOP, QUEEN)) - | (attacks_bb(s) & pieces(KING)); + | (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN)) + | (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN)) + | (attacks_bb(s) & pieces(KNIGHT)) | (attacks_bb(s) & pieces(KING)); } +bool Position::attackers_to_exist(Square s, Bitboard occupied, Color c) const { + + return ((attacks_bb(s) & pieces(c, ROOK, QUEEN)) + && (attacks_bb(s, occupied) & pieces(c, ROOK, QUEEN))) + || ((attacks_bb(s) & pieces(c, BISHOP, QUEEN)) + && (attacks_bb(s, occupied) & pieces(c, BISHOP, QUEEN))) + || (((pawn_attacks_bb(~c, s) & pieces(PAWN)) | (attacks_bb(s) & pieces(KNIGHT)) + | (attacks_bb(s) & pieces(KING))) + & pieces(c)); +} // Tests whether a pseudo-legal move is legal bool Position::legal(Move m) const { @@ -542,7 +551,7 @@ bool Position::legal(Move m) const { Direction step = to > from ? WEST : EAST; for (Square s = to; s != from; s += step) - if (attackers_to(s) & pieces(~us)) + if (attackers_to_exist(s, pieces(), ~us)) return false; // In case of Chess960, verify if the Rook blocks some checks. @@ -553,7 +562,7 @@ bool Position::legal(Move m) const { // If the moving piece is a king, check whether the destination square is // attacked by the opponent. if (type_of(piece_on(from)) == KING) - return !(attackers_to(to, pieces() ^ from) & pieces(~us)); + return !(attackers_to_exist(to, pieces() ^ from, ~us)); // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. @@ -622,7 +631,7 @@ bool Position::pseudo_legal(const Move m) const { } // In case of king moves under check we have to remove the king so as to catch // invalid moves like b1a1 when opposite queen is on c1. - else if (attackers_to(to, pieces() ^ from) & pieces(~us)) + else if (attackers_to_exist(to, pieces() ^ from, ~us)) return false; } @@ -1308,7 +1317,7 @@ bool Position::pos_is_ok() const { return true; if (pieceCount[W_KING] != 1 || pieceCount[B_KING] != 1 - || attackers_to(square(~sideToMove)) & pieces(sideToMove)) + || attackers_to_exist(square(~sideToMove), pieces(), sideToMove)) assert(0 && "pos_is_ok: Kings"); if ((pieces(PAWN) & (Rank1BB | Rank8BB)) || pieceCount[W_PAWN] > 8 || pieceCount[B_PAWN] > 8) diff --git a/src/position.h b/src/position.h index 7dc83f251..7fdaf84d5 100644 --- a/src/position.h +++ b/src/position.h @@ -126,6 +126,7 @@ class Position { // Attacks to/from a given square Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s, Bitboard occupied) const; + bool attackers_to_exist(Square s, Bitboard occupied, Color c) const; void update_slider_blockers(Color c) const; template Bitboard attacks_by(Color c) const; diff --git a/src/search.cpp b/src/search.cpp index 47e7f4bc3..d35121304 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -77,7 +77,7 @@ constexpr int futility_move_count(bool improving, Depth depth) { return (3 + depth * depth) / (2 - improving); } -int correction_value(const Worker& w, const Position& pos, Stack* ss) { +int correction_value(const Worker& w, const Position& pos, const Stack* ss) { const Color us = pos.side_to_move(); const auto m = (ss - 1)->currentMove; const auto pcv = w.pawnCorrectionHistory[us][pawn_structure_index(pos)]; @@ -1140,7 +1140,7 @@ moves_loop: // When in check, search starts here // Decrease reduction if position is or has been on the PV (~7 Elo) if (ss->ttPv) - r -= 1024 + (ttData.value > alpha) * 1024 + (ttData.depth >= depth) * 1024; + r -= 1024 + ((ttData.value > alpha) + (ttData.depth >= depth)) * 1024; // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) if (PvNode) @@ -1423,8 +1423,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; - static const int nonPawnWeight = 154; + const auto m = (ss - 1)->currentMove; + constexpr int nonPawnWeight = 154; auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); From 8b32e4825fb86b4409a266e3612d86790da9be36 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 12 Jan 2025 02:38:58 +0300 Subject: [PATCH 04/24] Make IIR less aggressive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is an elo gaining simplification which gains elo at longer time controls. Patch disallows IIR for cutNodes with existing tt moves as well as makes IIR for pv nodes less aggressive, basiclally confirming suspected scaling patterns for this heuristic. Result of 50k games STC run: https://tests.stockfishchess.org/tests/view/678304676ddf09c0b4b6f9f9 Elo: -2.93 ± 1.6 (95%) LOS: 0.0% Total: 50000 W: 12718 L: 13140 D: 24142 Ptnml(0-2): 189, 6087, 12835, 5735, 154 nElo: -5.71 ± 3.0 (95%) PairsRatio: 0.94 Passed VVLTC SPRT with STC bounds: https://tests.stockfishchess.org/tests/view/6782eb1a6ddf09c0b4b6e6b0 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 150292 W: 38868 L: 38458 D: 72966 Ptnml(0-2): 19, 13890, 46907, 14322, 8 Passed VVLTC SPRT with LTC bounds: https://tests.stockfishchess.org/tests/view/6782d8d96ddf09c0b4b6df18 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 153388 W: 39791 L: 39285 D: 74312 Ptnml(0-2): 13, 13924, 48311, 14436, 10 closes https://github.com/official-stockfish/Stockfish/pull/5763 Bench: 1507606 --- src/search.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d35121304..1e2497f70 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -833,19 +833,17 @@ Value Search::Worker::search( } // Step 10. Internal iterative reductions (~9 Elo) - // For PV nodes without a ttMove, we decrease depth. - if (PvNode && !ttData.move) - depth -= 3; + // For PV nodes without a ttMove as well as for deep enough cutNodes, we decrease depth. + // This heuristic is known to scale non-linearly, current version was tested at VVLTC. + // Further improvements need to be tested at similar time control if they make IIR + // more aggressive. + if ((PvNode || (cutNode && depth >= 7)) && !ttData.move) + depth -= 2; // Use qsearch if depth <= 0 if (depth <= 0) return qsearch(pos, ss, alpha, beta); - // For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove, - // or by 1 if there is a ttMove with an upper bound. - if (cutNode && depth >= 7 && (!ttData.move || ttData.bound == BOUND_UPPER)) - depth -= 1 + !ttData.move; - // 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. From 93edf7a74cdb6871e43f66716a8a07912eedc14f Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sun, 12 Jan 2025 10:54:59 +0800 Subject: [PATCH 05/24] VVLTC Search Tune Values were tuned with 118k VVLTC games. Tested against #5764. Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/678331226ddf09c0b4b6fd78 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 43556 W: 11219 L: 10942 D: 21395 Ptnml(0-2): 2, 3975, 13549, 4248, 4 Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/67834aa06ddf09c0b4b6fe34 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 37150 W: 9577 L: 9285 D: 18288 Ptnml(0-2): 2, 3344, 11593, 3632, 4 closes https://github.com/official-stockfish/Stockfish/pull/5765 Bench: 1258128 --- src/search.cpp | 150 ++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1e2497f70..9ee9fb791 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -66,7 +66,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 109 - 27 * noTtCutNode; + Value futilityMult = 112 - 26 * noTtCutNode; Value improvingDeduction = improving * futilityMult * 2; Value worseningDeduction = oppWorsening * futilityMult / 3; @@ -89,7 +89,7 @@ int correction_value(const Worker& w, const Position& pos, const Stack* ss) { m.is_ok() ? (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] : 0; - return (6384 * pcv + 3583 * macv + 6492 * micv + 6725 * (wnpcv + bnpcv) + 5880 * cntcv); + return (6922 * pcv + 3837 * macv + 6238 * micv + 7490 * (wnpcv + bnpcv) + 6270 * cntcv); } // Add correctionHistory value to raw staticEval and guarantee evaluation @@ -99,10 +99,10 @@ Value to_corrected_static_eval(Value v, const int cv) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(168 * d - 100, 1718); } +int stat_bonus(Depth d) { return std::min(154 * d - 102, 1661); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(768 * d - 257, 2351); } +int stat_malus(Depth d) { return std::min(831 * d - 269, 2666); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -274,7 +274,7 @@ void Search::Worker::iterative_deepening() { int searchAgainCounter = 0; - lowPlyHistory.fill(106); + lowPlyHistory.fill(97); // Iterative deepening loop until requested to stop or the target depth is reached while (++rootDepth < MAX_PLY && !threads.stop @@ -310,13 +310,13 @@ void Search::Worker::iterative_deepening() { selDepth = 0; // Reset aspiration window starting size - delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 13461; + delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 12991; Value avg = rootMoves[pvIdx].averageScore; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 150 * avg / (std::abs(avg) + 85); + optimism[us] = 141 * avg / (std::abs(avg) + 83); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -498,10 +498,10 @@ void Search::Worker::iterative_deepening() { // Reset histories, usually before a new game void Search::Worker::clear() { - mainHistory.fill(61); - lowPlyHistory.fill(106); - captureHistory.fill(-598); - pawnHistory.fill(-1181); + mainHistory.fill(63); + lowPlyHistory.fill(108); + captureHistory.fill(-631); + pawnHistory.fill(-1210); pawnCorrectionHistory.fill(0); majorPieceCorrectionHistory.fill(0); minorPieceCorrectionHistory.fill(0); @@ -516,10 +516,10 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h.fill(-427); + h.fill(-479); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int(19.43 * std::log(i)); + reductions[i] = int(2143 / 100.0 * std::log(i)); refreshTable.clear(networks[numaAccessToken]); } @@ -636,20 +636,20 @@ Value Search::Worker::search( if (!PvNode && !excludedMove && ttData.depth > depth - (ttData.value <= beta) && is_valid(ttData.value) // Can happen when !ttHit or when access race in probe() && (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER)) - && (cutNode == (ttData.value >= beta) || depth > 8)) + && (cutNode == (ttData.value >= beta) || depth > 9)) { // If ttMove is quiet, update move sorting heuristics on TT hit (~2 Elo) if (ttData.move && ttData.value >= beta) { // Bonus for a quiet ttMove that fails high (~2 Elo) if (!ttCapture) - update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth) * 747 / 1024); + update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth) * 746 / 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) * 1091 / 1024); + -stat_malus(depth + 1) * 1042 / 1024); } // Partial workaround for the graph history interaction problem @@ -757,11 +757,11 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) 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 * 1340 / 1024; + int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1881, 1413) + 616; + thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1151 / 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 * 1159 / 1024; + << bonus * 1107 / 1024; } // Set up the improving flag, which is true if current static evaluation is @@ -776,30 +776,30 @@ Value Search::Worker::search( // If eval is really low, check with qsearch if we can exceed alpha. If the // search suggests we cannot exceed alpha, return a speculative fail low. // For PvNodes, we must have a guard against mates being returned. - if (!PvNode && eval < alpha - 469 - 307 * depth * depth) + if (!PvNode && eval < alpha - 462 - 297 * depth * depth) return qsearch(pos, ss, alpha - 1, alpha); // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. if (!ss->ttPv && depth < 14 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 290 + - (ss - 1)->statScore / 310 + (ss->staticEval == eval) * (40 - std::abs(correctionValue) / 131072) >= beta && eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval)) return beta + (eval - beta) / 3; - improving |= ss->staticEval >= beta + 100; + improving |= ss->staticEval >= beta + 97; // Step 9. Null move search with verification search (~35 Elo) if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta - && ss->staticEval >= beta - 21 * depth + 421 && !excludedMove && pos.non_pawn_material(us) + && ss->staticEval >= beta - 20 * depth + 440 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && !is_loss(beta)) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 235, 7) + depth / 3 + 5; + Depth R = std::min(int(eval - beta) / 215, 7) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -847,7 +847,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 - 56 * improving; + probCutBeta = beta + 174 - 56 * improving; if (depth > 3 && !is_decisive(beta) // If value from transposition table is lower than probCutBeta, don't attempt @@ -909,7 +909,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea (~4 Elo) - probCutBeta = beta + 417; + probCutBeta = beta + 412; if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta && !is_decisive(beta) && is_valid(ttData.value) && !is_decisive(ttData.value)) return probCutBeta; @@ -992,15 +992,15 @@ moves_loop: // When in check, search starts here // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 287 + 253 * lmrDepth + Value futilityValue = ss->staticEval + 271 + 243 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / 33, -161 * depth, 156 * depth); - if (!pos.see_ge(move, -162 * depth - seeHist)) + int seeHist = std::clamp(captHist / 37, -152 * depth, 141 * depth); + if (!pos.see_ge(move, -156 * depth - seeHist)) continue; } else @@ -1011,15 +1011,15 @@ moves_loop: // When in check, search starts here + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (history < -3884 * depth) + if (history < -3901 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 3609; + lmrDepth += history / 3459; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 45 ? 140 : 43) + 141 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 47 ? 137 : 47) + 142 * lmrDepth; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha) @@ -1060,7 +1060,7 @@ moves_loop: // When in check, search starts here && is_valid(ttData.value) && !is_decisive(ttData.value) && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3) { - Value singularBeta = ttData.value - (56 + 79 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttData.value - (52 + 74 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1070,13 +1070,13 @@ moves_loop: // When in check, search starts here if (value < singularBeta) { - int doubleMargin = 249 * PvNode - 194 * !ttCapture; - int tripleMargin = 94 + 287 * PvNode - 249 * !ttCapture + 99 * ss->ttPv; + int doubleMargin = 259 * PvNode - 194 * !ttCapture; + int tripleMargin = 90 + 266 * PvNode - 272 * !ttCapture + 107 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); - depth += ((!PvNode) && (depth < 14)); + depth += ((!PvNode) && (depth < 15)); } // Multi-cut pruning @@ -1109,7 +1109,7 @@ moves_loop: // When in check, search starts here else if (PvNode && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4321) + > 4126) extension = 1; } @@ -1138,46 +1138,46 @@ moves_loop: // When in check, search starts here // Decrease reduction if position is or has been on the PV (~7 Elo) if (ss->ttPv) - r -= 1024 + ((ttData.value > alpha) + (ttData.depth >= depth)) * 1024; + r -= 1037 + (ttData.value > alpha) * 965 + (ttData.depth >= depth) * 960; // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) if (PvNode) - r -= 1024; + r -= 1018; // These reduction adjustments have no proven non-linear scaling - r += 330; + r += 307; - r -= std::abs(correctionValue) / 32768; + r -= std::abs(correctionValue) / 34112; // Increase reduction for cut nodes (~4 Elo) if (cutNode) - r += 2518 - (ttData.depth >= depth && ss->ttPv) * 991; + r += 2355 - (ttData.depth >= depth && ss->ttPv) * 1141; // Increase reduction if ttMove is a capture but the current move is not a capture (~3 Elo) if (ttCapture && !capture) - r += 1043 + (depth < 8) * 999; + r += 1087 + (depth < 8) * 990; // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss + 1)->cutoffCnt > 3) - r += 938 + allNode * 960; + r += 940 + allNode * 887; // For first picked move (ttMove) reduce reduction (~3 Elo) else if (move == ttData.move) - r -= 1879; + r -= 1960; if (capture) ss->statScore = 7 * int(PieceValue[pos.captured_piece()]) + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())] - - 5000; + - 4666; else ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 3996; + + (*contHist[1])[movedPiece][move.to_sq()] - 3874; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore * 1287 / 16384; + r -= ss->statScore * 1451 / 16384; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1) @@ -1197,7 +1197,7 @@ moves_loop: // When in check, search starts here { // Adjust full-depth search based on LMR results - if the result was // good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 42 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 40 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + 10; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1216,11 +1216,11 @@ moves_loop: // When in check, search starts here { // Increase reduction if ttMove is not present (~6 Elo) if (!ttData.move) - r += 2037; + r += 2111; // 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 > 2983), !cutNode); + -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3444), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail high, @@ -1369,25 +1369,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 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)); + int bonusScale = (118 * (depth > 5) + 37 * !allNode + 169 * ((ss - 1)->moveCount > 8) + + 128 * (!ss->inCheck && bestValue <= ss->staticEval - 102) + + 115 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 82)); // Proportional to "how much damage we have to undo" - bonusScale += std::min(-(ss - 1)->statScore / 113, 300); + bonusScale += std::min(-(ss - 1)->statScore / 106, 318); bonusScale = std::max(bonusScale, 0); const int scaledBonus = stat_bonus(depth) * bonusScale / 32; update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - scaledBonus * 416 / 1024); + scaledBonus * 436 / 1024); - thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << scaledBonus * 212 / 1024; + thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << scaledBonus * 207 / 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] - << scaledBonus * 1073 / 1024; + << scaledBonus * 1195 / 1024; } else if (priorCapture && prevSq != SQ_NONE) @@ -1422,14 +1422,14 @@ moves_loop: // When in check, search starts here || (bestValue > ss->staticEval && bestMove))) // positive correction & no fail low { const auto m = (ss - 1)->currentMove; - constexpr int nonPawnWeight = 154; + constexpr int nonPawnWeight = 165; auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] - << bonus * 107 / 128; - thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 162 / 128; - thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 148 / 128; + << bonus * 114 / 128; + thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 163 / 128; + thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 146 / 128; thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)] << bonus * nonPawnWeight / 128; thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)] @@ -1561,7 +1561,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 306; + futilityBase = ss->staticEval + 301; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1624,11 +1624,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= 5095) + <= 5228) continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -83)) + if (!pos.see_ge(move, -80)) continue; } @@ -1696,7 +1696,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const { int reductionScale = reductions[d] * reductions[mn]; - return reductionScale - delta * 814 / rootDelta + !i * reductionScale / 3 + 1304; + return reductionScale - delta * 768 / rootDelta + !i * reductionScale * 108 / 300 + 1168; } // elapsed() returns the time elapsed since the search started. If the @@ -1795,30 +1795,30 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 1131 / 1024); + update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 1216 / 1024); // Decrease stats for all non-best quiet moves for (Move move : quietsSearched) - update_quiet_histories(pos, ss, workerThread, move, -malus * 1028 / 1024); + update_quiet_histories(pos, ss, workerThread, move, -malus * 1062 / 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 * 1291 / 1024; + captureHistory[moved_piece][bestMove.to_sq()][captured] << bonus * 1272 / 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 * 919 / 1024); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * 966 / 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 * 1090 / 1024; + captureHistory[moved_piece][move.to_sq()][captured] << -malus * 1205 / 1024; } } @@ -1827,7 +1827,7 @@ void update_all_stats(const Position& pos, // 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}}}; + {{1, 1025}, {2, 621}, {3, 325}, {4, 512}, {6, 534}}}; for (const auto [i, weight] : conthist_bonuses) { @@ -1848,12 +1848,12 @@ void update_quiet_histories( workerThread.mainHistory[us][move.from_to()] << bonus; // Untuned to prevent duplicate effort if (ss->ply < LOW_PLY_HISTORY_SIZE) - workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 874 / 1024; + workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 879 / 1024; - update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 853 / 1024); + update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 888 / 1024); int pIndex = pawn_structure_index(pos); - workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus * 628 / 1024; + workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus * 634 / 1024; } } From e2612f9a294a2d9ee2f961ecd52a6c8b9043e989 Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Sun, 12 Jan 2025 13:00:45 +0000 Subject: [PATCH 06/24] Introduce Correction History Quad Extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also modifies the double and triple extension margins with the correction history adjustment. STC Elo Estimate: Elo: -4.40 ± 1.4 (95%) LOS: 0.0% Total: 60000 W: 15230 L: 15990 D: 28780 Ptnml(0-2): 264, 7495, 15168, 6883, 190 nElo: -8.48 ± 2.8 (95%) PairsRatio: 0.91 https://tests.stockfishchess.org/tests/view/6783a3786ddf09c0b4b703a1 Passed 1st VVLTC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 35736 W: 9354 L: 9088 D: 17294 Ptnml(0-2): 4, 3191, 11212, 3457, 4 https://tests.stockfishchess.org/tests/view/6783a3336ddf09c0b4b7039b Passed 2nd VVLTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 36394 W: 9515 L: 9225 D: 17654 Ptnml(0-2): 1, 3271, 11364, 3559, 2 https://tests.stockfishchess.org/tests/view/678395e26ddf09c0b4b70345 closes https://github.com/official-stockfish/Stockfish/pull/5767 Bench: 1567166 --- src/search.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9ee9fb791..f20e4f023 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1070,11 +1070,16 @@ moves_loop: // When in check, search starts here if (value < singularBeta) { - int doubleMargin = 259 * PvNode - 194 * !ttCapture; - int tripleMargin = 90 + 266 * PvNode - 272 * !ttCapture + 107 * ss->ttPv; + int corrValAdj = std::abs(correctionValue) / 262144; + int doubleMargin = 249 * PvNode - 194 * !ttCapture - corrValAdj; + int tripleMargin = + 94 + 287 * PvNode - 249 * !ttCapture + 99 * ss->ttPv - corrValAdj; + int quadMargin = + 394 + 287 * PvNode - 249 * !ttCapture + 99 * ss->ttPv - corrValAdj; extension = 1 + (value < singularBeta - doubleMargin) - + (value < singularBeta - tripleMargin); + + (value < singularBeta - tripleMargin) + + (value < singularBeta - quadMargin); depth += ((!PvNode) && (depth < 15)); } From c085670b8474dd2137446ff278f6b73f4374cc68 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 12 Jan 2025 20:20:56 +0300 Subject: [PATCH 07/24] Increase the depth margin Tested at VVLTC against the passed patches. Test 1 against PR5764 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 167260 W: 43053 L: 42521 D: 81686 Ptnml(0-2): 7, 15272, 52542, 15800, 9 https://tests.stockfishchess.org/tests/view/6782ef196ddf09c0b4b6e780 Test 2 against PR5765 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 300012 W: 77364 L: 76771 D: 145877 Ptnml(0-2): 22, 27555, 94256, 28154, 19 https://tests.stockfishchess.org/tests/view/678366446ddf09c0b4b7028c closes https://github.com/official-stockfish/Stockfish/pull/5768 Bench: 1379150 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f20e4f023..c27d93d25 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1056,7 +1056,7 @@ moves_loop: // When in check, search starts here // and lower extension margins scale well. if (!rootNode && move == ttData.move && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 33) + ss->ttPv + && depth >= 5 - (thisThread->completedDepth > 33) + ss->ttPv && is_valid(ttData.value) && !is_decisive(ttData.value) && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3) { From aaafaaecf2aa15806b1c1bd10f6a74379ba094b6 Mon Sep 17 00:00:00 2001 From: Carlos Esparza Date: Sun, 12 Jan 2025 23:22:01 +0100 Subject: [PATCH 08/24] prefetch in do_move() this allows removing Position::key_after() STC LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 24960 W: 6556 L: 6336 D: 12068 Ptnml(0-2): 59, 2554, 7056, 2730, 81 LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 115080 W: 29319 L: 29204 D: 56557 Ptnml(0-2): 51, 10736, 35864, 10825, 64 STC with 2MB hash LLR: 3.04 (-2.94,2.94) <-1.75,0.25> Total: 182176 W: 46998 L: 46932 D: 88246 Ptnml(0-2): 526, 19711, 50544, 19785, 522 LTC with 8MB hash LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 441180 W: 111557 L: 111746 D: 217877 Ptnml(0-2): 229, 39698, 140929, 39501, 233 closes https://github.com/official-stockfish/Stockfish/pull/5770 bench: 1379150 --- src/position.cpp | 32 +++++++++++--------------------- src/position.h | 9 +++++---- src/search.cpp | 41 ++++++++++++++++++----------------------- 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 9d883c92b..49f520e01 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -689,7 +689,12 @@ bool Position::gives_check(Move m) const { // Makes a move, and saves all information necessary // to a StateInfo object. The move is assumed to be legal. Pseudo-legal // moves should be filtered out before this function is called. -void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { +// If a pointer to the TT table is passed, the entry for the new position +// will be prefetched +void Position::do_move(Move m, + StateInfo& newSt, + bool givesCheck, + const TranspositionTable* tt = nullptr) { assert(m.is_ok()); assert(&newSt != st); @@ -887,11 +892,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; } - // Set capture piece - st->capturedPiece = captured; - // Update the key with the final value st->key = k; + if (tt) + prefetch(tt->first_entry(key())); + + // Set capture piece + st->capturedPiece = captured; // Calculate checkers bitboard (if move gives check) st->checkersBB = givesCheck ? attackers_to(square(them)) & pieces(us) : 0; @@ -1069,23 +1076,6 @@ void Position::undo_null_move() { } -// Computes the new hash key after the given move. Needed -// for speculative prefetch. It doesn't recognize special moves like castling, -// en passant and promotions. -Key Position::key_after(Move m) const { - - Square from = m.from_sq(); - Square to = m.to_sq(); - Piece pc = piece_on(from); - Piece captured = piece_on(to); - Key k = st->key ^ Zobrist::side; - - k ^= Zobrist::psq[captured][to] ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; - - return (captured || type_of(pc) == PAWN) ? k : adjust_key50(k); -} - - // Tests if the SEE (Static Exchange Evaluation) // value of move is greater or equal to the given threshold. We'll use an // algorithm similar to alpha-beta pruning with a null window. diff --git a/src/position.h b/src/position.h index 7fdaf84d5..0d49a60a9 100644 --- a/src/position.h +++ b/src/position.h @@ -141,8 +141,8 @@ class Position { Piece captured_piece() const; // Doing and undoing moves - void do_move(Move m, StateInfo& newSt); - void do_move(Move m, StateInfo& newSt, bool givesCheck); + void do_move(Move m, StateInfo& newSt, const TranspositionTable* tt); + void do_move(Move m, StateInfo& newSt, bool givesCheck, const TranspositionTable* tt); void undo_move(Move m); void do_null_move(StateInfo& newSt, const TranspositionTable& tt); void undo_null_move(); @@ -152,7 +152,6 @@ class Position { // Accessing hash keys Key key() const; - Key key_after(Move m) const; Key material_key() const; Key pawn_key() const; Key major_piece_key() const; @@ -369,7 +368,9 @@ inline void Position::move_piece(Square from, Square to) { board[to] = pc; } -inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); } +inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) { + do_move(m, newSt, gives_check(m), tt); +} inline StateInfo* Position::state() const { return st; } diff --git a/src/search.cpp b/src/search.cpp index c27d93d25..7f064bfb3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -872,17 +872,16 @@ Value Search::Worker::search( assert(pos.capture_stage(move)); - // Prefetch the TT entry for the resulting position - prefetch(tt.first_entry(pos.key_after(move))); + movedPiece = pos.moved_piece(move); + + pos.do_move(move, st, &tt); + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); ss->currentMove = move; ss->continuationHistory = - &this->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()]; + &this->continuationHistory[ss->inCheck][true][movedPiece][move.to_sq()]; ss->continuationCorrectionHistory = - &this->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()]; - - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); - pos.do_move(move, st); + &this->continuationCorrectionHistory[movedPiece][move.to_sq()]; // Perform a preliminary qsearch to verify that the move holds value = -qsearch(pos, ss + 1, -probCutBeta, -probCutBeta + 1); @@ -1118,12 +1117,13 @@ moves_loop: // When in check, search starts here extension = 1; } + // Step 16. Make the move + pos.do_move(move, st, givesCheck, &tt); + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); + // Add extension to new depth newDepth += extension; - // Speculative prefetch as early as possible - prefetch(tt.first_entry(pos.key_after(move))); - // Update the current move (this must be done after singular extension search) ss->currentMove = move; ss->continuationHistory = @@ -1132,10 +1132,6 @@ moves_loop: // When in check, search starts here &thisThread->continuationCorrectionHistory[movedPiece][move.to_sq()]; uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0; - // Step 16. Make the move - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); - pos.do_move(move, st, givesCheck); - // These reduction adjustments have proven non-linear scaling. // They are optimized to time controls of 180 + 1.8 and longer, // so changing them or adding conditions that are similar requires @@ -1637,20 +1633,19 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) continue; } - // Speculative prefetch as early as possible - prefetch(tt.first_entry(pos.key_after(move))); + // Step 7. Make and search the move + Piece movedPiece = pos.moved_piece(move); + + pos.do_move(move, st, givesCheck, &tt); + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); // Update the current move ss->currentMove = move; ss->continuationHistory = - &thisThread - ->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()]; + &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; ss->continuationCorrectionHistory = - &thisThread->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()]; + &thisThread->continuationCorrectionHistory[movedPiece][move.to_sq()]; - // Step 7. Make and search the move - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); - pos.do_move(move, st, givesCheck); value = -qsearch(pos, ss + 1, -beta, -alpha); pos.undo_move(move); @@ -2148,7 +2143,7 @@ bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& po if (pv[0] == Move::none()) return false; - pos.do_move(pv[0], st); + pos.do_move(pv[0], st, &tt); auto [ttHit, ttData, ttWriter] = tt.probe(pos.key()); if (ttHit) From 3104cd72d531dd4908bbaa01d3206a62112c3c74 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 12 Jan 2025 16:31:59 -0800 Subject: [PATCH 09/24] Fix initialization of TTData. https://tests.stockfishchess.org/tests/view/6757757686d5ee47d9541de9 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 151200 W: 39396 L: 39306 D: 72498 Ptnml(0-2): 445, 16404, 41781, 16556, 414 Discussed in more detail here #5766 closes https://github.com/official-stockfish/Stockfish/pull/5773 No functional change --- src/tt.cpp | 4 +++- src/tt.h | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tt.cpp b/src/tt.cpp index 50f5ca45a..5d8457611 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -238,7 +238,9 @@ std::tuple TranspositionTable::probe(const Key key) cons > tte[i].depth8 - tte[i].relative_age(generation8) * 2) replace = &tte[i]; - return {false, TTData(), TTWriter(replace)}; + return {false, + TTData{Move::none(), VALUE_NONE, VALUE_NONE, DEPTH_ENTRY_OFFSET, BOUND_NONE, false}, + TTWriter(replace)}; } diff --git a/src/tt.h b/src/tt.h index f0936fd28..065380ca8 100644 --- a/src/tt.h +++ b/src/tt.h @@ -51,6 +51,15 @@ struct TTData { Depth depth; Bound bound; bool is_pv; + + TTData() = delete; + TTData(Move m, Value v, Value ev, Depth d, Bound b, bool pv) : + move(m), + value(v), + eval(ev), + depth(d), + bound(b), + is_pv(pv) {}; }; From 5868b4cb584f7fcf55e4f901fc059af86c53d89e Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Mon, 13 Jan 2025 18:54:40 -0800 Subject: [PATCH 10/24] remove eval== staticeval check in fut pruning Simplify corrplexity in futility margin Don't check that staticEval == eval when applying the corrplexity-based adjustment in futility pruning. Passed Simplification STC LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 121760 W: 31640 L: 31512 D: 58608 Ptnml(0-2): 349, 14400, 31289, 14458, 384 https://tests.stockfishchess.org/tests/view/6780c4109168c8bf30927777 Passed Simplification LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 134772 W: 34245 L: 34140 D: 66387 Ptnml(0-2): 94, 14869, 37350, 14984, 89 https://tests.stockfishchess.org/tests/view/6782d6ea6ddf09c0b4b6dd36 closes https://github.com/official-stockfish/Stockfish/pull/5776 Bench: 1487627 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7f064bfb3..3776e84b0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -783,8 +783,7 @@ Value Search::Worker::search( // The depth condition is important for mate finding. if (!ss->ttPv && depth < 14 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 310 - + (ss->staticEval == eval) * (40 - std::abs(correctionValue) / 131072) + - (ss - 1)->statScore / 310 + 40 - std::abs(correctionValue) / 131072 >= beta && eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval)) return beta + (eval - beta) / 3; From 675319b45d8b420f33d23587f5c23d9bd01096e8 Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 14 Jan 2025 08:22:48 +0100 Subject: [PATCH 11/24] Fix Path to AUTHORS in CONTRIBUTING closes https://github.com/official-stockfish/Stockfish/pull/5777 No functional change --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index caffc916e..0b6fbce0d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,7 +49,7 @@ further discussion._ - Provide a clear and concise description of the changes in the pull request description. -_First time contributors should add their name to [AUTHORS](../AUTHORS)._ +_First time contributors should add their name to [AUTHORS](./AUTHORS)._ _Stockfish's development is not focused on adding new features. Thus any pull request introducing new features will potentially be closed without further @@ -86,7 +86,6 @@ more details. Thank you for contributing to Stockfish and helping us make it even better! - [copying-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt [discord-link]: https://discord.gg/GWDRS3kU6R [discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new From 4c2241089d4650e55a143659fe8002a392a91320 Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 14 Jan 2025 08:34:37 +0100 Subject: [PATCH 12/24] Remove addition of 1ms to all timestamps The +1 was a quick fix to avoid the division by zero, a more correct approach is to use 1ms as the minimum reported timestamp to avoid a division by zero. Later timestamps no longer include an additional 1ms. closes https://github.com/official-stockfish/Stockfish/pull/5778 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 3776e84b0..66c5ff439 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -2117,7 +2117,7 @@ void SearchManager::pv(Search::Worker& worker, if (!isExact) info.bound = bound; - TimePoint time = tm.elapsed_time() + 1; + TimePoint time = std::max(TimePoint(1), tm.elapsed_time()); info.timeMs = time; info.nodes = nodes; info.nps = nodes * 1000 / time; From 56000827af401563737e7be5cf97061133d00f79 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Mon, 13 Jan 2025 09:07:09 +0100 Subject: [PATCH 13/24] Simplify common hint for parent position Removes function hint_common_access_for_perspective together with it's comments, which weren't accurate anymore since merge of #5576 https://tests.stockfishchess.org/tests/view/6784c9cd460e2910c51dde39 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 295104 W: 76702 L: 76765 D: 141637 Ptnml(0-2): 1031, 32135, 81249, 32140, 997 closes https://github.com/official-stockfish/Stockfish/pull/5780 No functional change --- src/nnue/nnue_feature_transformer.h | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 8649d9521..14fdecd72 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -449,8 +449,8 @@ class FeatureTransformer { void hint_common_access(const Position& pos, AccumulatorCaches::Cache* cache) const { - hint_common_access_for_perspective(pos, cache); - hint_common_access_for_perspective(pos, cache); + update_accumulator(pos, cache); + update_accumulator(pos, cache); } private: @@ -821,31 +821,12 @@ class FeatureTransformer { entry.byTypeBB[pt] = pos.pieces(pt); } - template - void hint_common_access_for_perspective(const Position& pos, - AccumulatorCaches::Cache* cache) const { - - // Works like update_accumulator, but performs less work. - // Updates ONLY the accumulator for pos. - - // Look for a usable accumulator of an earlier position. We keep track - // of the estimated gain in terms of features to be added/subtracted. - // Fast early exit. - if ((pos.state()->*accPtr).computed[Perspective]) - return; - - StateInfo* oldest = try_find_computed_accumulator(pos); - - if ((oldest->*accPtr).computed[Perspective] && oldest != pos.state()) - update_accumulator_incremental(pos, oldest); - else - update_accumulator_refresh_cache(pos, cache); - } template void update_accumulator(const Position& pos, AccumulatorCaches::Cache* cache) const { - + if ((pos.state()->*accPtr).computed[Perspective]) + return; StateInfo* oldest = try_find_computed_accumulator(pos); if ((oldest->*accPtr).computed[Perspective] && oldest != pos.state()) From 69ec5dcbfcec498e378297d0383c79ba7804a8cf Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 16 Jan 2025 14:42:27 +0300 Subject: [PATCH 14/24] Remove the type of moved piece from the evasion capture movepick formula In the move generation the moves are generated in the order pawns, knight, bishops, rooks, queens and king. This follows increasing type_of(pos.moved_piece(m)) term, so in master a capturing was sorted after a capturing rook if the same piece was captured in evasion. Because we use a stable sorting method (stable means the order of elements with the same value are not changed) and generate the moves in the above order we do'nt need the removed term. Passed STC: LLR: 2.98 (-2.94,2.94) <-1.75,0.25> Total: 170560 W: 44222 L: 44148 D: 82190 Ptnml(0-2): 569, 18792, 46488, 18858, 573 https://tests.stockfishchess.org/tests/view/678530ee460e2910c51de21d closes https://github.com/official-stockfish/Stockfish/pull/5784 No functional change --- src/movepick.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 844861694..bee5ef5b7 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -186,8 +186,7 @@ void MovePicker::score() { else // Type == EVASIONS { if (pos.capture_stage(m)) - m.value = - PieceValue[pos.piece_on(m.to_sq())] - type_of(pos.moved_piece(m)) + (1 << 28); + m.value = PieceValue[pos.piece_on(m.to_sq())] + (1 << 28); else m.value = (*mainHistory)[pos.side_to_move()][m.from_to()] + (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()] From a944f082256efcb4d7ce181a472f9d674941f949 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Thu, 16 Jan 2025 11:06:52 -0800 Subject: [PATCH 15/24] retroactive reduction decrease if eval improves If the previous reduction was large but the static eval improves then increase the search depth. This patch looks at the next node when calculating the reduction, something I don't think has been done before and which can probably used for further elo gaining patches. Passed STC LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 55936 W: 14813 L: 14462 D: 26661 Ptnml(0-2): 220, 6565, 14094, 6822, 267 https://tests.stockfishchess.org/tests/view/67845b70460e2910c51ddcff Passed LTC LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 189468 W: 48411 L: 47773 D: 93284 Ptnml(0-2): 180, 20801, 52131, 21445, 177 https://tests.stockfishchess.org/tests/view/6784e2cb460e2910c51ddf86 closes https://github.com/official-stockfish/Stockfish/pull/5785 bench: 1512884 --- src/search.cpp | 21 +++++++++++++++++++-- src/search.h | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 66c5ff439..eea48a6f7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -247,10 +247,14 @@ void Search::Worker::iterative_deepening() { &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel (ss - i)->continuationCorrectionHistory = &this->continuationCorrectionHistory[NO_PIECE][0]; (ss - i)->staticEval = VALUE_NONE; + (ss - i)->reduction = 0; } for (int i = 0; i <= MAX_PLY + 2; ++i) - (ss + i)->ply = i; + { + (ss + i)->ply = i; + (ss + i)->reduction = 0; + } ss->pv = pv; @@ -567,6 +571,8 @@ Value Search::Worker::search( Value bestValue, value, eval, maxValue, probCutBeta; bool givesCheck, improving, priorCapture, opponentWorsening; bool capture, ttCapture; + int priorReduction = ss->reduction; + ss->reduction = 0; Piece movedPiece; ValueList capturesSearched; @@ -772,6 +778,11 @@ Value Search::Worker::search( opponentWorsening = ss->staticEval + (ss - 1)->staticEval > 2; + if (priorReduction >= 3 && ss->staticEval + (ss - 1)->staticEval < 0) + { + depth++; + } + // Step 7. Razoring (~1 Elo) // If eval is really low, check with qsearch if we can exceed alpha. If the // search suggests we cannot exceed alpha, return a speculative fail low. @@ -1187,10 +1198,16 @@ moves_loop: // When in check, search starts here // beyond the first move depth. // To prevent problems when the max value is less than the min value, // std::clamp has been replaced by a more robust implementation. + + Depth d = std::max( 1, std::min(newDepth - r / 1024, newDepth + !allNode + (PvNode && !bestMove))); - value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); + (ss + 1)->reduction = newDepth - d; + + value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); + (ss + 1)->reduction = 0; + // Do a full-depth search when reduced LMR search fails high if (value > alpha && d < newDepth) diff --git a/src/search.h b/src/search.h index dee759410..3983e0f33 100644 --- a/src/search.h +++ b/src/search.h @@ -74,6 +74,7 @@ struct Stack { bool ttPv; bool ttHit; int cutoffCnt; + int reduction; }; From 132b90df041fcb14d604bc29442f7067620c52ec Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 17 Jan 2025 10:28:21 +0100 Subject: [PATCH 16/24] Update CI to Ubuntu 22.04 from 20.04 fixes #5756 closes https://github.com/official-stockfish/Stockfish/pull/5786 No functional change --- .github/ci/matrix.json | 8 ++++---- .github/workflows/clang-format.yml | 2 +- .github/workflows/tests.yml | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/ci/matrix.json b/.github/ci/matrix.json index c6563eadf..44e0596ea 100644 --- a/.github/ci/matrix.json +++ b/.github/ci/matrix.json @@ -1,8 +1,8 @@ { "config": [ { - "name": "Ubuntu 20.04 GCC", - "os": "ubuntu-20.04", + "name": "Ubuntu 22.04 GCC", + "os": "ubuntu-22.04", "simple_name": "ubuntu", "compiler": "g++", "comp": "gcc", @@ -111,7 +111,7 @@ { "binaries": "x86-64-avxvnni", "config": { - "ubuntu-20.04": null + "ubuntu-22.04": null } }, { @@ -153,7 +153,7 @@ { "binaries": "apple-silicon", "config": { - "os": "ubuntu-20.04" + "os": "ubuntu-22.04" } } ] diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 452c2f2a3..ab6b4350e 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -18,7 +18,7 @@ permissions: jobs: Clang-Format: name: Clang-Format - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b97aaa29c..57d0d53f0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,15 +13,15 @@ jobs: fail-fast: false matrix: config: - - name: Ubuntu 20.04 GCC - os: ubuntu-20.04 + - name: Ubuntu 22.04 GCC + os: ubuntu-22.04 compiler: g++ comp: gcc run_32bit_tests: true run_64bit_tests: true shell: bash - - name: Ubuntu 20.04 Clang - os: ubuntu-20.04 + - name: Ubuntu 22.04 Clang + os: ubuntu-22.04 compiler: clang++ comp: clang run_32bit_tests: true From ccbd060b01b7aea5373729c06882e2c4d6d53291 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Wed, 15 Jan 2025 14:44:01 -0800 Subject: [PATCH 17/24] simplify razoring Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 93056 W: 24215 L: 24054 D: 44787 Ptnml(0-2): 364, 11085, 23470, 11244, 365 https://tests.stockfishchess.org/tests/view/67883a5d3b8f206a2696b804 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 66564 W: 16971 L: 16794 D: 32799 Ptnml(0-2): 56, 7403, 18192, 7570, 61 https://tests.stockfishchess.org/tests/view/6789ffa78082388fa0cbfe95 closes https://github.com/official-stockfish/Stockfish/pull/5788 bench 1500649 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index eea48a6f7..a080daf30 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -784,11 +784,10 @@ Value Search::Worker::search( } // Step 7. Razoring (~1 Elo) - // If eval is really low, check with qsearch if we can exceed alpha. If the - // search suggests we cannot exceed alpha, return a speculative fail low. + // If eval is really low, skip search entirely and return the qsearch value. // For PvNodes, we must have a guard against mates being returned. if (!PvNode && eval < alpha - 462 - 297 * depth * depth) - return qsearch(pos, ss, alpha - 1, alpha); + return qsearch(pos, ss, alpha, beta); // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. From 4423c8eefa9ca59053199347118574573a4bdca8 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Fri, 17 Jan 2025 21:03:11 +0100 Subject: [PATCH 18/24] Return stockfish-macos-m1-apple-silicon.tar https://github.com/[ppigazzini/stockfish-downloader now uses the official SF script for POSIX systems. closes https://github.com/official-stockfish/Stockfish/pull/5789 No functional change --- scripts/get_native_properties.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/get_native_properties.sh b/scripts/get_native_properties.sh index ed5fc9af0..132bd6f48 100755 --- a/scripts/get_native_properties.sh +++ b/scripts/get_native_properties.sh @@ -76,7 +76,7 @@ case $uname_s in case $uname_m in 'arm64') true_arch='apple-silicon' - file_arch='x86-64-sse41-popcnt' # Supported by Rosetta 2 + file_arch='m1-apple-silicon' ;; 'x86_64') flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '_.') From 165ace194f589abb3e506d36b268255ada2ae3b6 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 17 Jan 2025 23:53:39 +0300 Subject: [PATCH 19/24] Remove the cap from maxscale for x moves in y seconds TC Passed STC 40/10: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 48800 W: 13044 L: 12835 D: 22921 Ptnml(0-2): 229, 5457, 12863, 5578, 273 https://tests.stockfishchess.org/tests/view/67862dae460e2910c51de7c9 Passed LTC 40/40: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 141296 W: 36110 L: 36014 D: 69172 Ptnml(0-2): 222, 14350, 41440, 14382, 254 https://tests.stockfishchess.org/tests/view/678799903b8f206a2696b6f8 Passed STC 80/8: LLR: 2.99 (-2.94,2.94) <-1.75,0.25> Total: 155120 W: 41442 L: 41346 D: 72332 Ptnml(0-2): 953, 17232, 41102, 17312, 961 https://tests.stockfishchess.org/tests/view/678aca4dc00c743bc9e9fc47 Passed LTC 80/60: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 93950 W: 24042 L: 23904 D: 46004 Ptnml(0-2): 80, 9020, 28627, 9178, 70 https://tests.stockfishchess.org/tests/view/678af705c00c743bc9e9fe94 closes https://github.com/official-stockfish/Stockfish/pull/5790 No functional change --- src/timeman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index d0b0d0a94..2aaf96680 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -125,7 +125,7 @@ void TimeManagement::init(Search::LimitsType& limits, else { optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / timeLeft); - maxScale = std::min(6.3, 1.5 + 0.11 * mtg); + maxScale = 1.3 + 0.11 * mtg; } // Limit the maximum possible time for this move From b392ac76db4d2334746a3621dd0c851679a08ca8 Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Sat, 18 Jan 2025 00:45:10 +0100 Subject: [PATCH 20/24] Increase history bonus of TT moves Passed STC: https://tests.stockfishchess.org/tests/view/678807653b8f206a2696b78b LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 42208 W: 11113 L: 10783 D: 20312 Ptnml(0-2): 148, 4919, 10651, 5227, 159 Passed LTC: https://tests.stockfishchess.org/tests/view/6788a8463b8f206a2696b956 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 122886 W: 31454 L: 30952 D: 60480 Ptnml(0-2): 105, 13567, 33619, 14025, 127 closes https://github.com/official-stockfish/Stockfish/pull/5791 Bench: 1760081 --- src/search.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a080daf30..5b4415462 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -119,7 +119,8 @@ void update_all_stats(const Position& pos, Square prevSq, ValueList& quietsSearched, ValueList& capturesSearched, - Depth depth); + Depth depth, + bool isTTMove); } // namespace @@ -1380,7 +1381,8 @@ moves_loop: // When in check, search starts here // If there is a move that produces search value greater than alpha, // we update the stats of searched moves. else if (bestMove) - update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth); + update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth, + bestMove == ttData.move); // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) @@ -1799,13 +1801,14 @@ void update_all_stats(const Position& pos, Square prevSq, ValueList& quietsSearched, ValueList& capturesSearched, - Depth depth) { + Depth depth, + bool isTTMove) { CapturePieceToHistory& captureHistory = workerThread.captureHistory; Piece moved_piece = pos.moved_piece(bestMove); PieceType captured; - int bonus = stat_bonus(depth); + int bonus = stat_bonus(depth) + 300 * isTTMove; int malus = stat_malus(depth); if (!pos.capture_stage(bestMove)) From 7701d0b3c4600423bfaf42f582f32e478fa88532 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 18 Jan 2025 03:23:50 +0300 Subject: [PATCH 21/24] Introduce one more continuation history This one is counter counter counter history - with really low update value and divided by 3 in movepicker unlike the other ones. Passed STC: https://tests.stockfishchess.org/tests/view/67861495460e2910c51de720 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 44352 W: 11699 L: 11370 D: 21283 Ptnml(0-2): 156, 5098, 11361, 5383, 178 Passed LTC: https://tests.stockfishchess.org/tests/view/6786e89e3b8f206a2696b646 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 432660 W: 110355 L: 109207 D: 213098 Ptnml(0-2): 381, 48214, 118039, 49268, 428 closes https://github.com/official-stockfish/Stockfish/pull/5792 Bench: 1491837 --- src/movepick.cpp | 1 + src/search.cpp | 13 +++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index bee5ef5b7..c762e7e45 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -162,6 +162,7 @@ void MovePicker::score() { m.value += (*continuationHistory[1])[pc][to]; m.value += (*continuationHistory[2])[pc][to]; m.value += (*continuationHistory[3])[pc][to]; + m.value += (*continuationHistory[4])[pc][to] / 3; m.value += (*continuationHistory[5])[pc][to]; // bonus for checks diff --git a/src/search.cpp b/src/search.cpp index 5b4415462..983f387a8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -923,12 +923,9 @@ moves_loop: // When in check, search starts here && !is_decisive(beta) && is_valid(ttData.value) && !is_decisive(ttData.value)) return probCutBeta; - const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, - (ss - 2)->continuationHistory, - (ss - 3)->continuationHistory, - (ss - 4)->continuationHistory, - nullptr, - (ss - 6)->continuationHistory}; + const PieceToHistory* contHist[] = { + (ss - 1)->continuationHistory, (ss - 2)->continuationHistory, (ss - 3)->continuationHistory, + (ss - 4)->continuationHistory, (ss - 5)->continuationHistory, (ss - 6)->continuationHistory}; MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->lowPlyHistory, @@ -1844,8 +1841,8 @@ 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, 1025}, {2, 621}, {3, 325}, {4, 512}, {6, 534}}}; + static constexpr std::array conthist_bonuses = { + {{1, 1025}, {2, 621}, {3, 325}, {4, 512}, {5, 122}, {6, 534}}}; for (const auto [i, weight] : conthist_bonuses) { From 329c267e253e6119a43066fa3481e9c509e8c34a Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 17 Jan 2025 21:18:48 -0800 Subject: [PATCH 22/24] Optimize find_nnz() by reducing the size of lookup_indices https://tests.stockfishchess.org/tests/view/67896b688082388fa0cbfdee LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 452800 W: 118213 L: 117300 D: 217287 Ptnml(0-2): 1638, 50255, 121864, 50842, 1801 It's faster to shrink lookup_indices[] to 8 bit and zero extend to 16 bit using _mm_cvtepu8_epi16() than to read the larger 16 bit version. I suspect that having the constants available at compile time isn't too valuable and can be simplified back to generating at initialization time since this version also almost passed. https://tests.stockfishchess.org/tests/view/67863057460e2910c51de7e0 I will try that as a follow up. closes https://github.com/official-stockfish/Stockfish/pull/5793 No functional change --- .../layers/affine_transform_sparse_input.h | 110 ++++++++++++++++-- 1 file changed, 98 insertions(+), 12 deletions(-) diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index cbeb507f0..248e19dd0 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -38,17 +38,99 @@ namespace Stockfish::Eval::NNUE::Layers { #if (USE_SSSE3 | (USE_NEON >= 8)) -alignas(CacheLineSize) static inline const - std::array, 256> lookup_indices = []() { - std::array, 256> v{}; - for (unsigned i = 0; i < 256; ++i) - { - std::uint64_t j = i, k = 0; - while (j) - v[i][k++] = pop_lsb(j); - } - return v; - }(); + + #if (USE_SSE41) +alignas(CacheLineSize) static constexpr std::uint8_t + #else +alignas(CacheLineSize) static constexpr std::uint16_t + #endif + lookup_indices[256][8] = { + {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 0, 0, 0, 0, 0, 0}, {2, 0, 0, 0, 0, 0, 0, 0}, {0, 2, 0, 0, 0, 0, 0, 0}, + {1, 2, 0, 0, 0, 0, 0, 0}, {0, 1, 2, 0, 0, 0, 0, 0}, {3, 0, 0, 0, 0, 0, 0, 0}, + {0, 3, 0, 0, 0, 0, 0, 0}, {1, 3, 0, 0, 0, 0, 0, 0}, {0, 1, 3, 0, 0, 0, 0, 0}, + {2, 3, 0, 0, 0, 0, 0, 0}, {0, 2, 3, 0, 0, 0, 0, 0}, {1, 2, 3, 0, 0, 0, 0, 0}, + {0, 1, 2, 3, 0, 0, 0, 0}, {4, 0, 0, 0, 0, 0, 0, 0}, {0, 4, 0, 0, 0, 0, 0, 0}, + {1, 4, 0, 0, 0, 0, 0, 0}, {0, 1, 4, 0, 0, 0, 0, 0}, {2, 4, 0, 0, 0, 0, 0, 0}, + {0, 2, 4, 0, 0, 0, 0, 0}, {1, 2, 4, 0, 0, 0, 0, 0}, {0, 1, 2, 4, 0, 0, 0, 0}, + {3, 4, 0, 0, 0, 0, 0, 0}, {0, 3, 4, 0, 0, 0, 0, 0}, {1, 3, 4, 0, 0, 0, 0, 0}, + {0, 1, 3, 4, 0, 0, 0, 0}, {2, 3, 4, 0, 0, 0, 0, 0}, {0, 2, 3, 4, 0, 0, 0, 0}, + {1, 2, 3, 4, 0, 0, 0, 0}, {0, 1, 2, 3, 4, 0, 0, 0}, {5, 0, 0, 0, 0, 0, 0, 0}, + {0, 5, 0, 0, 0, 0, 0, 0}, {1, 5, 0, 0, 0, 0, 0, 0}, {0, 1, 5, 0, 0, 0, 0, 0}, + {2, 5, 0, 0, 0, 0, 0, 0}, {0, 2, 5, 0, 0, 0, 0, 0}, {1, 2, 5, 0, 0, 0, 0, 0}, + {0, 1, 2, 5, 0, 0, 0, 0}, {3, 5, 0, 0, 0, 0, 0, 0}, {0, 3, 5, 0, 0, 0, 0, 0}, + {1, 3, 5, 0, 0, 0, 0, 0}, {0, 1, 3, 5, 0, 0, 0, 0}, {2, 3, 5, 0, 0, 0, 0, 0}, + {0, 2, 3, 5, 0, 0, 0, 0}, {1, 2, 3, 5, 0, 0, 0, 0}, {0, 1, 2, 3, 5, 0, 0, 0}, + {4, 5, 0, 0, 0, 0, 0, 0}, {0, 4, 5, 0, 0, 0, 0, 0}, {1, 4, 5, 0, 0, 0, 0, 0}, + {0, 1, 4, 5, 0, 0, 0, 0}, {2, 4, 5, 0, 0, 0, 0, 0}, {0, 2, 4, 5, 0, 0, 0, 0}, + {1, 2, 4, 5, 0, 0, 0, 0}, {0, 1, 2, 4, 5, 0, 0, 0}, {3, 4, 5, 0, 0, 0, 0, 0}, + {0, 3, 4, 5, 0, 0, 0, 0}, {1, 3, 4, 5, 0, 0, 0, 0}, {0, 1, 3, 4, 5, 0, 0, 0}, + {2, 3, 4, 5, 0, 0, 0, 0}, {0, 2, 3, 4, 5, 0, 0, 0}, {1, 2, 3, 4, 5, 0, 0, 0}, + {0, 1, 2, 3, 4, 5, 0, 0}, {6, 0, 0, 0, 0, 0, 0, 0}, {0, 6, 0, 0, 0, 0, 0, 0}, + {1, 6, 0, 0, 0, 0, 0, 0}, {0, 1, 6, 0, 0, 0, 0, 0}, {2, 6, 0, 0, 0, 0, 0, 0}, + {0, 2, 6, 0, 0, 0, 0, 0}, {1, 2, 6, 0, 0, 0, 0, 0}, {0, 1, 2, 6, 0, 0, 0, 0}, + {3, 6, 0, 0, 0, 0, 0, 0}, {0, 3, 6, 0, 0, 0, 0, 0}, {1, 3, 6, 0, 0, 0, 0, 0}, + {0, 1, 3, 6, 0, 0, 0, 0}, {2, 3, 6, 0, 0, 0, 0, 0}, {0, 2, 3, 6, 0, 0, 0, 0}, + {1, 2, 3, 6, 0, 0, 0, 0}, {0, 1, 2, 3, 6, 0, 0, 0}, {4, 6, 0, 0, 0, 0, 0, 0}, + {0, 4, 6, 0, 0, 0, 0, 0}, {1, 4, 6, 0, 0, 0, 0, 0}, {0, 1, 4, 6, 0, 0, 0, 0}, + {2, 4, 6, 0, 0, 0, 0, 0}, {0, 2, 4, 6, 0, 0, 0, 0}, {1, 2, 4, 6, 0, 0, 0, 0}, + {0, 1, 2, 4, 6, 0, 0, 0}, {3, 4, 6, 0, 0, 0, 0, 0}, {0, 3, 4, 6, 0, 0, 0, 0}, + {1, 3, 4, 6, 0, 0, 0, 0}, {0, 1, 3, 4, 6, 0, 0, 0}, {2, 3, 4, 6, 0, 0, 0, 0}, + {0, 2, 3, 4, 6, 0, 0, 0}, {1, 2, 3, 4, 6, 0, 0, 0}, {0, 1, 2, 3, 4, 6, 0, 0}, + {5, 6, 0, 0, 0, 0, 0, 0}, {0, 5, 6, 0, 0, 0, 0, 0}, {1, 5, 6, 0, 0, 0, 0, 0}, + {0, 1, 5, 6, 0, 0, 0, 0}, {2, 5, 6, 0, 0, 0, 0, 0}, {0, 2, 5, 6, 0, 0, 0, 0}, + {1, 2, 5, 6, 0, 0, 0, 0}, {0, 1, 2, 5, 6, 0, 0, 0}, {3, 5, 6, 0, 0, 0, 0, 0}, + {0, 3, 5, 6, 0, 0, 0, 0}, {1, 3, 5, 6, 0, 0, 0, 0}, {0, 1, 3, 5, 6, 0, 0, 0}, + {2, 3, 5, 6, 0, 0, 0, 0}, {0, 2, 3, 5, 6, 0, 0, 0}, {1, 2, 3, 5, 6, 0, 0, 0}, + {0, 1, 2, 3, 5, 6, 0, 0}, {4, 5, 6, 0, 0, 0, 0, 0}, {0, 4, 5, 6, 0, 0, 0, 0}, + {1, 4, 5, 6, 0, 0, 0, 0}, {0, 1, 4, 5, 6, 0, 0, 0}, {2, 4, 5, 6, 0, 0, 0, 0}, + {0, 2, 4, 5, 6, 0, 0, 0}, {1, 2, 4, 5, 6, 0, 0, 0}, {0, 1, 2, 4, 5, 6, 0, 0}, + {3, 4, 5, 6, 0, 0, 0, 0}, {0, 3, 4, 5, 6, 0, 0, 0}, {1, 3, 4, 5, 6, 0, 0, 0}, + {0, 1, 3, 4, 5, 6, 0, 0}, {2, 3, 4, 5, 6, 0, 0, 0}, {0, 2, 3, 4, 5, 6, 0, 0}, + {1, 2, 3, 4, 5, 6, 0, 0}, {0, 1, 2, 3, 4, 5, 6, 0}, {7, 0, 0, 0, 0, 0, 0, 0}, + {0, 7, 0, 0, 0, 0, 0, 0}, {1, 7, 0, 0, 0, 0, 0, 0}, {0, 1, 7, 0, 0, 0, 0, 0}, + {2, 7, 0, 0, 0, 0, 0, 0}, {0, 2, 7, 0, 0, 0, 0, 0}, {1, 2, 7, 0, 0, 0, 0, 0}, + {0, 1, 2, 7, 0, 0, 0, 0}, {3, 7, 0, 0, 0, 0, 0, 0}, {0, 3, 7, 0, 0, 0, 0, 0}, + {1, 3, 7, 0, 0, 0, 0, 0}, {0, 1, 3, 7, 0, 0, 0, 0}, {2, 3, 7, 0, 0, 0, 0, 0}, + {0, 2, 3, 7, 0, 0, 0, 0}, {1, 2, 3, 7, 0, 0, 0, 0}, {0, 1, 2, 3, 7, 0, 0, 0}, + {4, 7, 0, 0, 0, 0, 0, 0}, {0, 4, 7, 0, 0, 0, 0, 0}, {1, 4, 7, 0, 0, 0, 0, 0}, + {0, 1, 4, 7, 0, 0, 0, 0}, {2, 4, 7, 0, 0, 0, 0, 0}, {0, 2, 4, 7, 0, 0, 0, 0}, + {1, 2, 4, 7, 0, 0, 0, 0}, {0, 1, 2, 4, 7, 0, 0, 0}, {3, 4, 7, 0, 0, 0, 0, 0}, + {0, 3, 4, 7, 0, 0, 0, 0}, {1, 3, 4, 7, 0, 0, 0, 0}, {0, 1, 3, 4, 7, 0, 0, 0}, + {2, 3, 4, 7, 0, 0, 0, 0}, {0, 2, 3, 4, 7, 0, 0, 0}, {1, 2, 3, 4, 7, 0, 0, 0}, + {0, 1, 2, 3, 4, 7, 0, 0}, {5, 7, 0, 0, 0, 0, 0, 0}, {0, 5, 7, 0, 0, 0, 0, 0}, + {1, 5, 7, 0, 0, 0, 0, 0}, {0, 1, 5, 7, 0, 0, 0, 0}, {2, 5, 7, 0, 0, 0, 0, 0}, + {0, 2, 5, 7, 0, 0, 0, 0}, {1, 2, 5, 7, 0, 0, 0, 0}, {0, 1, 2, 5, 7, 0, 0, 0}, + {3, 5, 7, 0, 0, 0, 0, 0}, {0, 3, 5, 7, 0, 0, 0, 0}, {1, 3, 5, 7, 0, 0, 0, 0}, + {0, 1, 3, 5, 7, 0, 0, 0}, {2, 3, 5, 7, 0, 0, 0, 0}, {0, 2, 3, 5, 7, 0, 0, 0}, + {1, 2, 3, 5, 7, 0, 0, 0}, {0, 1, 2, 3, 5, 7, 0, 0}, {4, 5, 7, 0, 0, 0, 0, 0}, + {0, 4, 5, 7, 0, 0, 0, 0}, {1, 4, 5, 7, 0, 0, 0, 0}, {0, 1, 4, 5, 7, 0, 0, 0}, + {2, 4, 5, 7, 0, 0, 0, 0}, {0, 2, 4, 5, 7, 0, 0, 0}, {1, 2, 4, 5, 7, 0, 0, 0}, + {0, 1, 2, 4, 5, 7, 0, 0}, {3, 4, 5, 7, 0, 0, 0, 0}, {0, 3, 4, 5, 7, 0, 0, 0}, + {1, 3, 4, 5, 7, 0, 0, 0}, {0, 1, 3, 4, 5, 7, 0, 0}, {2, 3, 4, 5, 7, 0, 0, 0}, + {0, 2, 3, 4, 5, 7, 0, 0}, {1, 2, 3, 4, 5, 7, 0, 0}, {0, 1, 2, 3, 4, 5, 7, 0}, + {6, 7, 0, 0, 0, 0, 0, 0}, {0, 6, 7, 0, 0, 0, 0, 0}, {1, 6, 7, 0, 0, 0, 0, 0}, + {0, 1, 6, 7, 0, 0, 0, 0}, {2, 6, 7, 0, 0, 0, 0, 0}, {0, 2, 6, 7, 0, 0, 0, 0}, + {1, 2, 6, 7, 0, 0, 0, 0}, {0, 1, 2, 6, 7, 0, 0, 0}, {3, 6, 7, 0, 0, 0, 0, 0}, + {0, 3, 6, 7, 0, 0, 0, 0}, {1, 3, 6, 7, 0, 0, 0, 0}, {0, 1, 3, 6, 7, 0, 0, 0}, + {2, 3, 6, 7, 0, 0, 0, 0}, {0, 2, 3, 6, 7, 0, 0, 0}, {1, 2, 3, 6, 7, 0, 0, 0}, + {0, 1, 2, 3, 6, 7, 0, 0}, {4, 6, 7, 0, 0, 0, 0, 0}, {0, 4, 6, 7, 0, 0, 0, 0}, + {1, 4, 6, 7, 0, 0, 0, 0}, {0, 1, 4, 6, 7, 0, 0, 0}, {2, 4, 6, 7, 0, 0, 0, 0}, + {0, 2, 4, 6, 7, 0, 0, 0}, {1, 2, 4, 6, 7, 0, 0, 0}, {0, 1, 2, 4, 6, 7, 0, 0}, + {3, 4, 6, 7, 0, 0, 0, 0}, {0, 3, 4, 6, 7, 0, 0, 0}, {1, 3, 4, 6, 7, 0, 0, 0}, + {0, 1, 3, 4, 6, 7, 0, 0}, {2, 3, 4, 6, 7, 0, 0, 0}, {0, 2, 3, 4, 6, 7, 0, 0}, + {1, 2, 3, 4, 6, 7, 0, 0}, {0, 1, 2, 3, 4, 6, 7, 0}, {5, 6, 7, 0, 0, 0, 0, 0}, + {0, 5, 6, 7, 0, 0, 0, 0}, {1, 5, 6, 7, 0, 0, 0, 0}, {0, 1, 5, 6, 7, 0, 0, 0}, + {2, 5, 6, 7, 0, 0, 0, 0}, {0, 2, 5, 6, 7, 0, 0, 0}, {1, 2, 5, 6, 7, 0, 0, 0}, + {0, 1, 2, 5, 6, 7, 0, 0}, {3, 5, 6, 7, 0, 0, 0, 0}, {0, 3, 5, 6, 7, 0, 0, 0}, + {1, 3, 5, 6, 7, 0, 0, 0}, {0, 1, 3, 5, 6, 7, 0, 0}, {2, 3, 5, 6, 7, 0, 0, 0}, + {0, 2, 3, 5, 6, 7, 0, 0}, {1, 2, 3, 5, 6, 7, 0, 0}, {0, 1, 2, 3, 5, 6, 7, 0}, + {4, 5, 6, 7, 0, 0, 0, 0}, {0, 4, 5, 6, 7, 0, 0, 0}, {1, 4, 5, 6, 7, 0, 0, 0}, + {0, 1, 4, 5, 6, 7, 0, 0}, {2, 4, 5, 6, 7, 0, 0, 0}, {0, 2, 4, 5, 6, 7, 0, 0}, + {1, 2, 4, 5, 6, 7, 0, 0}, {0, 1, 2, 4, 5, 6, 7, 0}, {3, 4, 5, 6, 7, 0, 0, 0}, + {0, 3, 4, 5, 6, 7, 0, 0}, {1, 3, 4, 5, 6, 7, 0, 0}, {0, 1, 3, 4, 5, 6, 7, 0}, + {2, 3, 4, 5, 6, 7, 0, 0}, {0, 2, 3, 4, 5, 6, 7, 0}, {1, 2, 3, 4, 5, 6, 7, 0}, + {0, 1, 2, 3, 4, 5, 6, 7}}; // Find indices of nonzero numbers in an int32_t array template @@ -74,7 +156,11 @@ void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_ou using vec128_t = __m128i; #define vec128_zero _mm_setzero_si128() #define vec128_set_16(a) _mm_set1_epi16(a) - #define vec128_load(a) _mm_load_si128(a) + #if (USE_SSE41) + #define vec128_load(a) _mm_cvtepu8_epi16(_mm_loadl_epi64(a)) + #else + #define vec128_load(a) _mm_load_si128(a) + #endif #define vec128_storeu(a, b) _mm_storeu_si128(a, b) #define vec128_add(a, b) _mm_add_epi16(a, b) #elif defined(USE_NEON) From c94bcf62e4dac6b92455f2701fda794883044b8b Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 18 Jan 2025 13:38:45 +0300 Subject: [PATCH 23/24] Moving up the if position is or has been on the PV reduction Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 29664 W: 7880 L: 7570 D: 14214 Ptnml(0-2): 93, 3487, 7390, 3741, 121 https://tests.stockfishchess.org/tests/view/678ac957c00c743bc9e9fc3f Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 81354 W: 20903 L: 20487 D: 39964 Ptnml(0-2): 66, 9003, 22123, 9419, 66 https://tests.stockfishchess.org/tests/view/678ad359c00c743bc9e9fcfa closes https://github.com/official-stockfish/Stockfish/pull/5794 Bench: 1414638 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 983f387a8..249ac56bc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -978,6 +978,10 @@ moves_loop: // When in check, search starts here Depth r = reduction(improving, depth, moveCount, delta); + // Decrease reduction if position is or has been on the PV (~7 Elo) + if (ss->ttPv) + r -= 1037 + (ttData.value > alpha) * 965 + (ttData.depth >= depth) * 960; + // Step 14. Pruning at shallow depth (~120 Elo). // Depth conditions are important for mate finding. if (!rootNode && pos.non_pawn_material(us) && !is_loss(bestValue)) @@ -1144,10 +1148,6 @@ moves_loop: // When in check, search starts here // so changing them or adding conditions that are similar requires // tests at these types of time controls. - // Decrease reduction if position is or has been on the PV (~7 Elo) - if (ss->ttPv) - r -= 1037 + (ttData.value > alpha) * 965 + (ttData.depth >= depth) * 960; - // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) if (PvNode) r -= 1018; From 738ac2a10025ca58198e3d2d7f0bc70d83c2cb7f Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Mon, 13 Jan 2025 15:22:00 -0800 Subject: [PATCH 24/24] tuned TM values Tuned 70k games at 240+2.4 th 2: https://tests.stockfishchess.org/tests/view/6783b1b16ddf09c0b4b703f5 Failed STC: LLR: -2.93 (-2.94,2.94) <0.00,2.00> Total: 491872 W: 128260 L: 127804 D: 235808 Ptnml(0-2): 1579, 55449, 131572, 55609, 1727 https://tests.stockfishchess.org/tests/view/6785a045460e2910c51de4b8 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 154824 W: 39315 L: 38874 D: 76635 Ptnml(0-2): 110, 15809, 45147, 16222, 124 https://tests.stockfishchess.org/tests/view/678ac722c00c743bc9e9fc35 Passed VLTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 77404 W: 19825 L: 19452 D: 38127 Ptnml(0-2): 18, 7262, 23765, 7643, 14 https://tests.stockfishchess.org/tests/view/678b2a98c00c743bc9ea048c closes https://github.com/official-stockfish/Stockfish/pull/5796 No functional change --- src/search.cpp | 22 ++++++++++++---------- src/timeman.cpp | 31 +++++++++++++++++-------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 249ac56bc..1d2604c26 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -446,17 +446,19 @@ void Search::Worker::iterative_deepening() { // Do we have time for the next iteration? Can we stop searching now? if (limits.use_time_management() && !threads.stop && !mainThread->stopOnPonderhit) { - int nodesEffort = rootMoves[0].effort * 100 / std::max(size_t(1), size_t(nodes)); + int nodesEffort = rootMoves[0].effort * 100000 / std::max(size_t(1), size_t(nodes)); - double fallingEval = (11 + 2 * (mainThread->bestPreviousAverageScore - bestValue) - + (mainThread->iterValue[iterIdx] - bestValue)) - / 100.0; - fallingEval = std::clamp(fallingEval, 0.580, 1.667); + double fallingEval = + (11.396 + 2.035 * (mainThread->bestPreviousAverageScore - bestValue) + + 0.968 * (mainThread->iterValue[iterIdx] - bestValue)) + / 100.0; + fallingEval = std::clamp(fallingEval, 0.5786, 1.6752); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.495 : 0.687; - double reduction = (1.48 + mainThread->previousTimeReduction) / (2.17 * timeReduction); - double bestMoveInstability = 1 + 1.88 * totBestMoveChanges / threads.size(); + timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.4857 : 0.7046; + double reduction = + (1.4540 + mainThread->previousTimeReduction) / (2.1593 * timeReduction); + double bestMoveInstability = 0.9929 + 1.8519 * totBestMoveChanges / threads.size(); double totalTime = mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability; @@ -467,7 +469,7 @@ void Search::Worker::iterative_deepening() { auto elapsedTime = elapsed(); - if (completedDepth >= 10 && nodesEffort >= 97 && elapsedTime > totalTime * 0.739 + if (completedDepth >= 10 && nodesEffort >= 97056 && elapsedTime > totalTime * 0.6540 && !mainThread->ponder) threads.stop = true; @@ -482,7 +484,7 @@ void Search::Worker::iterative_deepening() { threads.stop = true; } else - threads.increaseDepth = mainThread->ponder || elapsedTime <= totalTime * 0.506; + threads.increaseDepth = mainThread->ponder || elapsedTime <= totalTime * 0.5138; } mainThread->iterValue[iterIdx] = bestValue; diff --git a/src/timeman.cpp b/src/timeman.cpp index 2aaf96680..d073a84a9 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -88,17 +88,19 @@ void TimeManagement::init(Search::LimitsType& limits, const TimePoint scaledInc = limits.inc[us] / scaleFactor; // Maximum move horizon of 50 moves - int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; + int centiMTG = limits.movestogo ? std::min(limits.movestogo * 100, 5000) : 5051; // If less than one second, gradually reduce mtg - if (scaledTime < 1000 && double(mtg) / scaledInc > 0.05) + if (scaledTime < 1000 && double(centiMTG) / scaledInc > 5.051) { - mtg = scaledTime * 0.05; + centiMTG = scaledTime * 5.051; } // Make sure timeLeft is > 0 since we may use it as a divisor - TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) - - moveOverhead * (2 + mtg)); + TimePoint timeLeft = + std::max(TimePoint(1), + limits.time[us] + + (limits.inc[us] * (centiMTG - 100) - moveOverhead * (200 + centiMTG)) / 100); // x basetime (+ z increment) // If there is a healthy increment, timeLeft can exceed the actual available @@ -107,31 +109,32 @@ void TimeManagement::init(Search::LimitsType& limits, { // Extra time according to timeLeft if (originalTimeAdjust < 0) - originalTimeAdjust = 0.3285 * std::log10(timeLeft) - 0.4830; + originalTimeAdjust = 0.3128 * std::log10(timeLeft) - 0.4354; // Calculate time constants based on current time left. double logTimeInSec = std::log10(scaledTime / 1000.0); - double optConstant = std::min(0.00308 + 0.000319 * logTimeInSec, 0.00506); - double maxConstant = std::max(3.39 + 3.01 * logTimeInSec, 2.93); + double optConstant = std::min(0.0032116 + 0.000321123 * logTimeInSec, 0.00508017); + double maxConstant = std::max(3.3977 + 3.03950 * logTimeInSec, 2.94761); - optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant, - 0.213 * limits.time[us] / timeLeft) + optScale = std::min(0.0121431 + std::pow(ply + 2.94693, 0.461073) * optConstant, + 0.213035 * limits.time[us] / timeLeft) * originalTimeAdjust; - maxScale = std::min(6.64, maxConstant + ply / 12.0); + maxScale = std::min(6.67704, maxConstant + ply / 11.9847); } // x moves in y seconds (+ z increment) else { - optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / timeLeft); - maxScale = 1.3 + 0.11 * mtg; + optScale = + std::min((0.88 + ply / 116.4) / (centiMTG / 100.0), 0.88 * limits.time[us] / timeLeft); + maxScale = 1.3 + 0.11 * (centiMTG / 100.0); } // Limit the maximum possible time for this move optimumTime = TimePoint(optScale * timeLeft); maximumTime = - TimePoint(std::min(0.825 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; + TimePoint(std::min(0.825179 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; if (options["Ponder"]) optimumTime += optimumTime / 4;