From 2b4926e091cb423d34447b9f995578b4da74906b Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Mon, 31 Mar 2025 21:33:40 -0700 Subject: [PATCH 001/107] Simplify TT Move History Part 1 passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 195552 W: 50394 L: 50349 D: 94809 Ptnml(0-2): 581, 23222, 50122, 23273, 578 https://tests.stockfishchess.org/tests/view/67eb6ea831d7cf8afdc44c30 Part 2 passed Non-regression STC: LLR: 2.92 (-2.94,2.94) <-1.75,0.25> Total: 181664 W: 46786 L: 46727 D: 88151 Ptnml(0-2): 517, 21403, 46974, 21380, 558 https://tests.stockfishchess.org/tests/view/67eb6f3331d7cf8afdc44c33 Passed Non-regression LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 155454 W: 39496 L: 39412 D: 76546 Ptnml(0-2): 77, 16950, 43580, 17052, 68 https://tests.stockfishchess.org/tests/view/67eee76531d7cf8afdc45358 Passed Non-regression VLTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 118266 W: 30263 L: 30148 D: 57855 Ptnml(0-2): 11, 11844, 35309, 11957, 12 https://tests.stockfishchess.org/tests/view/67f2414a31d7cf8afdc45760 closes https://github.com/official-stockfish/Stockfish/pull/5987 Bench: 1792850 --- src/history.h | 2 +- src/search.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/history.h b/src/history.h index f2ef7661f..259d6eefb 100644 --- a/src/history.h +++ b/src/history.h @@ -166,7 +166,7 @@ struct CorrHistTypedef { template using CorrectionHistory = typename Detail::CorrHistTypedef::type; -using TTMoveHistory = Stats; +using TTMoveHistory = StatsEntry; } // namespace Stockfish diff --git a/src/search.cpp b/src/search.cpp index 3f24f86c5..0fecbce65 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -579,7 +579,7 @@ void Search::Worker::clear() { minorPieceCorrectionHistory.fill(0); nonPawnCorrectionHistory.fill(0); - ttMoveHistory.fill(0); + ttMoveHistory = 0; for (auto& to : continuationCorrectionHistory) for (auto& h : to) @@ -1151,10 +1151,10 @@ moves_loop: // When in check, search starts here if (value < singularBeta) { - int corrValAdj1 = std::abs(correctionValue) / 248873; - int corrValAdj2 = std::abs(correctionValue) / 255331; - int doubleMargin = 262 * PvNode - 188 * !ttCapture - corrValAdj1 - - ttMoveHistory[pawn_structure_index(pos)][us] / 128; + int corrValAdj1 = std::abs(correctionValue) / 248873; + int corrValAdj2 = std::abs(correctionValue) / 255331; + int doubleMargin = + 262 * PvNode - 188 * !ttCapture - corrValAdj1 - ttMoveHistory / 128; int tripleMargin = 88 + 265 * PvNode - 256 * !ttCapture + 93 * ss->ttPv - corrValAdj2; @@ -1450,8 +1450,8 @@ moves_loop: // When in check, search starts here bestMove == ttData.move, moveCount); if (!PvNode) { - int bonus = (ttData.move == move) ? 800 : -600 * moveCount; - ttMoveHistory[pawn_structure_index(pos)][us] << bonus; + int bonus = (ttData.move == move) ? 800 : -870; + ttMoveHistory << bonus; } } From 698c069bba69912d576075bf3d63e32ac5032d9d Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Mon, 14 Apr 2025 15:00:39 +0200 Subject: [PATCH 002/107] Move node increment inside do_move function Move node increment inside do_move function so that we can centralize the increment into a single point. closes https://github.com/official-stockfish/Stockfish/pull/5989 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 0fecbce65..fcbac3078 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -556,6 +556,7 @@ void Search::Worker::do_move(Position& pos, const Move move, StateInfo& st) { void Search::Worker::do_move(Position& pos, const Move move, StateInfo& st, const bool givesCheck) { DirtyPiece dp = pos.do_move(move, st, givesCheck, &tt); + nodes.fetch_add(1, std::memory_order_relaxed); accumulatorStack.push(dp); } @@ -940,7 +941,6 @@ Value Search::Worker::search( movedPiece = pos.moved_piece(move); do_move(pos, move, st); - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); ss->currentMove = move; ss->isTTMove = (move == ttData.move); @@ -1193,7 +1193,6 @@ moves_loop: // When in check, search starts here // Step 16. Make the move do_move(pos, move, st, givesCheck); - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); // Add extension to new depth newDepth += extension; @@ -1711,7 +1710,6 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) Piece movedPiece = pos.moved_piece(move); do_move(pos, move, st, givesCheck); - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); // Update the current move ss->currentMove = move; From 3d18ad719b2e7103e27936561f8276fbed9a354b Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Wed, 2 Apr 2025 11:57:06 -0700 Subject: [PATCH 003/107] Skip 5th continuation history Passed simplification STC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 70208 W: 18098 L: 17914 D: 34196 Ptnml(0-2): 199, 8300, 17907, 8514, 184 https://tests.stockfishchess.org/tests/view/67ed889b31d7cf8afdc451cb Passed simplification LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 485004 W: 122703 L: 122956 D: 239345 Ptnml(0-2): 288, 53162, 135805, 53009, 238 https://tests.stockfishchess.org/tests/view/67ee810231d7cf8afdc452ea closes https://github.com/official-stockfish/Stockfish/pull/5992 Bench: 1715901 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index fcbac3078..92998b026 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1917,7 +1917,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, 1103}, {2, 659}, {3, 323}, {4, 533}, {5, 121}, {6, 474}}}; + {{1, 1103}, {2, 659}, {3, 323}, {4, 533}, {6, 474}}}; for (const auto [i, weight] : conthist_bonuses) { From f9459e4c8e5b595db0fa76c3ca7b475bf03858a9 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 31 Mar 2025 08:15:10 +0200 Subject: [PATCH 004/107] Use matching llvm-profdata when using multiple clang compilers in parallel, it is necessary to use a matching llvm-profdata, as the profile data format may change between versions. To use the proper llvm-profdata binary, the one in the same directory as the compiler is used. This allows for code like: ```bash echo "Compiling clang" for comp in clang++-11 clang++-12 clang++-13 clang++-14 clang++-15 clang++-16 clang++-17 clang++-18 clang++-19 clang++-20 do make -j profile-build CXX=$comp COMP=clang >& out.compile.$comp mv stockfish stockfish.$comp done ``` after installing the required versions with the automatic installation script (https://apt.llvm.org/) closes https://github.com/official-stockfish/Stockfish/pull/5958 No functional change --- src/Makefile | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 76b94785e..17badefde 100644 --- a/src/Makefile +++ b/src/Makefile @@ -567,6 +567,15 @@ ifeq ($(COMP),ndk) LDFLAGS += -static-libstdc++ -pie -lm -latomic endif +# llvm-profdata must be version compatible with the specified CXX (be it clang, or the gcc alias) +# make -j profile-build CXX=clang++-20 COMP=clang +# Locate the version in the same directory as the compiler used, +# with fallback to a generic one if it can't be located + LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX))))llvm-profdata +ifeq ($(wildcard $(LLVM_PROFDATA)),) + LLVM_PROFDATA := llvm-profdata +endif + ifeq ($(comp),icx) profile_make = icx-profile-make profile_use = icx-profile-use @@ -1081,7 +1090,7 @@ clang-profile-make: all clang-profile-use: - $(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw + $(XCRUN) $(LLVM_PROFDATA) merge -output=stockfish.profdata *.profraw $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-use=stockfish.profdata' \ EXTRALDFLAGS='-fprofile-use ' \ From f273eea71fc8ec030d0f4279c03c4e1fc2af4584 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 11 Apr 2025 20:58:11 -0700 Subject: [PATCH 005/107] Remove non-functional accumulator reset Passed Non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 219360 W: 56600 L: 56583 D: 106177 Ptnml(0-2): 582, 23419, 61620, 23518, 541 https://tests.stockfishchess.org/tests/view/67fad20dcd501869c669780f closes https://github.com/official-stockfish/Stockfish/pull/5986 no functional change --- src/evaluate.cpp | 2 -- src/nnue/nnue_accumulator.cpp | 65 ++++++++++++++--------------------- src/nnue/nnue_accumulator.h | 13 +++---- src/nnue/nnue_misc.cpp | 5 ++- src/search.cpp | 2 +- 5 files changed, 33 insertions(+), 54 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 92b03af37..23f4b8c2b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -101,8 +101,6 @@ std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { Eval::NNUE::AccumulatorStack accumulators; auto caches = std::make_unique(networks); - accumulators.reset(pos, networks, *caches); - std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); ss << '\n' << NNUE::trace(pos, networks, *caches) << '\n'; diff --git a/src/nnue/nnue_accumulator.cpp b/src/nnue/nnue_accumulator.cpp index 2153cd4a6..2bf76f530 100644 --- a/src/nnue/nnue_accumulator.cpp +++ b/src/nnue/nnue_accumulator.cpp @@ -19,15 +19,14 @@ #include "nnue_accumulator.h" #include +#include #include -#include #include #include "../bitboard.h" #include "../misc.h" #include "../position.h" #include "../types.h" -#include "network.h" #include "nnue_architecture.h" #include "nnue_feature_transformer.h" @@ -68,39 +67,24 @@ void AccumulatorState::reset(const DirtyPiece& dp) noexcept { accumulatorSmall.computed.fill(false); } -const AccumulatorState& AccumulatorStack::latest() const noexcept { - return m_accumulators[m_current_idx - 1]; -} +const AccumulatorState& AccumulatorStack::latest() const noexcept { return accumulators[size - 1]; } -AccumulatorState& AccumulatorStack::mut_latest() noexcept { - return m_accumulators[m_current_idx - 1]; -} +AccumulatorState& AccumulatorStack::mut_latest() noexcept { return accumulators[size - 1]; } -void AccumulatorStack::reset(const Position& rootPos, - const Networks& networks, - AccumulatorCaches& caches) noexcept { - m_current_idx = 1; - - update_accumulator_refresh_cache( - *networks.big.featureTransformer, rootPos, m_accumulators[0], caches.big); - update_accumulator_refresh_cache( - *networks.big.featureTransformer, rootPos, m_accumulators[0], caches.big); - - update_accumulator_refresh_cache( - *networks.small.featureTransformer, rootPos, m_accumulators[0], caches.small); - update_accumulator_refresh_cache( - *networks.small.featureTransformer, rootPos, m_accumulators[0], caches.small); +void AccumulatorStack::reset() noexcept { + accumulators[0].reset({}); + size = 1; } void AccumulatorStack::push(const DirtyPiece& dirtyPiece) noexcept { - assert(m_current_idx + 1 < m_accumulators.size()); - m_accumulators[m_current_idx].reset(dirtyPiece); - m_current_idx++; + assert(size + 1 < accumulators.size()); + accumulators[size].reset(dirtyPiece); + size++; } void AccumulatorStack::pop() noexcept { - assert(m_current_idx > 1); - m_current_idx--; + assert(size > 1); + size--; } template @@ -119,7 +103,7 @@ void AccumulatorStack::evaluate_side(const Position& pos, const auto last_usable_accum = find_last_usable_accumulator(); - if ((m_accumulators[last_usable_accum].template acc()).computed[Perspective]) + if ((accumulators[last_usable_accum].template acc()).computed[Perspective]) forward_update_incremental(pos, featureTransformer, last_usable_accum); else @@ -134,12 +118,12 @@ void AccumulatorStack::evaluate_side(const Position& pos, template std::size_t AccumulatorStack::find_last_usable_accumulator() const noexcept { - for (std::size_t curr_idx = m_current_idx - 1; curr_idx > 0; curr_idx--) + for (std::size_t curr_idx = size - 1; curr_idx > 0; curr_idx--) { - if ((m_accumulators[curr_idx].template acc()).computed[Perspective]) + if ((accumulators[curr_idx].template acc()).computed[Perspective]) return curr_idx; - if (FeatureSet::requires_refresh(m_accumulators[curr_idx].dirtyPiece, Perspective)) + if (FeatureSet::requires_refresh(accumulators[curr_idx].dirtyPiece, Perspective)) return curr_idx; } @@ -152,14 +136,14 @@ void AccumulatorStack::forward_update_incremental( const FeatureTransformer& featureTransformer, const std::size_t begin) noexcept { - assert(begin < m_accumulators.size()); - assert((m_accumulators[begin].acc()).computed[Perspective]); + assert(begin < accumulators.size()); + assert((accumulators[begin].acc()).computed[Perspective]); const Square ksq = pos.square(Perspective); - for (std::size_t next = begin + 1; next < m_current_idx; next++) + for (std::size_t next = begin + 1; next < size; next++) update_accumulator_incremental( - featureTransformer, ksq, m_accumulators[next], m_accumulators[next - 1]); + featureTransformer, ksq, accumulators[next], accumulators[next - 1]); assert((latest().acc()).computed[Perspective]); } @@ -170,17 +154,17 @@ void AccumulatorStack::backward_update_incremental( const FeatureTransformer& featureTransformer, const std::size_t end) noexcept { - assert(end < m_accumulators.size()); - assert(end < m_current_idx); + assert(end < accumulators.size()); + assert(end < size); assert((latest().acc()).computed[Perspective]); const Square ksq = pos.square(Perspective); - for (std::size_t next = m_current_idx - 2; next >= end; next--) + for (std::int64_t next = std::int64_t(size) - 2; next >= std::int64_t(end); next--) update_accumulator_incremental( - featureTransformer, ksq, m_accumulators[next], m_accumulators[next + 1]); + featureTransformer, ksq, accumulators[next], accumulators[next + 1]); - assert((m_accumulators[end].acc()).computed[Perspective]); + assert((accumulators[end].acc()).computed[Perspective]); } // Explicit template instantiations @@ -323,6 +307,7 @@ void update_accumulator_refresh_cache(const FeatureTransformer& feat const Position& pos, AccumulatorState& accumulatorState, AccumulatorCaches::Cache& cache) { + using Tiling [[maybe_unused]] = SIMDTiling; const Square ksq = pos.square(Perspective); diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index d83a5a446..aa9e2a676 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -41,8 +41,6 @@ using BiasType = std::int16_t; using PSQTWeightType = std::int32_t; using IndexType = std::uint32_t; -struct Networks; - template struct alignas(CacheLineSize) Accumulator; @@ -149,13 +147,12 @@ struct AccumulatorState { class AccumulatorStack { public: AccumulatorStack() : - m_accumulators(MAX_PLY + 1), - m_current_idx{} {} + accumulators(MAX_PLY + 1), + size{1} {} [[nodiscard]] const AccumulatorState& latest() const noexcept; - void - reset(const Position& rootPos, const Networks& networks, AccumulatorCaches& caches) noexcept; + void reset() noexcept; void push(const DirtyPiece& dirtyPiece) noexcept; void pop() noexcept; @@ -185,8 +182,8 @@ class AccumulatorStack { const FeatureTransformer& featureTransformer, const std::size_t end) noexcept; - std::vector m_accumulators; - std::size_t m_current_idx; + std::vector accumulators; + std::size_t size; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 809d454b5..c99874076 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -121,7 +121,6 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat }; AccumulatorStack accumulators; - accumulators.reset(pos, networks, caches); // We estimate the value of each piece by doing a differential evaluation from // the current base eval, simulating the removal of the piece from its square. @@ -140,7 +139,7 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat { pos.remove_piece(sq); - accumulators.reset(pos, networks, caches); + accumulators.reset(); std::tie(psqt, positional) = networks.big.evaluate(pos, accumulators, &caches.big); Value eval = psqt + positional; eval = pos.side_to_move() == WHITE ? eval : -eval; @@ -157,7 +156,7 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat ss << board[row] << '\n'; ss << '\n'; - accumulators.reset(pos, networks, caches); + accumulators.reset(); auto t = networks.big.trace_evaluate(pos, accumulators, &caches.big); ss << " NNUE network contributions " diff --git a/src/search.cpp b/src/search.cpp index 92998b026..fe0e340b4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -195,7 +195,7 @@ void Search::Worker::ensure_network_replicated() { void Search::Worker::start_searching() { - accumulatorStack.reset(rootPos, networks[numaAccessToken], refreshTable); + accumulatorStack.reset(); // Non-main threads go directly to iterative_deepening() if (!is_mainthread()) From f2507d05625434a1377c603bc2530ffdd4900e58 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 14 Apr 2025 22:04:51 +0200 Subject: [PATCH 006/107] Add x86-64-avxvnni in CI will result in the corresponding binaries being available for download closes https://github.com/official-stockfish/Stockfish/pull/5990 No functional change --- .github/ci/matrix.json | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/ci/matrix.json b/.github/ci/matrix.json index 44e0596ea..fa720a1c3 100644 --- a/.github/ci/matrix.json +++ b/.github/ci/matrix.json @@ -84,12 +84,6 @@ "os": "macos-14" } }, - { - "binaries": "x86-64-avxvnni", - "config": { - "os": "macos-14" - } - }, { "binaries": "x86-64-avx512", "config": { @@ -108,12 +102,6 @@ "os": "macos-14" } }, - { - "binaries": "x86-64-avxvnni", - "config": { - "ubuntu-22.04": null - } - }, { "binaries": "x86-64-avxvnni", "config": { From b915ed702aa4ac6bec948cb2baf3021f38762102 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 18 Apr 2025 09:08:39 -0700 Subject: [PATCH 007/107] remove StateInfo::next unused. closes https://github.com/official-stockfish/Stockfish/pull/5997 no functional change --- src/position.cpp | 2 -- src/position.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 0e5748e6a..85ade69a9 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -698,7 +698,6 @@ DirtyPiece Position::do_move(Move m, // our state pointer to point to the new (ready to be updated) state. std::memcpy(&newSt, st, offsetof(StateInfo, key)); newSt.previous = st; - st->next = &newSt; st = &newSt; // Increment ply counters. In particular, rule50 will be reset to zero later on @@ -1011,7 +1010,6 @@ void Position::do_null_move(StateInfo& newSt, const TranspositionTable& tt) { std::memcpy(&newSt, st, sizeof(StateInfo)); newSt.previous = st; - st->next = &newSt; st = &newSt; if (st->epSquare != SQ_NONE) diff --git a/src/position.h b/src/position.h index 75f22c7df..724165b00 100644 --- a/src/position.h +++ b/src/position.h @@ -53,7 +53,6 @@ struct StateInfo { Key key; Bitboard checkersBB; StateInfo* previous; - StateInfo* next; Bitboard blockersForKing[COLOR_NB]; Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; @@ -165,7 +164,6 @@ class Position { bool pos_is_ok() const; void flip(); - // Used by NNUE StateInfo* state() const; void put_piece(Piece pc, Square s); From 16cd38dba1c168e23982ee6d521a10bafc82ca14 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 10 Apr 2025 10:00:27 -0700 Subject: [PATCH 008/107] Tweak TT Move Reduction by TT Move History Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 62496 W: 16197 L: 15844 D: 30455 Ptnml(0-2): 200, 7234, 16011, 7619, 184 https://tests.stockfishchess.org/tests/view/67f7fa9b31d7cf8afdc4609c Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 400470 W: 102068 L: 101012 D: 197390 Ptnml(0-2): 201, 43207, 112347, 44295, 185 https://tests.stockfishchess.org/tests/view/67f927b0cd501869c66975e0 Passed VVLTC Non-regression: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 125394 W: 32408 L: 32304 D: 60682 Ptnml(0-2): 3, 11702, 39188, 11796, 8 https://tests.stockfishchess.org/tests/view/6804c215cd501869c66986b9 closes https://github.com/official-stockfish/Stockfish/pull/5998 bench 1760988 --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index fe0e340b4..9a106e673 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1298,6 +1298,8 @@ moves_loop: // When in check, search starts here if (!ttData.move) r += 1156; + r -= ttMoveHistory / 8; + // Note that if expected reduction is high, we reduce search depth here value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3495) - (r > 5510 && newDepth > 2), !cutNode); From 4176ad7b0a176a615f77c91b78b8b74e113a0a88 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 20 Apr 2025 09:21:01 -0700 Subject: [PATCH 009/107] simplify risk tolerance Passed Non-regression STC: LLR: 2.98 (-2.94,2.94) <-1.75,0.25> Total: 73408 W: 19028 L: 18844 D: 35536 Ptnml(0-2): 201, 8709, 18743, 8807, 244 https://tests.stockfishchess.org/tests/view/68051f3698cd372e3ae9f63a Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 91236 W: 23193 L: 23045 D: 44998 Ptnml(0-2): 34, 9908, 25599, 10030, 47 https://tests.stockfishchess.org/tests/view/6805239498cd372e3ae9fa41 closes https://github.com/official-stockfish/Stockfish/pull/6000 bench 1864632 --- src/search.cpp | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9a106e673..071a9d18a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -97,31 +97,6 @@ int correction_value(const Worker& w, const Position& pos, const Stack* const ss return 7685 * pcv + 7495 * micv + 9144 * (wnpcv + bnpcv) + 6469 * cntcv; } -int risk_tolerance(const Position& pos, Value v) { - // Returns (some constant of) second derivative of sigmoid. - static constexpr auto sigmoid_d2 = [](int x, int y) { - return 644800 * x / ((x * x + 3 * y * y) * y); - }; - - int m = pos.count() + pos.non_pawn_material() / 300; - - // a and b are the crude approximation of the wdl model. - // The win rate is: 1/(1+exp((a-v)/b)) - // The loss rate is 1/(1+exp((v+a)/b)) - int a = 356; - int b = ((65 * m - 3172) * m + 240578) / 2048; - - // guard against overflow - assert(abs(v) + a <= std::numeric_limits::max() / 644800); - - // The risk utility is therefore d/dv^2 (1/(1+exp(-(v-a)/b)) -1/(1+exp(-(-v-a)/b))) - // -115200x/(x^2+3) = -345600(ab) / (a^2+3b^2) (multiplied by some constant) (second degree pade approximant) - int winning_risk = sigmoid_d2(v - a, b); - int losing_risk = sigmoid_d2(v + a, b); - - return -(winning_risk + losing_risk) * 32; -} - // Add correctionHistory value to raw staticEval and guarantee evaluation // does not hit the tablebase range. Value to_corrected_static_eval(const Value v, const int cv) { @@ -150,6 +125,29 @@ void update_correction_history(const Position& pos, << bonus * 143 / 128; } +int risk_tolerance(Value v) { + // Returns (some constant of) second derivative of sigmoid. + static constexpr auto sigmoid_d2 = [](int x, int y) { + return 644800 * x / ((x * x + 3 * y * y) * y); + }; + + // a and b are the crude approximation of the wdl model. + // The win rate is: 1/(1+exp((a-v)/b)) + // The loss rate is 1/(1+exp((v+a)/b)) + int a = 356; + int b = 123; + + // guard against overflow + assert(abs(v) + a <= std::numeric_limits::max() / 644800); + + // The risk utility is therefore d/dv^2 (1/(1+exp(-(v-a)/b)) -1/(1+exp(-(-v-a)/b))) + // -115200x/(x^2+3) = -345600(ab) / (a^2+3b^2) (multiplied by some constant) (second degree pade approximant) + int winning_risk = sigmoid_d2(v - a, b); + int losing_risk = sigmoid_d2(v + a, b); + + return -(winning_risk + losing_risk) * 32; +} + // 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); } Value value_to_tt(Value v, int ply); @@ -1218,7 +1216,7 @@ moves_loop: // When in check, search starts here r -= std::abs(correctionValue) / 29696; if (PvNode && std::abs(bestValue) <= 2000) - r -= risk_tolerance(pos, bestValue); + r -= risk_tolerance(bestValue); // Increase reduction for cut nodes if (cutNode) From 449a8b017eb9556e562eb1e067d346a55d99f4e9 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 20 Apr 2025 23:39:00 +0300 Subject: [PATCH 010/107] Do second step of shallower search Specifically if lmr depth is not lower than new depth and value is really bad. Passed STC: https://tests.stockfishchess.org/tests/view/6805444898cd372e3aea0494 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 77664 W: 20136 L: 19762 D: 37766 Ptnml(0-2): 214, 9006, 20039, 9338, 235 Passed LTC: https://tests.stockfishchess.org/tests/view/680549b298cd372e3aea05ac LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 115458 W: 29447 L: 28971 D: 57040 Ptnml(0-2): 62, 12357, 32421, 12821, 68 closes https://github.com/official-stockfish/Stockfish/pull/6001 bench: 1515501 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 071a9d18a..2a5bf1b91 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1286,7 +1286,11 @@ moves_loop: // When in check, search starts here update_continuation_histories(ss, movedPiece, move.to_sq(), 1600); } else if (value > alpha && value < bestValue + 9) + { newDepth--; + if (value < bestValue + 3) + newDepth--; + } } // Step 18. Full-depth search when LMR is skipped From f6b0d53a995da2d3fdcdb9402420b30a058662da Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 21 Apr 2025 00:19:02 +0300 Subject: [PATCH 011/107] Re-adding the 5th continuation history the two simplifications (#5992 and #5978) are strongly correlated. Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 98016 W: 25415 L: 25008 D: 47593 Ptnml(0-2): 291, 11509, 25005, 11908, 295 https://tests.stockfishchess.org/tests/view/6802774fcd501869c6698168 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 588588 W: 150073 L: 148633 D: 289882 Ptnml(0-2): 281, 63615, 165078, 65023, 297 https://tests.stockfishchess.org/tests/view/6804cbdacd501869c66987dc closes https://github.com/official-stockfish/Stockfish/pull/6002 Bench: 1783315 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 2a5bf1b91..54d220cd1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1921,7 +1921,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, 1103}, {2, 659}, {3, 323}, {4, 533}, {6, 474}}}; + {{1, 1103}, {2, 659}, {3, 323}, {4, 533}, {5, 121}, {6, 474}}}; for (const auto [i, weight] : conthist_bonuses) { From 7988de4aa302e65dd4453651d65a6fbc095a66aa Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 20 Apr 2025 23:32:01 +0200 Subject: [PATCH 012/107] Prefer discovered and double checks in capture ordering. Increase weight for captured piece value if also a discovered or double check. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 142144 W: 36632 L: 36164 D: 69348 Ptnml(0-2): 429, 16470, 36788, 16974, 411 https://tests.stockfishchess.org/tests/view/68052d7498cd372e3ae9faaa Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 668328 W: 170837 L: 169238 D: 328253 Ptnml(0-2): 308, 72010, 187990, 73487, 369 https://tests.stockfishchess.org/tests/view/68053c9398cd372e3aea043b closes https://github.com/official-stockfish/Stockfish/pull/6003 Bench: 1636625 --- src/movepick.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index cc6d47901..6c8eb09c7 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -146,7 +146,11 @@ void MovePicker::score() { for (auto& m : *this) if constexpr (Type == CAPTURES) m.value = - 7 * int(PieceValue[pos.piece_on(m.to_sq())]) + (2 + * (pos.blockers_for_king(~pos.side_to_move()) & m.from_sq() + && !aligned(m.from_sq(), m.to_sq(), pos.square(~pos.side_to_move()))) + + 7) + * int(PieceValue[pos.piece_on(m.to_sq())]) + (*captureHistory)[pos.moved_piece(m)][m.to_sq()][type_of(pos.piece_on(m.to_sq()))]; else if constexpr (Type == QUIETS) From 88a524c55244b3827747c0cb7c8de490b6119d23 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 20 Apr 2025 14:29:29 -0700 Subject: [PATCH 013/107] Tweak futility formula Passed STC LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 248448 W: 64344 L: 63718 D: 120386 Ptnml(0-2): 750, 29172, 63783, 29740, 779 https://tests.stockfishchess.org/tests/view/68056f5598cd372e3aea2901 Passed LTC LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 118824 W: 30358 L: 29874 D: 58592 Ptnml(0-2): 59, 12797, 33228, 13257, 71 https://tests.stockfishchess.org/tests/view/6805675698cd372e3aea20d0 closes https://github.com/official-stockfish/Stockfish/pull/6004 bench 1839796 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 54d220cd1..b8a4b32a6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1099,7 +1099,7 @@ moves_loop: // When in check, search starts here lmrDepth += history / 3593; Value futilityValue = ss->staticEval + (bestMove ? 48 : 146) + 116 * lmrDepth - + 103 * (bestValue < ss->staticEval - 128); + + 103 * (bestValue < ss->staticEval - 128 && ss->staticEval > alpha - 50); // Futility pruning: parent node // (*Scaler): Generally, more frequent futility pruning From 0dcfe096d6223ea8c5663cf72f714280a38d8506 Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Mon, 21 Apr 2025 15:13:30 +0200 Subject: [PATCH 014/107] Increase full depth search reduction when cutNode In addition to the core patch, improve the use of `isTTMove`: - this name was used to mean both `bestMove == ttData.move` and `move == ttData.move`, so i replaced the argument `isTTMove` of `update_all_stats` with `TTMove` directly. - `ttData.move == move` was still used in some places instead of `ss->isTTMove`. I replaced these to be more consistent. Passed STC: https://tests.stockfishchess.org/tests/view/68057b8f98cd372e3aea3472 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 38400 W: 10048 L: 9734 D: 18618 Ptnml(0-2): 102, 4360, 9956, 4686, 96 Passed LTC: https://tests.stockfishchess.org/tests/view/68057f7c98cd372e3aea3842 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 312666 W: 79494 L: 78616 D: 154556 Ptnml(0-2): 144, 33809, 87563, 34659, 158 closes https://github.com/official-stockfish/Stockfish/pull/6007 Bench: 1623376 --- src/search.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b8a4b32a6..85e4d0077 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -164,7 +164,7 @@ void update_all_stats(const Position& pos, ValueList& quietsSearched, ValueList& capturesSearched, Depth depth, - bool isTTMove, + Move TTMove, int moveCount); } // namespace @@ -1098,8 +1098,9 @@ moves_loop: // When in check, search starts here lmrDepth += history / 3593; - Value futilityValue = ss->staticEval + (bestMove ? 48 : 146) + 116 * lmrDepth - + 103 * (bestValue < ss->staticEval - 128 && ss->staticEval > alpha - 50); + Value futilityValue = + ss->staticEval + (bestMove ? 48 : 146) + 116 * lmrDepth + + 103 * (bestValue < ss->staticEval - 128 && ss->staticEval > alpha - 50); // Futility pruning: parent node // (*Scaler): Generally, more frequent futility pruning @@ -1231,7 +1232,7 @@ moves_loop: // When in check, search starts here r += 1042 + allNode * 864; // For first picked move (ttMove) reduce reduction - else if (move == ttData.move) + else if (ss->isTTMove) r -= 1937; if (capture) @@ -1302,6 +1303,9 @@ moves_loop: // When in check, search starts here r -= ttMoveHistory / 8; + if (cutNode) + r += 520; + // Note that if expected reduction is high, we reduce search depth here value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3495) - (r > 5510 && newDepth > 2), !cutNode); @@ -1315,7 +1319,7 @@ moves_loop: // When in check, search starts here (ss + 1)->pv[0] = Move::none(); // Extend move from transposition table if we are about to dive into qsearch. - if (move == ttData.move && thisThread->rootDepth > 8) + if (ss->isTTMove && thisThread->rootDepth > 8) newDepth = std::max(newDepth, 1); value = -search(pos, ss + 1, -beta, -alpha, newDepth, false); @@ -1450,10 +1454,10 @@ moves_loop: // When in check, search starts here else if (bestMove) { update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth, - bestMove == ttData.move, moveCount); + ttData.move, moveCount); if (!PvNode) { - int bonus = (ttData.move == move) ? 800 : -870; + int bonus = ss->isTTMove ? 800 : -870; ttMoveHistory << bonus; } } @@ -1877,14 +1881,14 @@ void update_all_stats(const Position& pos, ValueList& quietsSearched, ValueList& capturesSearched, Depth depth, - bool isTTMove, + Move TTMove, int moveCount) { CapturePieceToHistory& captureHistory = workerThread.captureHistory; Piece moved_piece = pos.moved_piece(bestMove); PieceType captured; - int bonus = std::min(141 * depth - 89, 1613) + 311 * isTTMove; + int bonus = std::min(141 * depth - 89, 1613) + 311 * (bestMove == TTMove); int malus = std::min(695 * depth - 215, 2808) - 31 * (moveCount - 1); if (!pos.capture_stage(bestMove)) From 8b85290313a7cb9beed6d11a0af8d38cbfd2bdd3 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 21 Apr 2025 18:39:13 +0300 Subject: [PATCH 015/107] Remove manual stack alignment workaround for GCC < 9.3 consequently using these old compilers will now error out. closes https://github.com/official-stockfish/Stockfish/pull/6008 No functional change. --- src/nnue/network.cpp | 21 +-------------------- src/types.h | 6 +++--- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index e23294e4f..957dc7bff 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -212,21 +212,11 @@ NetworkOutput Network::evaluate(const Position& pos, AccumulatorStack& accumulatorStack, AccumulatorCaches::Cache* cache) const { - // We manually align the arrays on the stack because with gcc < 9.3 - // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; -#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType - transformedFeaturesUnaligned[FeatureTransformer::BufferSize - + alignment / sizeof(TransformedFeatureType)]; - - auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); -#else alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; -#endif ASSERT_ALIGNED(transformedFeatures, alignment); @@ -284,20 +274,11 @@ NnueEvalTrace Network::trace_evaluate(const Position& pos, AccumulatorStack& accumulatorStack, AccumulatorCaches::Cache* cache) const { - // We manually align the arrays on the stack because with gcc < 9.3 - // overaligning stack variables with alignas() doesn't work correctly. + constexpr uint64_t alignment = CacheLineSize; -#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType - transformedFeaturesUnaligned[FeatureTransformer::BufferSize - + alignment / sizeof(TransformedFeatureType)]; - - auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); -#else alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; -#endif ASSERT_ALIGNED(transformedFeatures, alignment); diff --git a/src/types.h b/src/types.h index d7f9dfb74..a76d00336 100644 --- a/src/types.h +++ b/src/types.h @@ -57,9 +57,9 @@ // _WIN32 Building on Windows (any) // _WIN64 Building on Windows 64 bit - #if defined(__GNUC__) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) \ - && defined(_WIN32) && !defined(__clang__) - #define ALIGNAS_ON_STACK_VARIABLES_BROKEN + #if defined(__GNUC__) && !defined(__clang__) \ + && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ < 3)) + #error "Stockfish requires GCC 9.3 or later for correct compilation" #endif #define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast(ptr) % alignment == 0) From f590767b91f6e7b95ee5797b3cf32c143e50d3c2 Mon Sep 17 00:00:00 2001 From: breatn Date: Tue, 22 Apr 2025 18:54:04 +0100 Subject: [PATCH 016/107] Adaptive beta cut passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 197088 W: 51084 L: 50533 D: 95471 Ptnml(0-2): 547, 23201, 50577, 23592, 627 https://tests.stockfishchess.org/tests/view/680604d798cd372e3aea58fe passed LTC LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 127950 W: 32719 L: 32214 D: 63017 Ptnml(0-2): 69, 13825, 35673, 14348, 60 https://tests.stockfishchess.org/tests/view/6805eae498cd372e3aea588a closes https://github.com/official-stockfish/Stockfish/pull/6012 bench 1579003 --- AUTHORS | 1 + src/search.cpp | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 54c5f8a21..f16758609 100644 --- a/AUTHORS +++ b/AUTHORS @@ -34,6 +34,7 @@ Artem Solopiy (EntityFX) Auguste Pop Balazs Szilagyi Balint Pfliegel +Baptiste Rech (breatn) Ben Chaney (Chaneybenjamini) Ben Koshy (BKSpurgeon) Bill Henry (VoyagerOne) diff --git a/src/search.cpp b/src/search.cpp index 85e4d0077..d08418813 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -103,6 +103,18 @@ Value to_corrected_static_eval(const Value v, const int cv) { return std::clamp(v + cv / 131072, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } +int adaptive_probcut_margin(Depth depth) { + // Base margin + constexpr int base = 180; + + // Approximate log2(depth) using a fast lookup table + static constexpr int logTable[32] = {0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; + + int logDepth = logTable[std::min(depth, 31)]; + return base + logDepth * 60 + std::min(10, (depth - 16) * 2); +}; + void update_correction_history(const Position& pos, Stack* const ss, Search::Worker& workerThread, @@ -972,7 +984,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea - probCutBeta = beta + 415; + probCutBeta = beta + adaptive_probcut_margin(depth); 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; From 5f32b3ed4b6df4039b1db131bb1db40a8e378ef1 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 23 Apr 2025 03:40:16 +0300 Subject: [PATCH 017/107] Remove unneeded return statement closes https://github.com/official-stockfish/Stockfish/pull/6013 No functional change --- src/memory.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/memory.h b/src/memory.h index 4dc232878..e4a59fec5 100644 --- a/src/memory.h +++ b/src/memory.h @@ -52,7 +52,6 @@ void memory_deleter(T* ptr, FREE_FUNC free_func) { ptr->~T(); free_func(ptr); - return; } // Frees memory which was placed there with placement new. From 7e6a0c464bd0fce5b5d94f7d6bd4af3d4d282504 Mon Sep 17 00:00:00 2001 From: Myself <63040919+AliceRoselia@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:00:36 +0700 Subject: [PATCH 018/107] Check only if good see. Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 106400 W: 27863 L: 27444 D: 51093 Ptnml(0-2): 320, 12431, 27275, 12858, 316 https://tests.stockfishchess.org/tests/view/6806239498cd372e3aea5949 Passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 420534 W: 107594 L: 106494 D: 206446 Ptnml(0-2): 197, 45541, 117722, 46579, 228 https://tests.stockfishchess.org/tests/view/6806b4b3878abf56f9a0d4fc closes https://github.com/official-stockfish/Stockfish/pull/6020 bench: 1675758 --- src/movepick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 6c8eb09c7..cac4abe4a 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -170,7 +170,7 @@ void MovePicker::score() { m.value += (*continuationHistory[5])[pc][to]; // bonus for checks - m.value += bool(pos.check_squares(pt) & to) * 16384; + m.value += (bool(pos.check_squares(pt) & to) && pos.see_ge(m, -75)) * 16384; // bonus for escaping from capture m.value += threatenedPieces & from ? (pt == QUEEN && !(to & threatenedByRook) ? 51700 From 4b58079485dd1f7ee69959ec4e8c9f05502a32e6 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 26 Apr 2025 19:36:08 +0300 Subject: [PATCH 019/107] Simplify and cleanup futility pruning for child nodes This patch removes (eval - beta) / 8 addition and adjusts constants accordingly, also moves every calculation into futility_margin function. Passed STC: https://tests.stockfishchess.org/tests/view/6806d00f878abf56f9a0d524 LLR: 2.99 (-2.94,2.94) <-1.75,0.25> Total: 483456 W: 124592 L: 124860 D: 234004 Ptnml(0-2): 1419, 57640, 123927, 57274, 1468 Passed LTC: https://tests.stockfishchess.org/tests/view/680cceb33629b02d74b1554c LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 263868 W: 67076 L: 67104 D: 129688 Ptnml(0-2): 155, 28893, 73846, 28905, 135 closes https://github.com/official-stockfish/Stockfish/pull/6021 bench: 1618439 --- src/search.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d08418813..79bb44c63 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -71,12 +71,20 @@ namespace { // tests at these types of time controls. // Futility margin -Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 110 - 25 * noTtCutNode; +Value futility_margin(Depth d, + bool noTtCutNode, + bool improving, + bool oppWorsening, + int statScore, + int correctionValue) { + Value futilityMult = 98 - 22 * noTtCutNode; Value improvingDeduction = improving * futilityMult * 2; Value worseningDeduction = oppWorsening * futilityMult / 3; + Value statScoreAddition = statScore / 339; + Value correctionAddition = correctionValue / 157363; - return futilityMult * d - improvingDeduction - worseningDeduction; + return futilityMult * d - improvingDeduction - worseningDeduction + statScoreAddition + + correctionAddition; } constexpr int futility_move_count(bool improving, Depth depth) { @@ -866,9 +874,9 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node // 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 / 301 + 37 + ((eval - beta) / 8) - - std::abs(correctionValue) / 139878 + && eval + - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening, + (ss - 1)->statScore, std::abs(correctionValue)) >= beta && eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval)) return beta + (eval - beta) / 3; From 27428a61c2fb6fd1ff1f8f0e88746e0680771730 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 26 Apr 2025 19:48:20 +0300 Subject: [PATCH 020/107] Allow some nodes to spawn even deeper lmr searches This includes nodes that were PvNode on a previous ply and only for non cutNodes and movecounts < 8. Passed STC: https://tests.stockfishchess.org/tests/view/680cdf2e3629b02d74b15576 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 123552 W: 31979 L: 31539 D: 60034 Ptnml(0-2): 322, 14449, 31803, 14871, 331 Passed LTC: https://tests.stockfishchess.org/tests/view/680cf09a3629b02d74b15599 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 114306 W: 29310 L: 28837 D: 56159 Ptnml(0-2): 51, 12247, 32090, 12708, 57 closes https://github.com/official-stockfish/Stockfish/pull/6022 bench: 1585741 --- src/search.cpp | 6 ++++-- src/search.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 79bb44c63..01f0c2c23 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -708,6 +708,7 @@ Value Search::Worker::search( (ss + 2)->cutoffCnt = 0; Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; ss->statScore = 0; + ss->isPvNode = PvNode; // Step 4. Transposition table lookup excludedMove = ss->excludedMove; @@ -1281,8 +1282,9 @@ moves_loop: // When in check, search starts here // std::clamp has been replaced by a more robust implementation. - Depth d = std::max( - 1, std::min(newDepth - r / 1024, newDepth + !allNode + (PvNode && !bestMove))); + Depth d = std::max(1, std::min(newDepth - r / 1024, + newDepth + !allNode + (PvNode && !bestMove))) + + (!cutNode && (ss - 1)->isPvNode && moveCount < 8); ss->reduction = newDepth - d; diff --git a/src/search.h b/src/search.h index 6ef8322a8..6ab98f7c4 100644 --- a/src/search.h +++ b/src/search.h @@ -76,6 +76,7 @@ struct Stack { int cutoffCnt; int reduction; bool isTTMove; + bool isPvNode; }; From 4e49f8dff99dfa3a7cfb8b8fd8c1d7c54e1291d2 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 27 Feb 2025 12:43:58 -0800 Subject: [PATCH 021/107] Clean up search * Correct IIR scaling comments * Replace `(PvNode || cutNode)` with `!allNode` * Consistent formatting for scaler tags * Add comments to some recently-introduced LMR terms * Add comments on PCM bonus tweaks Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 389472 W: 102457 L: 102622 D: 184393 Ptnml(0-2): 1676, 41887, 107798, 41676, 1699 https://tests.stockfishchess.org/tests/view/67a0ea670774dfd78deb23cd closes https://github.com/official-stockfish/Stockfish/pull/5854 Bench: 1585741 --- src/search.cpp | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 01f0c2c23..bafccd0f5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -658,8 +658,7 @@ Value Search::Worker::search( Value bestValue, value, eval, maxValue, probCutBeta; bool givesCheck, improving, priorCapture, opponentWorsening; bool capture, ttCapture; - int priorReduction = (ss - 1)->reduction; - (ss - 1)->reduction = 0; + int priorReduction; Piece movedPiece; ValueList capturesSearched; @@ -704,11 +703,13 @@ Value Search::Worker::search( assert(0 <= ss->ply && ss->ply < MAX_PLY); - bestMove = Move::none(); + Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; + bestMove = Move::none(); + priorReduction = (ss - 1)->reduction; + (ss - 1)->reduction = 0; + ss->statScore = 0; + ss->isPvNode = PvNode; (ss + 2)->cutoffCnt = 0; - Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; - ss->statScore = 0; - ss->isPvNode = PvNode; // Step 4. Transposition table lookup excludedMove = ss->excludedMove; @@ -927,8 +928,8 @@ Value Search::Worker::search( // Step 10. Internal iterative reductions // For PV nodes without a ttMove as well as for deep enough cutNodes, we decrease depth. - // (* Scaler) Especially if they make IIR more aggressive. - if (((PvNode || cutNode) && depth >= 7 - 3 * PvNode) && !ttData.move) + // (*Scaler) Especially if they make IIR less aggressive. + if (depth >= 7 - 3 * PvNode && !allNode && !ttData.move) depth--; // Step 11. ProbCut @@ -1153,7 +1154,7 @@ moves_loop: // When in check, search starts here // and if the result is lower than ttValue minus a margin, then we will // extend the ttMove. Recursive singular search is avoided. - // (* Scaler) Generally, higher singularBeta (i.e closer to ttValue) + // (*Scaler) Generally, higher singularBeta (i.e closer to ttValue) // and lower extension margins scale well. if (!rootNode && move == ttData.move && !excludedMove @@ -1233,8 +1234,8 @@ moves_loop: // When in check, search starts here // These reduction adjustments have no proven non-linear scaling - r += 306 - moveCount * 34; - + r += 306; // Base reduction offset to compensate for other tweaks + r -= moveCount * 34; r -= std::abs(correctionValue) / 29696; if (PvNode && std::abs(bestValue) <= 2000) @@ -1280,18 +1281,14 @@ 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))) + (!cutNode && (ss - 1)->isPvNode && moveCount < 8); ss->reduction = newDepth - d; - value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); ss->reduction = 0; - // Do a full-depth search when reduced LMR search fails high if (value > alpha && d < newDepth) { @@ -1487,12 +1484,14 @@ moves_loop: // When in check, search starts here // Bonus for prior quiet countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonusScale = - (std::min(78 * depth - 312, 194) + 34 * !allNode + 164 * ((ss - 1)->moveCount > 8) - + 141 * (!ss->inCheck && bestValue <= ss->staticEval - 100) - + 121 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 75) - + 86 * ((ss - 1)->isTTMove) + 86 * (ss->cutoffCnt <= 3) - + std::min(-(ss - 1)->statScore / 112, 303)); + int bonusScale = std::min(-(ss - 1)->statScore / 112, 303); + bonusScale += std::min(78 * depth - 312, 194); + bonusScale += 34 * !allNode; + bonusScale += 164 * ((ss - 1)->moveCount > 8); + bonusScale += 86 * (ss - 1)->isTTMove; + bonusScale += 86 * (ss->cutoffCnt <= 3); + bonusScale += 141 * (!ss->inCheck && bestValue <= ss->staticEval - 100); + bonusScale += 121 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 75); bonusScale = std::max(bonusScale, 0); @@ -1903,14 +1902,14 @@ void update_all_stats(const Position& pos, ValueList& quietsSearched, ValueList& capturesSearched, Depth depth, - Move TTMove, + Move ttMove, int moveCount) { CapturePieceToHistory& captureHistory = workerThread.captureHistory; Piece moved_piece = pos.moved_piece(bestMove); PieceType captured; - int bonus = std::min(141 * depth - 89, 1613) + 311 * (bestMove == TTMove); + int bonus = std::min(141 * depth - 89, 1613) + 311 * (bestMove == ttMove); int malus = std::min(695 * depth - 215, 2808) - 31 * (moveCount - 1); if (!pos.capture_stage(bestMove)) From fda269a2997033a01ed49d83337a2e0405cec805 Mon Sep 17 00:00:00 2001 From: Carlos Esparza Date: Wed, 9 Apr 2025 16:09:47 -0700 Subject: [PATCH 022/107] Introduce double incremental accumulator updates when we need to update an accumulator by two moves and the second move captures the piece moved in the first move, we can skip computing the middle accumulator and cancel a feature add with a feature remove to save work. Passed STC https://tests.stockfishchess.org/tests/view/67f70b1c31d7cf8afdc45f51 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 72800 W: 18878 L: 18529 D: 35393 Ptnml(0-2): 160, 7711, 20374, 7930, 225 closes https://github.com/official-stockfish/Stockfish/pull/5988 No functional change --- src/nnue/nnue_accumulator.cpp | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/nnue/nnue_accumulator.cpp b/src/nnue/nnue_accumulator.cpp index 2bf76f530..5c1128539 100644 --- a/src/nnue/nnue_accumulator.cpp +++ b/src/nnue/nnue_accumulator.cpp @@ -46,6 +46,13 @@ namespace Stockfish::Eval::NNUE { namespace { +template +void double_inc_update(const FeatureTransformer& featureTransformer, + const Square ksq, + AccumulatorState& middle_state, + AccumulatorState& target_state, + const AccumulatorState& computed); + template void update_accumulator_incremental( const FeatureTransformer& featureTransformer, @@ -142,8 +149,27 @@ void AccumulatorStack::forward_update_incremental( const Square ksq = pos.square(Perspective); for (std::size_t next = begin + 1; next < size; next++) + { + if (next + 1 < size) + { + auto& dp1 = accumulators[next].dirtyPiece; + auto& dp2 = accumulators[next + 1].dirtyPiece; + + if (dp2.dirty_num >= 2 && dp1.piece[0] == dp2.piece[1] && dp1.to[0] == dp2.from[1]) + { + const Square captureSq = dp1.to[0]; + dp1.to[0] = dp2.from[1] = SQ_NONE; + double_inc_update(featureTransformer, ksq, accumulators[next], + accumulators[next + 1], accumulators[next - 1]); + dp1.to[0] = dp2.from[1] = captureSq; + + next++; + continue; + } + } update_accumulator_incremental( featureTransformer, ksq, accumulators[next], accumulators[next - 1]); + } assert((latest().acc()).computed[Perspective]); } @@ -240,6 +266,49 @@ auto make_accumulator_update_context(const FeatureTransformer& featu accumulatorTo}; } +template +void double_inc_update(const FeatureTransformer& featureTransformer, + const Square ksq, + AccumulatorState& middle_state, + AccumulatorState& target_state, + const AccumulatorState& computed) { + + assert(computed.acc().computed[Perspective]); + assert(!middle_state.acc().computed[Perspective]); + assert(!target_state.acc().computed[Perspective]); + + FeatureSet::IndexList removed, added; + FeatureSet::append_changed_indices(ksq, middle_state.dirtyPiece, removed, added); + // you can't capture a piece that was just involved in castling since the rook ends up + // in a square that the king passed + assert(added.size() < 2); + FeatureSet::append_changed_indices(ksq, target_state.dirtyPiece, removed, added); + + assert(added.size() == 1); + assert(removed.size() == 2 || removed.size() == 3); + + // Workaround compiler warning for uninitialized variables, replicated on + // profile builds on windows with gcc 14.2.0. + // TODO remove once unneeded + sf_assume(added.size() == 1); + sf_assume(removed.size() == 2 || removed.size() == 3); + + auto updateContext = + make_accumulator_update_context(featureTransformer, computed, target_state); + + if (removed.size() == 2) + { + updateContext.template apply(added[0], removed[0], removed[1]); + } + else + { + updateContext.template apply(added[0], removed[0], removed[1], + removed[2]); + } + + target_state.acc().computed[Perspective] = true; +} + template void update_accumulator_incremental( const FeatureTransformer& featureTransformer, From f0de8dc0349bac56021a900910f14a00a729dbc6 Mon Sep 17 00:00:00 2001 From: Carlos Esparza Date: Mon, 21 Apr 2025 18:26:46 -0700 Subject: [PATCH 023/107] Simplify move ordering bonuses for putting piece en prise and escaping capture Now there is also a penalty for exposing knights and bishops to capture by a pawn. Passed STC: https://tests.stockfishchess.org/tests/view/68074379878abf56f9a0d5b1 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 96512 W: 24841 L: 24687 D: 46984 Ptnml(0-2): 294, 11336, 24835, 11504, 287 Passed LTC: https://tests.stockfishchess.org/tests/view/6808954a878abf56f9a0d76d LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 221328 W: 56271 L: 56255 D: 108802 Ptnml(0-2): 131, 24149, 62071, 24199, 114 closes https://github.com/official-stockfish/Stockfish/pull/6023 Bench: 1778227 --- src/movepick.cpp | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index cac4abe4a..71a5f9959 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -126,21 +126,20 @@ void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); - [[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook, - threatenedPieces; + [[maybe_unused]] Bitboard threatenedPieces, threatByLesser[4]; if constexpr (Type == QUIETS) { Color us = pos.side_to_move(); - threatenedByPawn = pos.attacks_by(~us); - threatenedByMinor = - pos.attacks_by(~us) | pos.attacks_by(~us) | threatenedByPawn; - threatenedByRook = pos.attacks_by(~us) | threatenedByMinor; + threatByLesser[0] = threatByLesser[1] = pos.attacks_by(~us); + threatByLesser[2] = + pos.attacks_by(~us) | pos.attacks_by(~us) | threatByLesser[0]; + threatByLesser[3] = pos.attacks_by(~us) | threatByLesser[2]; // Pieces threatened by pieces of lesser material value - threatenedPieces = (pos.pieces(us, QUEEN) & threatenedByRook) - | (pos.pieces(us, ROOK) & threatenedByMinor) - | (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn); + threatenedPieces = (pos.pieces(us, QUEEN) & threatByLesser[3]) + | (pos.pieces(us, ROOK) & threatByLesser[2]) + | (pos.pieces(us, KNIGHT, BISHOP) & threatByLesser[0]); } for (auto& m : *this) @@ -172,17 +171,15 @@ void MovePicker::score() { // bonus for checks m.value += (bool(pos.check_squares(pt) & to) && pos.see_ge(m, -75)) * 16384; - // bonus for escaping from capture - m.value += threatenedPieces & from ? (pt == QUEEN && !(to & threatenedByRook) ? 51700 - : pt == ROOK && !(to & threatenedByMinor) ? 25600 - : !(to & threatenedByPawn) ? 14450 - : 0) - : 0; - - // malus for putting piece en prise - m.value -= (pt == QUEEN && bool(to & threatenedByRook) ? 49000 - : pt == ROOK && bool(to & threatenedByMinor) ? 24335 - : 0); + // penalty for moving to a square threatened by a lesser piece + // or bonus for escaping an attack by a lesser piece. + constexpr int bonus[4] = {144, 144, 256, 517}; + if (KNIGHT <= pt && pt <= QUEEN) + { + auto i = pt - 2; + int v = (threatByLesser[i] & to ? -95 : 100 * bool(threatByLesser[i] & from)); + m.value += bonus[i] * v; + } if (ply < LOW_PLY_HISTORY_SIZE) m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + 2 * ply); From b0a7a34d3fd9b7024abd64150d47405d1be1dd69 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 26 Apr 2025 14:38:21 -0700 Subject: [PATCH 024/107] Simplify malus calculation closes https://github.com/official-stockfish/Stockfish/pull/6024 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 bafccd0f5..bd2cb1cbc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1910,7 +1910,7 @@ void update_all_stats(const Position& pos, PieceType captured; int bonus = std::min(141 * depth - 89, 1613) + 311 * (bestMove == ttMove); - int malus = std::min(695 * depth - 215, 2808) - 31 * (moveCount - 1); + int malus = std::min(695 * depth - 184, 2839) - 31 * moveCount; if (!pos.capture_stage(bestMove)) { From 37cc2293efdc1c2b00f97d1a10f85f54e9c8ec64 Mon Sep 17 00:00:00 2001 From: Myself <63040919+AliceRoselia@users.noreply.github.com> Date: Sun, 27 Apr 2025 14:52:01 +0700 Subject: [PATCH 025/107] Replace complex probcut function with a precomputed table. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/680de2683629b02d74b15b46 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 116000 W: 29864 L: 29742 D: 56394 Ptnml(0-2): 215, 11811, 33854, 11877, 243 closes https://github.com/official-stockfish/Stockfish/pull/6026 No functional change --- src/search.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index bd2cb1cbc..e9dfb7172 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -111,17 +111,11 @@ Value to_corrected_static_eval(const Value v, const int cv) { return std::clamp(v + cv / 131072, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } -int adaptive_probcut_margin(Depth depth) { - // Base margin - constexpr int base = 180; - // Approximate log2(depth) using a fast lookup table - static constexpr int logTable[32] = {0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; +constexpr int adaptiveProbcutMargin[32] = {148, 150, 212, 214, 276, 278, 280, 282, 344, 346, 348, + 350, 352, 354, 356, 358, 420, 422, 424, 426, 428, 430, + 430, 430, 430, 430, 430, 430, 430, 430, 430, 430}; - int logDepth = logTable[std::min(depth, 31)]; - return base + logDepth * 60 + std::min(10, (depth - 16) * 2); -}; void update_correction_history(const Position& pos, Stack* const ss, @@ -994,7 +988,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea - probCutBeta = beta + adaptive_probcut_margin(depth); + probCutBeta = beta + adaptiveProbcutMargin[std::min(depth, 31)]; 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; From f98c178960a26e80bd7ffc9a0a280610fdaf92fd Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 27 Apr 2025 14:42:35 +0300 Subject: [PATCH 026/107] Improve quiet moves bonus Inspired by an old test by Peregrine. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 453024 W: 117244 L: 116316 D: 219464 Ptnml(0-2): 1336, 53355, 116258, 54171, 1392 https://tests.stockfishchess.org/tests/view/680ccacc3629b02d74b15532 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 140550 W: 35990 L: 35462 D: 69098 Ptnml(0-2): 65, 15152, 39319, 15668, 71 https://tests.stockfishchess.org/tests/view/680d2ed73629b02d74b15691 closes https://github.com/official-stockfish/Stockfish/pull/6028 Bench: 1708152 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e9dfb7172..fab0fd617 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -839,7 +839,8 @@ Value Search::Worker::search( } // Use static evaluation difference to improve quiet move ordering - if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) + if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture + && (ttData.depth - 2) <= depth) { int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1950, 1416) + 655; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1124 / 1024; From e5aa4b48c633eccdd6effda94583c52bfe436bb1 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 27 Apr 2025 14:48:29 +0300 Subject: [PATCH 027/107] Simplify Evasion Move Scoring Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 160256 W: 41432 L: 41348 D: 77476 Ptnml(0-2): 485, 19034, 41028, 19074, 507 https://tests.stockfishchess.org/tests/view/680d242c3629b02d74b15662 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 103086 W: 26388 L: 26252 D: 50446 Ptnml(0-2): 41, 11174, 28982, 11300, 46 https://tests.stockfishchess.org/tests/view/680d47f83629b02d74b1571e closes https://github.com/official-stockfish/Stockfish/pull/6029 Bench: 1937261 --- src/movepick.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 71a5f9959..c8738c568 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -191,8 +191,7 @@ void MovePicker::score() { 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()] - + (*pawnHistory)[pawn_structure_index(pos)][pos.moved_piece(m)][m.to_sq()]; + + (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()]; } } From 267fd8a3d5a19939e4026edabbe59db4f1966a10 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 27 Apr 2025 15:03:33 +0300 Subject: [PATCH 028/107] Tweak History Bonus Inspired by @Ergodice , who came up first with the idea. Passed STC: LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 18400 W: 4867 L: 4576 D: 8957 Ptnml(0-2): 52, 2052, 4714, 2317, 65 https://tests.stockfishchess.org/tests/view/68062a3c98cd372e3aea5959 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 454338 W: 116461 L: 115294 D: 222583 Ptnml(0-2): 198, 49139, 127346, 50270, 216 https://tests.stockfishchess.org/tests/view/6806347c98cd372e3aea5967 Passed VLTC non-reg: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 385970 W: 98401 L: 98546 D: 189023 Ptnml(0-2): 51, 38958, 115105, 38827, 44 https://tests.stockfishchess.org/tests/view/680cfe873629b02d74b155cf closes https://github.com/official-stockfish/Stockfish/pull/6030 Bench: 1715817 --- src/search.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fab0fd617..a6a189fca 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1962,12 +1962,14 @@ 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 * 829 / 1024; + workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 800 / 1024; - update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 1004 / 1024); + update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), + bonus * (bonus > 0 ? 1094 : 790) / 1024); int pIndex = pawn_structure_index(pos); - workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus * 587 / 1024; + workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] + << bonus * (bonus > 0 ? 725 : 460) / 1024; } } From 3e26d3acc7ae2dba0ad9a47dc7bd3e36c109bcec Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 27 Apr 2025 16:31:54 +0300 Subject: [PATCH 029/107] Do more pruning in moves loop Effectively reverts one commit from some months ago. Passed VVLTC SPRT with STC bounds https://tests.stockfishchess.org/tests/view/680d39373629b02d74b156d7 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 405058 W: 104843 L: 104111 D: 196104 Ptnml(0-2): 35, 38029, 125672, 38755, 38 Passed VVLTC SPRT with LTC bounds https://tests.stockfishchess.org/tests/view/680d1a3b3629b02d74b1563d LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 57032 W: 14917 L: 14588 D: 27527 Ptnml(0-2): 6, 5202, 17768, 5537, 3 closes https://github.com/official-stockfish/Stockfish/pull/6031 Bench: 1643819 --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a6a189fca..35a2b5de2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1049,8 +1049,6 @@ moves_loop: // When in check, search starts here Depth r = reduction(improving, depth, moveCount, delta); - r -= 32 * moveCount; - // Increase reduction for ttPv nodes (*Scaler) // Smaller or even negative value is better for short time controls // Bigger value is better for long time controls @@ -1230,7 +1228,7 @@ moves_loop: // When in check, search starts here // These reduction adjustments have no proven non-linear scaling r += 306; // Base reduction offset to compensate for other tweaks - r -= moveCount * 34; + r -= moveCount * 66; r -= std::abs(correctionValue) / 29696; if (PvNode && std::abs(bestValue) <= 2000) From af3692b2d05201e3e4ba3c7c6a8dc5a8700e0ae4 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 27 Apr 2025 01:55:17 -0700 Subject: [PATCH 030/107] Simplify second probcut to linear function of depth Passed non-regression STC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 57472 W: 14962 L: 14765 D: 27745 Ptnml(0-2): 140, 6715, 14817, 6936, 128 https://tests.stockfishchess.org/tests/view/680df1063629b02d74b15b69 Passed non-regression LTC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 88416 W: 22499 L: 22348 D: 43569 Ptnml(0-2): 31, 9565, 24874, 9698, 40 https://tests.stockfishchess.org/tests/view/680df3a93629b02d74b15b7d closes https://github.com/official-stockfish/Stockfish/pull/6035 Bench: 1792000 --- src/search.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 35a2b5de2..b370f7ca9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -111,12 +111,6 @@ Value to_corrected_static_eval(const Value v, const int cv) { return std::clamp(v + cv / 131072, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } - -constexpr int adaptiveProbcutMargin[32] = {148, 150, 212, 214, 276, 278, 280, 282, 344, 346, 348, - 350, 352, 354, 356, 358, 420, 422, 424, 426, 428, 430, - 430, 430, 430, 430, 430, 430, 430, 430, 430, 430}; - - void update_correction_history(const Position& pos, Stack* const ss, Search::Worker& workerThread, @@ -989,7 +983,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea - probCutBeta = beta + adaptiveProbcutMargin[std::min(depth, 31)]; + probCutBeta = beta + 180 + depth * 20; 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; From 94e6c0498ff24d0a66fd0817fcbb88855a9b6116 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 27 Apr 2025 21:40:45 +0300 Subject: [PATCH 031/107] VVLTC Tune Passed VVLTC with LTC bounds: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 12800 W: 3432 L: 3184 D: 6184 Ptnml(0-2): 1, 1098, 3954, 1346, 1 https://tests.stockfishchess.org/tests/view/680e255e3629b02d74b15d5e Passed VVLTC with STC bounds: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 14402 W: 3865 L: 3625 D: 6912 Ptnml(0-2): 0, 1236, 4490, 1474, 1 https://tests.stockfishchess.org/tests/view/680e4dfb3629b02d74b15da6 Passed VVLTC third test (removing an unrelated change): LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 25584 W: 6670 L: 6398 D: 12516 Ptnml(0-2): 4, 2290, 7932, 2562, 4 https://tests.stockfishchess.org/tests/view/680e74223629b02d74b15def closes https://github.com/official-stockfish/Stockfish/pull/6036 Bench: 1857323 --- src/search.cpp | 190 ++++++++++++++++++++++++------------------------- 1 file changed, 95 insertions(+), 95 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b370f7ca9..f673ba340 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -77,11 +77,11 @@ Value futility_margin(Depth d, bool oppWorsening, int statScore, int correctionValue) { - Value futilityMult = 98 - 22 * noTtCutNode; + Value futilityMult = 105 - 23 * noTtCutNode; Value improvingDeduction = improving * futilityMult * 2; Value worseningDeduction = oppWorsening * futilityMult / 3; - Value statScoreAddition = statScore / 339; - Value correctionAddition = correctionValue / 157363; + Value statScoreAddition = statScore / 335; + Value correctionAddition = correctionValue / 149902; return futilityMult * d - improvingDeduction - worseningDeduction + statScoreAddition + correctionAddition; @@ -102,7 +102,7 @@ int correction_value(const Worker& w, const Position& pos, const Stack* const ss m.is_ok() ? (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] : 0; - return 7685 * pcv + 7495 * micv + 9144 * (wnpcv + bnpcv) + 6469 * cntcv; + return 7696 * pcv + 7689 * micv + 9708 * (wnpcv + bnpcv) + 6978 * cntcv; } // Add correctionHistory value to raw staticEval and guarantee evaluation @@ -118,11 +118,11 @@ void update_correction_history(const Position& pos, const Move m = (ss - 1)->currentMove; const Color us = pos.side_to_move(); - static constexpr int nonPawnWeight = 162; + static constexpr int nonPawnWeight = 172; workerThread.pawnCorrectionHistory[pawn_structure_index(pos)][us] << bonus * 111 / 128; - workerThread.minorPieceCorrectionHistory[minor_piece_index(pos)][us] << bonus * 146 / 128; + workerThread.minorPieceCorrectionHistory[minor_piece_index(pos)][us] << bonus * 151 / 128; workerThread.nonPawnCorrectionHistory[non_pawn_index(pos)][WHITE][us] << bonus * nonPawnWeight / 128; workerThread.nonPawnCorrectionHistory[non_pawn_index(pos)][BLACK][us] @@ -130,20 +130,20 @@ void update_correction_history(const Position& pos, if (m.is_ok()) (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] - << bonus * 143 / 128; + << bonus * 141 / 128; } int risk_tolerance(Value v) { // Returns (some constant of) second derivative of sigmoid. static constexpr auto sigmoid_d2 = [](int x, int y) { - return 644800 * x / ((x * x + 3 * y * y) * y); + return 631760 * x / ((x * x + 3 * y * y) * y); }; // a and b are the crude approximation of the wdl model. // The win rate is: 1/(1+exp((a-v)/b)) // The loss rate is 1/(1+exp((v+a)/b)) - int a = 356; - int b = 123; + int a = 340; + int b = 122; // guard against overflow assert(abs(v) + a <= std::numeric_limits::max() / 644800); @@ -330,7 +330,7 @@ void Search::Worker::iterative_deepening() { int searchAgainCounter = 0; - lowPlyHistory.fill(92); + lowPlyHistory.fill(86); // Iterative deepening loop until requested to stop or the target depth is reached while (++rootDepth < MAX_PLY && !threads.stop @@ -366,13 +366,13 @@ void Search::Worker::iterative_deepening() { selDepth = 0; // Reset aspiration window starting size - delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 11834; + delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 11134; 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 - optimism[us] = 138 * avg / (std::abs(avg) + 84); + optimism[us] = 137 * avg / (std::abs(avg) + 91); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -578,11 +578,11 @@ void Search::Worker::undo_null_move(Position& pos) { pos.undo_null_move(); } // Reset histories, usually before a new game void Search::Worker::clear() { - mainHistory.fill(66); - lowPlyHistory.fill(105); - captureHistory.fill(-646); - pawnHistory.fill(-1262); - pawnCorrectionHistory.fill(6); + mainHistory.fill(67); + lowPlyHistory.fill(107); + captureHistory.fill(-688); + pawnHistory.fill(-1287); + pawnCorrectionHistory.fill(5); minorPieceCorrectionHistory.fill(0); nonPawnCorrectionHistory.fill(0); @@ -590,16 +590,16 @@ void Search::Worker::clear() { for (auto& to : continuationCorrectionHistory) for (auto& h : to) - h.fill(5); + h.fill(8); for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h.fill(-468); + h.fill(-473); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int(2954 / 128.0 * std::log(i)); + reductions[i] = int(2796 / 128.0 * std::log(i)); refreshTable.clear(networks[numaAccessToken]); } @@ -727,11 +727,11 @@ Value Search::Worker::search( // Bonus for a quiet ttMove that fails high if (!ttCapture) update_quiet_histories(pos, ss, *this, ttData.move, - std::min(120 * depth - 75, 1241)); + std::min(125 * depth - 77, 1157)); // Extra penalty for early quiet moves of the previous ply if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 3 && !priorCapture) - update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -2200); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -2301); } // Partial workaround for the graph history interaction problem @@ -836,11 +836,11 @@ Value Search::Worker::search( if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture && (ttData.depth - 2) <= depth) { - int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1950, 1416) + 655; - thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1124 / 1024; + int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1858, 1492) + 661; + thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1057 / 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 * 1196 / 1024; + << bonus * 1266 / 1024; } // Set up the improving flag, which is true if current static evaluation is @@ -853,13 +853,13 @@ Value Search::Worker::search( if (priorReduction >= 3 && !opponentWorsening) depth++; - if (priorReduction >= 1 && depth >= 2 && ss->staticEval + (ss - 1)->staticEval > 188) + if (priorReduction >= 1 && depth >= 2 && ss->staticEval + (ss - 1)->staticEval > 175) depth--; // Step 7. Razoring // 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 - 461 - 315 * depth * depth) + if (!PvNode && eval < alpha - 486 - 325 * depth * depth) return qsearch(pos, ss, alpha, beta); // Step 8. Futility pruning: child node @@ -874,13 +874,13 @@ Value Search::Worker::search( // Step 9. Null move search with verification search if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta - && ss->staticEval >= beta - 19 * depth + 418 && !excludedMove && pos.non_pawn_material(us) + && ss->staticEval >= beta - 19 * depth + 389 && !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) / 232, 6) + depth / 3 + 5; + Depth R = std::min(int(eval - beta) / 213, 6) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -918,13 +918,13 @@ Value Search::Worker::search( // Step 10. Internal iterative reductions // For PV nodes without a ttMove as well as for deep enough cutNodes, we decrease depth. // (*Scaler) Especially if they make IIR less aggressive. - if (depth >= 7 - 3 * PvNode && !allNode && !ttData.move) + if ((!allNode && depth >= (PvNode ? 5 : 7)) && !ttData.move) depth--; // Step 11. ProbCut // 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 + 185 - 58 * improving; + probCutBeta = beta + 201 - 58 * improving; if (depth >= 3 && !is_decisive(beta) // If value from transposition table is lower than probCutBeta, don't attempt @@ -1047,7 +1047,7 @@ moves_loop: // When in check, search starts here // Smaller or even negative value is better for short time controls // Bigger value is better for long time controls if (ss->ttPv) - r += 979; + r += 968; // Step 14. Pruning at shallow depth. // Depth conditions are important for mate finding. @@ -1069,15 +1069,15 @@ moves_loop: // When in check, search starts here // Futility pruning for captures if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 242 + 230 * lmrDepth - + PieceValue[capturedPiece] + 133 * captHist / 1024; + Value futilityValue = ss->staticEval + 232 + 224 * lmrDepth + + PieceValue[capturedPiece] + 131 * captHist / 1024; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks - int seeHist = std::clamp(captHist / 32, -138 * depth, 135 * depth); - if (!pos.see_ge(move, -154 * depth - seeHist)) + int seeHist = std::clamp(captHist / 31, -137 * depth, 125 * depth); + if (!pos.see_ge(move, -158 * depth - seeHist)) { bool skip = true; if (depth > 2 && !capture && givesCheck && alpha < 0 @@ -1100,16 +1100,16 @@ moves_loop: // When in check, search starts here + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning - if (history < -4348 * depth) + if (history < -4229 * depth) continue; history += 68 * thisThread->mainHistory[us][move.from_to()] / 32; - lmrDepth += history / 3593; + lmrDepth += history / 3388; Value futilityValue = - ss->staticEval + (bestMove ? 48 : 146) + 116 * lmrDepth - + 103 * (bestValue < ss->staticEval - 128 && ss->staticEval > alpha - 50); + ss->staticEval + (bestMove ? 46 : 138) + 117 * lmrDepth + + 102 * (bestValue < ss->staticEval - 127 && ss->staticEval > alpha - 50); // Futility pruning: parent node // (*Scaler): Generally, more frequent futility pruning @@ -1145,11 +1145,11 @@ moves_loop: // When in check, search starts here // and lower extension margins scale well. if (!rootNode && move == ttData.move && !excludedMove - && depth >= 6 - (thisThread->completedDepth > 29) + ss->ttPv + && depth >= 6 - (thisThread->completedDepth > 27) + ss->ttPv && is_valid(ttData.value) && !is_decisive(ttData.value) && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3) { - Value singularBeta = ttData.value - (59 + 77 * (ss->ttPv && !PvNode)) * depth / 54; + Value singularBeta = ttData.value - (58 + 76 * (ss->ttPv && !PvNode)) * depth / 57; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1159,12 +1159,12 @@ moves_loop: // When in check, search starts here if (value < singularBeta) { - int corrValAdj1 = std::abs(correctionValue) / 248873; - int corrValAdj2 = std::abs(correctionValue) / 255331; - int doubleMargin = - 262 * PvNode - 188 * !ttCapture - corrValAdj1 - ttMoveHistory / 128; + int corrValAdj1 = std::abs(correctionValue) / 248400; + int corrValAdj2 = std::abs(correctionValue) / 249757; + int doubleMargin = -4 + 244 * PvNode - 206 * !ttCapture - corrValAdj1 + - 997 * ttMoveHistory / 131072; int tripleMargin = - 88 + 265 * PvNode - 256 * !ttCapture + 93 * ss->ttPv - corrValAdj2; + 84 + 269 * PvNode - 253 * !ttCapture + 91 * ss->ttPv - corrValAdj2; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); @@ -1216,49 +1216,49 @@ moves_loop: // When in check, search starts here // Decrease reduction for PvNodes (*Scaler) if (ss->ttPv) - r -= 2381 + PvNode * 1008 + (ttData.value > alpha) * 880 - + (ttData.depth >= depth) * (1022 + cutNode * 1140); + r -= 2437 + PvNode * 926 + (ttData.value > alpha) * 901 + + (ttData.depth >= depth) * (943 + cutNode * 1180); // These reduction adjustments have no proven non-linear scaling - r += 306; // Base reduction offset to compensate for other tweaks + r += 316; // Base reduction offset to compensate for other tweaks r -= moveCount * 66; - r -= std::abs(correctionValue) / 29696; + r -= std::abs(correctionValue) / 28047; - if (PvNode && std::abs(bestValue) <= 2000) + if (PvNode && std::abs(bestValue) <= 2078) r -= risk_tolerance(bestValue); // Increase reduction for cut nodes if (cutNode) - r += 2784 + 1038 * !ttData.move; + r += 2864 + 966 * !ttData.move; // Increase reduction if ttMove is a capture but the current move is not a capture if (ttCapture && !capture) - r += 1171 + (depth < 8) * 985; + r += 1210 + (depth < 8) * 963; // Increase reduction if next ply has a lot of fail high if ((ss + 1)->cutoffCnt > 2) - r += 1042 + allNode * 864; + r += 1036 + allNode * 848; // For first picked move (ttMove) reduce reduction else if (ss->isTTMove) - r -= 1937; + r -= 2006; if (capture) ss->statScore = - 846 * int(PieceValue[pos.captured_piece()]) / 128 + 826 * int(PieceValue[pos.captured_piece()]) / 128 + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())] - - 4822; + - 5030; else if (ss->inCheck) ss->statScore = thisThread->mainHistory[us][move.from_to()] - + (*contHist[0])[movedPiece][move.to_sq()] - 2771; + + (*contHist[0])[movedPiece][move.to_sq()] - 2766; else ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 3271; + + (*contHist[1])[movedPiece][move.to_sq()] - 3206; // Decrease/increase reduction for moves with a good/bad history - r -= ss->statScore * 1582 / 16384; + r -= ss->statScore * 826 / 8192; // Step 17. Late moves reduction / extension (LMR) if (depth >= 2 && moveCount > 1) @@ -1281,7 +1281,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 + 43 + 2 * newDepth); + const bool doDeeperSearch = value > (bestValue + 42 + 2 * newDepth); const bool doShallowerSearch = value < bestValue + 9; newDepth += doDeeperSearch - doShallowerSearch; @@ -1290,12 +1290,12 @@ moves_loop: // When in check, search starts here value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); // Post LMR continuation history updates - update_continuation_histories(ss, movedPiece, move.to_sq(), 1600); + update_continuation_histories(ss, movedPiece, move.to_sq(), 1508); } else if (value > alpha && value < bestValue + 9) { newDepth--; - if (value < bestValue + 3) + if (value < bestValue + 4) newDepth--; } } @@ -1305,7 +1305,7 @@ moves_loop: // When in check, search starts here { // Increase reduction if ttMove is not present if (!ttData.move) - r += 1156; + r += 1128; r -= ttMoveHistory / 8; @@ -1314,7 +1314,7 @@ moves_loop: // When in check, search starts here // Note that if expected reduction is high, we reduce search depth here value = -search(pos, ss + 1, -(alpha + 1), -alpha, - newDepth - (r > 3495) - (r > 5510 && newDepth > 2), !cutNode); + newDepth - (r > 3564) - (r > 4969 && newDepth > 2), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail high, @@ -1463,7 +1463,7 @@ moves_loop: // When in check, search starts here ttData.move, moveCount); if (!PvNode) { - int bonus = ss->isTTMove ? 800 : -870; + int bonus = ss->isTTMove ? 800 : -879; ttMoveHistory << bonus; } } @@ -1471,28 +1471,28 @@ moves_loop: // When in check, search starts here // Bonus for prior quiet countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonusScale = std::min(-(ss - 1)->statScore / 112, 303); - bonusScale += std::min(78 * depth - 312, 194); - bonusScale += 34 * !allNode; - bonusScale += 164 * ((ss - 1)->moveCount > 8); + int bonusScale = std::min(-(ss - 1)->statScore / 113, 293); + bonusScale += std::min(73 * depth - 347, 184); + bonusScale += 33 * !allNode; + bonusScale += 174 * ((ss - 1)->moveCount > 8); bonusScale += 86 * (ss - 1)->isTTMove; - bonusScale += 86 * (ss->cutoffCnt <= 3); - bonusScale += 141 * (!ss->inCheck && bestValue <= ss->staticEval - 100); - bonusScale += 121 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 75); + bonusScale += 90 * (ss->cutoffCnt <= 3); + bonusScale += 144 * (!ss->inCheck && bestValue <= ss->staticEval - 104); + bonusScale += 128 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 82); bonusScale = std::max(bonusScale, 0); - const int scaledBonus = std::min(160 * depth - 99, 1492) * bonusScale; + const int scaledBonus = std::min(159 * depth - 94, 1501) * bonusScale; update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - scaledBonus * 388 / 32768); + scaledBonus * 412 / 32768); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] - << scaledBonus * 212 / 32768; + << scaledBonus * 203 / 32768; 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 * 1055 / 32768; + << scaledBonus * 1040 / 32768; } // Bonus for prior capture countermove that caused the fail low @@ -1500,7 +1500,7 @@ moves_loop: // When in check, search starts here { Piece capturedPiece = pos.captured_piece(); assert(capturedPiece != NO_PIECE); - thisThread->captureHistory[pos.piece_on(prevSq)][prevSq][type_of(capturedPiece)] << 1100; + thisThread->captureHistory[pos.piece_on(prevSq)][prevSq][type_of(capturedPiece)] << 1080; } if (PvNode) @@ -1652,7 +1652,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 359; + futilityBase = ss->staticEval + 376; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1714,11 +1714,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) && (*contHist[0])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= 6290) + <= 6218) continue; // Do not search moves with bad enough SEE values - if (!pos.see_ge(move, -75)) + if (!pos.see_ge(move, -74)) continue; } @@ -1800,7 +1800,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 * 764 / rootDelta + !i * reductionScale * 191 / 512 + 1087; + return reductionScale - delta * 794 / rootDelta + !i * reductionScale * 205 / 512 + 1086; } // elapsed() returns the time elapsed since the search started. If the @@ -1896,35 +1896,35 @@ void update_all_stats(const Position& pos, Piece moved_piece = pos.moved_piece(bestMove); PieceType captured; - int bonus = std::min(141 * depth - 89, 1613) + 311 * (bestMove == ttMove); - int malus = std::min(695 * depth - 184, 2839) - 31 * moveCount; + int bonus = std::min(143 * depth - 89, 1496) + 302 * (bestMove == ttMove); + int malus = std::min(737 * depth - 179, 3141) - 30 * moveCount; if (!pos.capture_stage(bestMove)) { - update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 1129 / 1024); + update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 1059 / 1024); // Decrease stats for all non-best quiet moves for (Move move : quietsSearched) - update_quiet_histories(pos, ss, workerThread, move, -malus * 1246 / 1024); + update_quiet_histories(pos, ss, workerThread, move, -malus * 1310 / 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 * 1187 / 1024; + captureHistory[moved_piece][bestMove.to_sq()][captured] << bonus * 1213 / 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 * 987 / 1024); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * 980 / 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 * 1377 / 1024; + captureHistory[moved_piece][move.to_sq()][captured] << -malus * 1388 / 1024; } } @@ -1933,7 +1933,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, 1103}, {2, 659}, {3, 323}, {4, 533}, {5, 121}, {6, 474}}}; + {{1, 1092}, {2, 631}, {3, 294}, {4, 517}, {5, 126}, {6, 445}}}; for (const auto [i, weight] : conthist_bonuses) { @@ -1954,14 +1954,14 @@ 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 * 800 / 1024; + workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 792 / 1024; update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), - bonus * (bonus > 0 ? 1094 : 790) / 1024); + bonus * (bonus > 0 ? 1082 : 784) / 1024); int pIndex = pawn_structure_index(pos); workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] - << bonus * (bonus > 0 ? 725 : 460) / 1024; + << bonus * (bonus > 0 ? 705 : 450) / 1024; } } From 0f905b4e88dba8dac26d7c457707507eb8479477 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Mon, 21 Apr 2025 08:25:18 +0200 Subject: [PATCH 032/107] Preserve all moves in movepicker Simplifies method otherPieceTypesMobile quite a bit and makes it more precise. More precise because capturesSearched list not always contains all processed captures: although extremely rare, it can happen that a 'good' capture get pruned at step 14 and doesn't make it to capturesSearched. (functional change at higher depths) passed STC-simplification bounds https://tests.stockfishchess.org/tests/view/68025974cd501869c6698153 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 273664 W: 15658 L: 15681 D: 242325 Ptnml(0-2): 166, 10368, 115802, 10315, 181 passed LTC-simplification bounds https://tests.stockfishchess.org/tests/view/6804b86acd501869c6698673 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 96780 W: 24547 L: 24419 D: 47814 Ptnml(0-2): 30, 8466, 31286, 8562, 46 Applied changes requested by disservin & retested to be on the safe side STC: https://tests.stockfishchess.org/tests/view/6806110698cd372e3aea5919 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 107392 W: 27739 L: 27606 D: 52047 Ptnml(0-2): 266, 10867, 31306, 10982, 275 LTC: https://tests.stockfishchess.org/tests/view/6806346198cd372e3aea5965 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 233484 W: 59106 L: 59103 D: 115275 Ptnml(0-2): 116, 22787, 70939, 22778, 122 closes https://github.com/official-stockfish/Stockfish/pull/6005 Bench: 1857323 --- src/movepick.cpp | 42 +++++++++++++++++++----------------------- src/movepick.h | 8 ++------ src/search.cpp | 9 +++++---- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index c8738c568..0360c2bde 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -19,8 +19,8 @@ #include "movepick.h" #include -#include #include +#include #include "bitboard.h" #include "misc.h" @@ -223,6 +223,7 @@ top: case QSEARCH_TT : case PROBCUT_TT : ++stage; + cur = moves + 1; return ttMove; case CAPTURE_INIT : @@ -238,9 +239,12 @@ top: case GOOD_CAPTURE : if (select([&]() { - // Move losing capture to endBadCaptures to be tried later - return pos.see_ge(*cur, -cur->value / 18) ? true - : (*endBadCaptures++ = *cur, false); + if (!pos.see_ge(*cur, -cur->value / 18)) + { + std::swap(*endBadCaptures++, *cur); + return false; + } + return true; })) return *(cur - 1); @@ -250,7 +254,6 @@ top: case QUIET_INIT : if (!skipQuiets) { - cur = endBadCaptures; endMoves = beginBadQuiets = endBadQuiets = generate(pos, cur); score(); @@ -317,30 +320,23 @@ top: void MovePicker::skip_quiet_moves() { skipQuiets = true; } -bool MovePicker::otherPieceTypesMobile(PieceType pt, ValueList& capturesSearched) { - if (stage != GOOD_QUIET && stage != BAD_QUIET) - return true; +bool MovePicker::other_piece_types_mobile(PieceType pt) { + assert(stage == GOOD_QUIET || stage == BAD_QUIET || stage == EVASION); - // verify good captures - for (std::size_t i = 0; i < capturesSearched.size(); i++) - if (type_of(pos.moved_piece(capturesSearched[i])) != pt) + // verify all generated captures and quiets + for (ExtMove* m = moves; m < endMoves; ++m) + { + if (*m && type_of(pos.moved_piece(*m)) != pt) { - if (type_of(pos.moved_piece(capturesSearched[i])) != KING) + if (type_of(pos.moved_piece(*m)) != KING) return true; - if (pos.legal(capturesSearched[i])) - return true; - } - - // now verify bad captures and quiets - for (ExtMove* c = moves; c < endBadQuiets; ++c) - if (type_of(pos.moved_piece(*c)) != pt) - { - if (type_of(pos.moved_piece(*c)) != KING) - return true; - if (pos.legal(*c)) + if (pos.legal(*m)) return true; } + } return false; } +void MovePicker::mark_current_illegal() { *(cur - 1) = Move::none(); } + } // namespace Stockfish diff --git a/src/movepick.h b/src/movepick.h index dfafe69a5..72a6f4e1c 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -19,8 +19,6 @@ #ifndef MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED -#include - #include "history.h" #include "movegen.h" #include "types.h" @@ -29,9 +27,6 @@ namespace Stockfish { class Position; -template -class ValueList; - // The MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which emits one // new pseudo-legal move on every call, until there are no moves left, when @@ -55,7 +50,8 @@ class MovePicker { MovePicker(const Position&, Move, int, const CapturePieceToHistory*); Move next_move(); void skip_quiet_moves(); - bool otherPieceTypesMobile(PieceType pt, ValueList& capturesSearched); + bool other_piece_types_mobile(PieceType pt); + void mark_current_illegal(); private: template diff --git a/src/search.cpp b/src/search.cpp index f673ba340..05d522b19 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1011,8 +1011,10 @@ moves_loop: // When in check, search starts here // Check for legality if (!pos.legal(move)) + { + mp.mark_current_illegal(); continue; - + } // At root obey the "searchmoves" option and skip moves not listed in Root // Move List. In MultiPV mode we also skip PV moves that have been already // searched and those of lower "TB rank" if we are in a TB root position. @@ -1084,9 +1086,8 @@ moves_loop: // When in check, search starts here && pos.non_pawn_material(us) == PieceValue[movedPiece] && PieceValue[movedPiece] >= RookValue && !(PseudoAttacks[KING][pos.square(us)] & move.from_sq())) - skip = mp.otherPieceTypesMobile( - type_of(movedPiece), - capturesSearched); // if the opponent captures last mobile piece it might be stalemate + // if the opponent captures last mobile piece it might be stalemate + skip = mp.other_piece_types_mobile(type_of(movedPiece)); if (skip) continue; From ed6b8d179a42414151fa1da69cebe1f2c8cfdd20 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 28 Apr 2025 16:46:30 +0300 Subject: [PATCH 033/107] Refactor futility_margin a small term to the futility calculation that depends on eval - beta. Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 203136 W: 52797 L: 52239 D: 98100 Ptnml(0-2): 549, 23827, 52255, 24391, 546 https://tests.stockfishchess.org/tests/view/680e84a43629b02d74b15e2e Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 100356 W: 25950 L: 25507 D: 48899 Ptnml(0-2): 35, 10683, 28302, 11120, 38 https://tests.stockfishchess.org/tests/view/680ebcb03629b02d74b16040 closes https://github.com/official-stockfish/Stockfish/pull/6009 closes https://github.com/official-stockfish/Stockfish/pull/6041 Bench: 1815939 Co-authored-by: Michael Chaly Co-authored-by: xu-shawn <50402888+xu-shawn@users.noreply.github.com> --- src/search.cpp | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 05d522b19..870cff54b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -70,23 +70,6 @@ namespace { // so changing them or adding conditions that are similar requires // tests at these types of time controls. -// Futility margin -Value futility_margin(Depth d, - bool noTtCutNode, - bool improving, - bool oppWorsening, - int statScore, - int correctionValue) { - Value futilityMult = 105 - 23 * noTtCutNode; - Value improvingDeduction = improving * futilityMult * 2; - Value worseningDeduction = oppWorsening * futilityMult / 3; - Value statScoreAddition = statScore / 335; - Value correctionAddition = correctionValue / 149902; - - return futilityMult * d - improvingDeduction - worseningDeduction + statScoreAddition - + correctionAddition; -} - constexpr int futility_move_count(bool improving, Depth depth) { return (3 + depth * depth) / (2 - improving); } @@ -864,13 +847,23 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node // 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, std::abs(correctionValue)) - >= beta - && eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval)) - return beta + (eval - beta) / 3; + { + auto futility_margin = [&](Depth d) { + Value futilityMult = 105 - 23 * (cutNode && !ss->ttHit); + Value improvingDeduction = improving * futilityMult * 2; + Value worseningDeduction = opponentWorsening * futilityMult / 3; + + return futilityMult * d // + - improvingDeduction // + - worseningDeduction // + + (ss - 1)->statScore / 335 // + + std::abs(correctionValue) / 149902; + }; + + if (!ss->ttPv && depth < 14 && eval + (eval - beta) / 8 - futility_margin(depth) >= beta + && eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval)) + return beta + (eval - beta) / 3; + } // Step 9. Null move search with verification search if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta From 63c6f22627136dc1d900b5fef1c2cb415ab855e1 Mon Sep 17 00:00:00 2001 From: Carlos Esparza Date: Fri, 11 Apr 2025 11:58:20 -0700 Subject: [PATCH 034/107] Simplify DirtyPiece Simplifies the DirtyPiece struct. passed STC: https://tests.stockfishchess.org/tests/view/68054c9798cd372e3aea05d7 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 36608 W: 9641 L: 9437 D: 17530 Ptnml(0-2): 89, 3630, 10668, 3822, 95 passed LTC: https://tests.stockfishchess.org/tests/view/6805514598cd372e3aea0783 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 127620 W: 31993 L: 31894 D: 63733 Ptnml(0-2): 42, 10983, 41665, 11074, 46 closes https://github.com/official-stockfish/Stockfish/pull/6016 No functional change --- src/nnue/features/half_ka_v2_hm.cpp | 18 ++++++------ src/nnue/nnue_accumulator.cpp | 12 ++++---- src/position.cpp | 44 ++++++++++++++--------------- src/types.h | 18 +++++------- 4 files changed, 44 insertions(+), 48 deletions(-) diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index eb3c7e6a7..c91da2cc8 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -58,13 +58,15 @@ void HalfKAv2_hm::append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added) { - for (int i = 0; i < dp.dirty_num; ++i) - { - if (dp.from[i] != SQ_NONE) - removed.push_back(make_index(dp.from[i], dp.piece[i], ksq)); - if (dp.to[i] != SQ_NONE) - added.push_back(make_index(dp.to[i], dp.piece[i], ksq)); - } + removed.push_back(make_index(dp.from, dp.pc, ksq)); + if (dp.to != SQ_NONE) + added.push_back(make_index(dp.to, dp.pc, ksq)); + + if (dp.remove_sq != SQ_NONE) + removed.push_back(make_index(dp.remove_sq, dp.remove_pc, ksq)); + + if (dp.add_sq != SQ_NONE) + added.push_back(make_index(dp.add_sq, dp.add_pc, ksq)); } // Explicit template instantiations @@ -78,7 +80,7 @@ template void HalfKAv2_hm::append_changed_indices(Square ksq, IndexList& added); bool HalfKAv2_hm::requires_refresh(const DirtyPiece& dirtyPiece, Color perspective) { - return dirtyPiece.piece[0] == make_piece(perspective, KING); + return dirtyPiece.pc == make_piece(perspective, KING); } } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/nnue_accumulator.cpp b/src/nnue/nnue_accumulator.cpp index 5c1128539..831284364 100644 --- a/src/nnue/nnue_accumulator.cpp +++ b/src/nnue/nnue_accumulator.cpp @@ -152,16 +152,16 @@ void AccumulatorStack::forward_update_incremental( { if (next + 1 < size) { - auto& dp1 = accumulators[next].dirtyPiece; - auto& dp2 = accumulators[next + 1].dirtyPiece; + DirtyPiece& dp1 = accumulators[next].dirtyPiece; + DirtyPiece& dp2 = accumulators[next + 1].dirtyPiece; - if (dp2.dirty_num >= 2 && dp1.piece[0] == dp2.piece[1] && dp1.to[0] == dp2.from[1]) + if (dp1.to != SQ_NONE && dp1.to == dp2.remove_sq) { - const Square captureSq = dp1.to[0]; - dp1.to[0] = dp2.from[1] = SQ_NONE; + const Square captureSq = dp1.to; + dp1.to = dp2.remove_sq = SQ_NONE; double_inc_update(featureTransformer, ksq, accumulators[next], accumulators[next + 1], accumulators[next - 1]); - dp1.to[0] = dp2.from[1] = captureSq; + dp1.to = dp2.remove_sq = captureSq; next++; continue; diff --git a/src/position.cpp b/src/position.cpp index 85ade69a9..9f023b5fe 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -706,9 +706,6 @@ DirtyPiece Position::do_move(Move m, ++st->rule50; ++st->pliesFromNull; - DirtyPiece dp; - dp.dirty_num = 1; - Color us = sideToMove; Color them = ~us; Square from = m.from_sq(); @@ -716,6 +713,12 @@ DirtyPiece Position::do_move(Move m, Piece pc = piece_on(from); Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); + DirtyPiece dp; + dp.pc = pc; + dp.from = from; + dp.to = to; + dp.add_sq = SQ_NONE; + assert(color_of(pc) == us); assert(captured == NO_PIECE || color_of(captured) == (m.type_of() != CASTLING ? them : us)); assert(type_of(captured) != KING); @@ -762,10 +765,8 @@ DirtyPiece Position::do_move(Move m, st->minorPieceKey ^= Zobrist::psq[captured][capsq]; } - dp.dirty_num = 2; // 1 piece moved, 1 piece captured - dp.piece[1] = captured; - dp.from[1] = capsq; - dp.to[1] = SQ_NONE; + dp.remove_pc = captured; + dp.remove_sq = capsq; // Update board and piece lists remove_piece(capsq); @@ -776,6 +777,8 @@ DirtyPiece Position::do_move(Move m, // Reset rule 50 counter st->rule50 = 0; } + else + dp.remove_sq = SQ_NONE; // Update hash key k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; @@ -798,9 +801,6 @@ DirtyPiece Position::do_move(Move m, // Move the piece. The tricky Chess960 castling is handled earlier if (m.type_of() != CASTLING) { - dp.piece[0] = pc; - dp.from[0] = from; - dp.to[0] = to; move_piece(from, to); } @@ -827,12 +827,9 @@ DirtyPiece Position::do_move(Move m, remove_piece(to); put_piece(promotion, to); - // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE - dp.to[0] = SQ_NONE; - dp.piece[dp.dirty_num] = promotion; - dp.from[dp.dirty_num] = SQ_NONE; - dp.to[dp.dirty_num] = to; - dp.dirty_num++; + dp.add_pc = promotion; + dp.add_sq = to; + dp.to = SQ_NONE; // Update hash keys // Zobrist::psq[pc][to] is zero, so we don't need to clear it @@ -899,6 +896,10 @@ DirtyPiece Position::do_move(Move m, assert(pos_is_ok()); + assert(dp.pc != NO_PIECE); + assert(!(bool(captured) || m.type_of() == CASTLING) ^ (dp.remove_sq != SQ_NONE)); + assert(dp.from != SQ_NONE); + assert(!(dp.add_sq != SQ_NONE) ^ (m.type_of() == PROMOTION || m.type_of() == CASTLING)); return dp; } @@ -981,13 +982,10 @@ void Position::do_castling( if (Do) { - dp->piece[0] = make_piece(us, KING); - dp->from[0] = from; - dp->to[0] = to; - dp->piece[1] = make_piece(us, ROOK); - dp->from[1] = rfrom; - dp->to[1] = rto; - dp->dirty_num = 2; + dp->to = to; + dp->remove_pc = dp->add_pc = make_piece(us, ROOK); + dp->remove_sq = rfrom; + dp->add_sq = rto; } // Remove both pieces first since squares could overlap in Chess960 diff --git a/src/types.h b/src/types.h index a76d00336..6cd6ee744 100644 --- a/src/types.h +++ b/src/types.h @@ -276,18 +276,14 @@ enum Rank : int { // Keep track of what a move changes on the board (used by NNUE) struct DirtyPiece { + Piece pc; // this is never allowed to be NO_PIECE + Square from, to; // to should be SQ_NONE for promotions - // Number of changed pieces - int dirty_num; - - // Max 3 pieces can change in one move. A promotion with capture moves - // both the pawn and the captured piece to SQ_NONE and the piece promoted - // to from SQ_NONE to the capture square. - Piece piece[3]; - - // From and to squares, which may be SQ_NONE - Square from[3]; - Square to[3]; + // if {add,remove}_sq is SQ_NONE, {add,remove}_pc is allowed to be + // uninitialized + // castling uses add_sq and remove_sq to remove and add the rook + Square remove_sq, add_sq; + Piece remove_pc, add_pc; }; #define ENABLE_INCR_OPERATORS_ON(T) \ From 81cc004060fd8e2b32286203e0366d0b5abdf8c8 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 27 Apr 2025 01:49:43 -0700 Subject: [PATCH 035/107] Remove risk tolerance Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 379328 W: 97567 L: 97724 D: 184037 Ptnml(0-2): 909, 44861, 98314, 44638, 942 https://tests.stockfishchess.org/tests/view/680defc63629b02d74b15b62 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 160752 W: 40762 L: 40685 D: 79305 Ptnml(0-2): 60, 17548, 45091, 17609, 68 https://tests.stockfishchess.org/tests/view/680e8ff43629b02d74b15e65 closes https://github.com/official-stockfish/Stockfish/pull/6037 Bench: 1897340 --- src/search.cpp | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 870cff54b..12de7a679 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -116,29 +115,6 @@ void update_correction_history(const Position& pos, << bonus * 141 / 128; } -int risk_tolerance(Value v) { - // Returns (some constant of) second derivative of sigmoid. - static constexpr auto sigmoid_d2 = [](int x, int y) { - return 631760 * x / ((x * x + 3 * y * y) * y); - }; - - // a and b are the crude approximation of the wdl model. - // The win rate is: 1/(1+exp((a-v)/b)) - // The loss rate is 1/(1+exp((v+a)/b)) - int a = 340; - int b = 122; - - // guard against overflow - assert(abs(v) + a <= std::numeric_limits::max() / 644800); - - // The risk utility is therefore d/dv^2 (1/(1+exp(-(v-a)/b)) -1/(1+exp(-(-v-a)/b))) - // -115200x/(x^2+3) = -345600(ab) / (a^2+3b^2) (multiplied by some constant) (second degree pade approximant) - int winning_risk = sigmoid_d2(v - a, b); - int losing_risk = sigmoid_d2(v + a, b); - - return -(winning_risk + losing_risk) * 32; -} - // 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); } Value value_to_tt(Value v, int ply); @@ -1219,9 +1195,6 @@ moves_loop: // When in check, search starts here r -= moveCount * 66; r -= std::abs(correctionValue) / 28047; - if (PvNode && std::abs(bestValue) <= 2078) - r -= risk_tolerance(bestValue); - // Increase reduction for cut nodes if (cutNode) r += 2864 + 966 * !ttData.move; From b73c8982df3da4a811845212040654342886a79e Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 28 Apr 2025 02:48:31 +0300 Subject: [PATCH 036/107] Another scaling revert Revert recent passer because it seems to not scale for longer time controls. Adjust comments accordingly. Passed VVLTC SPRT with STC bounds: https://tests.stockfishchess.org/tests/view/680ddff73629b02d74b15b3f LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 198316 W: 51317 L: 50846 D: 96153 Ptnml(0-2): 17, 18459, 61737, 18926, 19 Passed VVLTC SPRT with LTC bounds: https://tests.stockfishchess.org/tests/view/680d5b7e3629b02d74b15a21 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 123274 W: 31738 L: 31283 D: 60253 Ptnml(0-2): 7, 11444, 38282, 11895, 9 closes https://github.com/official-stockfish/Stockfish/pull/6038 Bench: 2173845 --- src/search.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 12de7a679..b7c6d8e3b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1244,6 +1244,8 @@ moves_loop: // When in check, search starts here ss->reduction = 0; // Do a full-depth search when reduced LMR search fails high + // (*Scaler) Usually doing more shallower searches + // doesn't scale well to longer TCs if (value > alpha && d < newDepth) { // Adjust full-depth search based on LMR results - if the result was @@ -1260,11 +1262,7 @@ moves_loop: // When in check, search starts here update_continuation_histories(ss, movedPiece, move.to_sq(), 1508); } else if (value > alpha && value < bestValue + 9) - { newDepth--; - if (value < bestValue + 4) - newDepth--; - } } // Step 18. Full-depth search when LMR is skipped From 7afd9e859df87625e3474415dcf49ef0959d238e Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 27 Apr 2025 18:13:58 -0700 Subject: [PATCH 037/107] Simplify MovePicker::score() by using piece types for clarity STC https://tests.stockfishchess.org/tests/view/680ed6cc3629b02d74b160bf LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 200128 W: 51838 L: 51802 D: 96488 Ptnml(0-2): 457, 19956, 59247, 19902, 502 closes https://github.com/official-stockfish/Stockfish/pull/6039 No functional change --- src/movepick.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 0360c2bde..39caf8e67 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -126,20 +126,20 @@ void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); - [[maybe_unused]] Bitboard threatenedPieces, threatByLesser[4]; + [[maybe_unused]] Bitboard threatenedPieces, threatByLesser[QUEEN + 1]; if constexpr (Type == QUIETS) { Color us = pos.side_to_move(); - threatByLesser[0] = threatByLesser[1] = pos.attacks_by(~us); - threatByLesser[2] = - pos.attacks_by(~us) | pos.attacks_by(~us) | threatByLesser[0]; - threatByLesser[3] = pos.attacks_by(~us) | threatByLesser[2]; + threatByLesser[KNIGHT] = threatByLesser[BISHOP] = pos.attacks_by(~us); + threatByLesser[ROOK] = + pos.attacks_by(~us) | pos.attacks_by(~us) | threatByLesser[KNIGHT]; + threatByLesser[QUEEN] = pos.attacks_by(~us) | threatByLesser[ROOK]; // Pieces threatened by pieces of lesser material value - threatenedPieces = (pos.pieces(us, QUEEN) & threatByLesser[3]) - | (pos.pieces(us, ROOK) & threatByLesser[2]) - | (pos.pieces(us, KNIGHT, BISHOP) & threatByLesser[0]); + threatenedPieces = (pos.pieces(us, QUEEN) & threatByLesser[QUEEN]) + | (pos.pieces(us, ROOK) & threatByLesser[ROOK]) + | (pos.pieces(us, KNIGHT, BISHOP) & threatByLesser[KNIGHT]); } for (auto& m : *this) @@ -173,12 +173,11 @@ void MovePicker::score() { // penalty for moving to a square threatened by a lesser piece // or bonus for escaping an attack by a lesser piece. - constexpr int bonus[4] = {144, 144, 256, 517}; if (KNIGHT <= pt && pt <= QUEEN) { - auto i = pt - 2; - int v = (threatByLesser[i] & to ? -95 : 100 * bool(threatByLesser[i] & from)); - m.value += bonus[i] * v; + static constexpr int bonus[QUEEN + 1] = {0, 0, 144, 144, 256, 517}; + int v = (threatByLesser[pt] & to ? -95 : 100 * bool(threatByLesser[pt] & from)); + m.value += bonus[pt] * v; } if (ply < LOW_PLY_HISTORY_SIZE) From e9925b122f2704c9fdb719110bfaaae7253c3f4c Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 27 Apr 2025 20:12:25 -0700 Subject: [PATCH 038/107] Simplify ttCapture LMR Passed Non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 51104 W: 13389 L: 13184 D: 24531 Ptnml(0-2): 182, 5940, 13068, 6215, 147 https://tests.stockfishchess.org/tests/view/680ef2503629b02d74b16498 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 73350 W: 18804 L: 18638 D: 35908 Ptnml(0-2): 30, 7906, 20639, 8068, 32 https://tests.stockfishchess.org/tests/view/6810510e3629b02d74b1668a closes https://github.com/official-stockfish/Stockfish/pull/6044 Bench: 1805151 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b7c6d8e3b..81c25fecc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1200,7 +1200,7 @@ moves_loop: // When in check, search starts here r += 2864 + 966 * !ttData.move; // Increase reduction if ttMove is a capture but the current move is not a capture - if (ttCapture && !capture) + if (ttCapture) r += 1210 + (depth < 8) * 963; // Increase reduction if next ply has a lot of fail high From 63a2ab1510d81b68614d268234291982249bbe77 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 3 May 2025 14:21:47 -0700 Subject: [PATCH 039/107] Simplify Captures scoring STC https://tests.stockfishchess.org/tests/view/680ee3b43629b02d74b160f3 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 200896 W: 52089 L: 52048 D: 96759 Ptnml(0-2): 592, 23821, 51589, 23846, 600 LTC https://tests.stockfishchess.org/tests/view/68106adf3629b02d74b166b1 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 128856 W: 33026 L: 32917 D: 62913 Ptnml(0-2): 54, 13916, 36384, 14015, 59 Replaces discovered checks by direct checks which are simpler and more common. It's also what's used for scoring quiets. closes https://github.com/official-stockfish/Stockfish/pull/6047 Bench: 1886966 --- src/movepick.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 39caf8e67..882ca67d5 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -145,11 +145,8 @@ void MovePicker::score() { for (auto& m : *this) if constexpr (Type == CAPTURES) m.value = - (2 - * (pos.blockers_for_king(~pos.side_to_move()) & m.from_sq() - && !aligned(m.from_sq(), m.to_sq(), pos.square(~pos.side_to_move()))) - + 7) - * int(PieceValue[pos.piece_on(m.to_sq())]) + 7 * int(PieceValue[pos.piece_on(m.to_sq())]) + + 1024 * bool(pos.check_squares(type_of(pos.moved_piece(m))) & m.to_sq()) + (*captureHistory)[pos.moved_piece(m)][m.to_sq()][type_of(pos.piece_on(m.to_sq()))]; else if constexpr (Type == QUIETS) From d4b405a5a6ab47610d7a1b93645ea4a85f941086 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 4 May 2025 20:02:58 +0300 Subject: [PATCH 040/107] Remove a cutNode condition Allow some nodes to spawn even deeper lmr searches, also for cutNodes Passed STC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 59072 W: 15387 L: 15190 D: 28495 Ptnml(0-2): 145, 6956, 15166, 7095, 174 https://tests.stockfishchess.org/tests/view/6810b4c13629b02d74b16714 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 93294 W: 23737 L: 23591 D: 45966 Ptnml(0-2): 47, 10116, 26169, 10274, 41 https://tests.stockfishchess.org/tests/view/681170613629b02d74b167f3 closes https://github.com/official-stockfish/Stockfish/pull/6052 Bench: 1937565 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 81c25fecc..2cab7bd9d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1237,7 +1237,7 @@ moves_loop: // When in check, search starts here // std::clamp has been replaced by a more robust implementation. Depth d = std::max(1, std::min(newDepth - r / 1024, newDepth + !allNode + (PvNode && !bestMove))) - + (!cutNode && (ss - 1)->isPvNode && moveCount < 8); + + ((ss - 1)->isPvNode && moveCount < 8); ss->reduction = newDepth - d; value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); From 05e39527a89c9de535c7d3eab67accb3b307c9bd Mon Sep 17 00:00:00 2001 From: gab8192 Date: Wed, 30 Apr 2025 15:44:34 +0200 Subject: [PATCH 041/107] Simplify low-ply history weight Passed Non-regression STC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 116064 W: 30095 L: 29959 D: 56010 Ptnml(0-2): 333, 13753, 29731, 13875, 340 https://tests.stockfishchess.org/tests/view/681229f53629b02d74b168d3 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 172824 W: 44096 L: 44031 D: 84697 Ptnml(0-2): 91, 18868, 48410, 18971, 72 https://tests.stockfishchess.org/tests/view/681474243629b02d74b16b44 closes https://github.com/official-stockfish/Stockfish/pull/6053 Bench: 2064179 --- src/movepick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 882ca67d5..1c278147f 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -178,7 +178,7 @@ void MovePicker::score() { } if (ply < LOW_PLY_HISTORY_SIZE) - m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + 2 * ply); + m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply); } else // Type == EVASIONS From 40ef7b1212c5a055c20a9c184f0bdf999c33c944 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 8 May 2025 19:43:55 +0300 Subject: [PATCH 042/107] Simplify probcut Passed STC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 80800 W: 20947 L: 20774 D: 39079 Ptnml(0-2): 217, 9446, 20906, 9609, 222 https://tests.stockfishchess.org/tests/view/680e83163629b02d74b15e2a Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 359004 W: 91362 L: 91486 D: 176156 Ptnml(0-2): 177, 39133, 101007, 39007, 178 https://tests.stockfishchess.org/tests/view/680e95db3629b02d74b15e7a closes https://github.com/official-stockfish/Stockfish/pull/6054 Bench: 2060860 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 2cab7bd9d..45515555e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -952,7 +952,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea - probCutBeta = beta + 180 + depth * 20; + probCutBeta = beta + 400; 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; From 1f9af9966fd5be2aefd33ab99d1cb07b8415c3d1 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Thu, 8 May 2025 12:10:51 -0700 Subject: [PATCH 043/107] Simplify futility pruning term Passed simplification STC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 49920 W: 12933 L: 12727 D: 24260 Ptnml(0-2): 141, 5865, 12752, 6051, 151 https://tests.stockfishchess.org/tests/view/681d01d33629b02d74b1756e Passed simplification LTC LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 68808 W: 17573 L: 17402 D: 33833 Ptnml(0-2): 23, 7343, 19511, 7494, 33 https://tests.stockfishchess.org/tests/view/681e33843629b02d74b176b1 closes https://github.com/official-stockfish/Stockfish/pull/6058 Bench: 2041086 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 45515555e..7c83cae24 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1077,9 +1077,8 @@ moves_loop: // When in check, search starts here lmrDepth += history / 3388; - Value futilityValue = - ss->staticEval + (bestMove ? 46 : 138) + 117 * lmrDepth - + 102 * (bestValue < ss->staticEval - 127 && ss->staticEval > alpha - 50); + Value futilityValue = ss->staticEval + (bestMove ? 46 : 138) + 117 * lmrDepth + + 102 * (ss->staticEval > alpha); // Futility pruning: parent node // (*Scaler): Generally, more frequent futility pruning From b5f11085dd49d1132a9c807a458156d2f3dd199d Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 27 Apr 2025 12:16:19 -0700 Subject: [PATCH 044/107] Do more extensions in nodes far from the root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parameters found by @FauziAkram in the latest tune. Passed VVLTC w/ LTC Bound: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 243264 W: 63452 L: 62774 D: 117038 Ptnml(0-2): 18, 22494, 75934, 23164, 22 https://tests.stockfishchess.org/tests/view/680e82b23629b02d74b15e27 Passed VVLTC w/ STC Bound: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 37838 W: 9935 L: 9667 D: 18236 Ptnml(0-2): 6, 3383, 11873, 3651, 6 https://tests.stockfishchess.org/tests/view/681e707a3629b02d74b176e8 STC Elo Estimate: Elo: -0.49 ± 2.4 (95%) LOS: 34.8% Total: 20000 W: 5118 L: 5146 D: 9736 Ptnml(0-2): 55, 2365, 5182, 2349, 49 nElo: -0.96 ± 4.8 (95%) PairsRatio: 0.99 https://tests.stockfishchess.org/tests/view/6822af4b3629b02d74b17be6 closes https://github.com/official-stockfish/Stockfish/pull/6059 Bench: 2135382 --- src/search.cpp | 114 ++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 59 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7c83cae24..d5e58eb5b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1100,72 +1100,68 @@ moves_loop: // When in check, search starts here } // Step 15. Extensions - // We take care to not overdo to avoid search getting stuck. - if (ss->ply < thisThread->rootDepth * 2) + // Singular extension search. If all moves but one + // fail low on a search of (alpha-s, beta-s), and just one fails high on + // (alpha, beta), then that move is singular and should be extended. To + // verify this we do a reduced search on the position excluding the ttMove + // and if the result is lower than ttValue minus a margin, then we will + // extend the ttMove. Recursive singular search is avoided. + + // (*Scaler) Generally, higher singularBeta (i.e closer to ttValue) + // and lower extension margins scale well. + + if (!rootNode && move == ttData.move && !excludedMove + && depth >= 6 - (thisThread->completedDepth > 27) + ss->ttPv && is_valid(ttData.value) + && !is_decisive(ttData.value) && (ttData.bound & BOUND_LOWER) + && ttData.depth >= depth - 3) { - // Singular extension search. If all moves but one - // fail low on a search of (alpha-s, beta-s), and just one fails high on - // (alpha, beta), then that move is singular and should be extended. To - // verify this we do a reduced search on the position excluding the ttMove - // and if the result is lower than ttValue minus a margin, then we will - // extend the ttMove. Recursive singular search is avoided. + Value singularBeta = ttData.value - (58 + 76 * (ss->ttPv && !PvNode)) * depth / 57; + Depth singularDepth = newDepth / 2; - // (*Scaler) Generally, higher singularBeta (i.e closer to ttValue) - // and lower extension margins scale well. + ss->excludedMove = move; + value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); + ss->excludedMove = Move::none(); - if (!rootNode && move == ttData.move && !excludedMove - && depth >= 6 - (thisThread->completedDepth > 27) + ss->ttPv - && is_valid(ttData.value) && !is_decisive(ttData.value) - && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3) + if (value < singularBeta) { - Value singularBeta = ttData.value - (58 + 76 * (ss->ttPv && !PvNode)) * depth / 57; - Depth singularDepth = newDepth / 2; + int corrValAdj1 = std::abs(correctionValue) / 248400; + int corrValAdj2 = std::abs(correctionValue) / 249757; + int doubleMargin = -4 + 244 * PvNode - 206 * !ttCapture - corrValAdj1 + - 997 * ttMoveHistory / 131072 + - (ss->ply * 2 > thisThread->rootDepth * 3) * 47; + int tripleMargin = 84 + 269 * PvNode - 253 * !ttCapture + 91 * ss->ttPv + - corrValAdj2 - (ss->ply * 2 > thisThread->rootDepth * 3) * 54; - ss->excludedMove = move; - value = - search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); - ss->excludedMove = Move::none(); + extension = + 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); - if (value < singularBeta) - { - int corrValAdj1 = std::abs(correctionValue) / 248400; - int corrValAdj2 = std::abs(correctionValue) / 249757; - int doubleMargin = -4 + 244 * PvNode - 206 * !ttCapture - corrValAdj1 - - 997 * ttMoveHistory / 131072; - int tripleMargin = - 84 + 269 * PvNode - 253 * !ttCapture + 91 * ss->ttPv - corrValAdj2; - - extension = 1 + (value < singularBeta - doubleMargin) - + (value < singularBeta - tripleMargin); - - depth++; - } - - // Multi-cut pruning - // Our ttMove is assumed to fail high based on the bound of the TT entry, - // and if after excluding the ttMove with a reduced search we fail high - // over the original beta, we assume this expected cut-node is not - // singular (multiple moves fail high), and we can prune the whole - // subtree by returning a softbound. - else if (value >= beta && !is_decisive(value)) - return value; - - // Negative extensions - // If other moves failed high over (ttValue - margin) without the - // ttMove on a reduced search, but we cannot do multi-cut because - // (ttValue - margin) is lower than the original beta, we do not know - // if the ttMove is singular or can do a multi-cut, so we reduce the - // ttMove in favor of other moves based on some conditions: - - // If the ttMove is assumed to fail high over current beta - else if (ttData.value >= beta) - extension = -3; - - // If we are on a cutNode but the ttMove is not assumed to fail high - // over current beta - else if (cutNode) - extension = -2; + depth++; } + + // Multi-cut pruning + // Our ttMove is assumed to fail high based on the bound of the TT entry, + // and if after excluding the ttMove with a reduced search we fail high + // over the original beta, we assume this expected cut-node is not + // singular (multiple moves fail high), and we can prune the whole + // subtree by returning a softbound. + else if (value >= beta && !is_decisive(value)) + return value; + + // Negative extensions + // If other moves failed high over (ttValue - margin) without the + // ttMove on a reduced search, but we cannot do multi-cut because + // (ttValue - margin) is lower than the original beta, we do not know + // if the ttMove is singular or can do a multi-cut, so we reduce the + // ttMove in favor of other moves based on some conditions: + + // If the ttMove is assumed to fail high over current beta + else if (ttData.value >= beta) + extension = -3; + + // If we are on a cutNode but the ttMove is not assumed to fail high + // over current beta + else if (cutNode) + extension = -2; } // Step 16. Make the move From c4e2479a75ae34a620f68cfb686d04ce36709ab6 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 13 May 2025 04:06:51 +0300 Subject: [PATCH 045/107] Introducing a depth component to the penalty. Passed STC: LLR: 2.97 (-2.94,2.94) <0.00,2.00> Total: 31648 W: 8358 L: 8050 D: 15240 Ptnml(0-2): 78, 3596, 8182, 3876, 92 https://tests.stockfishchess.org/tests/view/680fa73d3629b02d74b165a9 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 177720 W: 45524 L: 44920 D: 87276 Ptnml(0-2): 91, 19130, 49813, 19736, 90 https://tests.stockfishchess.org/tests/view/68109e2c3629b02d74b166ee closes https://github.com/official-stockfish/Stockfish/pull/6060 Bench: 2251724 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d5e58eb5b..6827f5127 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1877,7 +1877,8 @@ void update_all_stats(const Position& pos, // 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 * 980 / 1024); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, + -malus * (512 + depth * 16) / 1024); // Decrease stats for all non-best capture moves for (Move move : capturesSearched) From 07f6edf93426fab8edb274232f9a40e46ea3d961 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 2 May 2025 18:32:55 +0300 Subject: [PATCH 046/107] Refactor Position::pseudo_legal Pawn Move Check use intermediate variables to make the statement easier to read closes https://github.com/official-stockfish/Stockfish/pull/6045 No functional change --- src/position.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 9f023b5fe..5e2c27822 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -597,10 +597,14 @@ bool Position::pseudo_legal(const Move m) const { if ((Rank8BB | Rank1BB) & to) return false; - if (!(attacks_bb(from, us) & pieces(~us) & to) // Not a capture - && !((from + pawn_push(us) == to) && empty(to)) // Not a single push - && !((from + 2 * pawn_push(us) == to) // Not a double push - && (relative_rank(us, from) == RANK_2) && empty(to) && empty(to - pawn_push(us)))) + // Check if it's a valid capture, single push, or double push + const bool isCapture = bool(attacks_bb(from, us) & pieces(~us) & to); + const bool isSinglePush = (from + pawn_push(us) == to) && empty(to); + const bool isDoublePush = (from + 2 * pawn_push(us) == to) + && (relative_rank(us, from) == RANK_2) && empty(to) + && empty(to - pawn_push(us)); + + if (!(isCapture || isSinglePush || isDoublePush)) return false; } else if (!(attacks_bb(type_of(pc), from, pieces()) & to)) From 6b7e05f0c5287179de25a0722f9eea0f87f64648 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 9 May 2025 15:34:36 -0700 Subject: [PATCH 047/107] Simplify PCM TTMove Bonus Passed Non-regression STC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 114048 W: 29597 L: 29459 D: 54992 Ptnml(0-2): 315, 13619, 29045, 13703, 342 https://tests.stockfishchess.org/tests/view/681e83533629b02d74b17701 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 61014 W: 15582 L: 15405 D: 30027 Ptnml(0-2): 25, 6485, 17307, 6668, 22 https://tests.stockfishchess.org/tests/view/68226b523629b02d74b17b89 closes https://github.com/official-stockfish/Stockfish/pull/6061 bench 2016566 --- src/search.cpp | 14 ++++++-------- src/search.h | 1 - 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6827f5127..4fca84f95 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -921,7 +921,6 @@ Value Search::Worker::search( do_move(pos, move, st); ss->currentMove = move; - ss->isTTMove = (move == ttData.move); ss->continuationHistory = &this->continuationHistory[ss->inCheck][true][movedPiece][move.to_sq()]; ss->continuationCorrectionHistory = @@ -1172,7 +1171,6 @@ moves_loop: // When in check, search starts here // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->isTTMove = (move == ttData.move); ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; ss->continuationCorrectionHistory = @@ -1203,7 +1201,7 @@ moves_loop: // When in check, search starts here r += 1036 + allNode * 848; // For first picked move (ttMove) reduce reduction - else if (ss->isTTMove) + else if (move == ttData.move) r -= 2006; if (capture) @@ -1285,7 +1283,7 @@ moves_loop: // When in check, search starts here (ss + 1)->pv[0] = Move::none(); // Extend move from transposition table if we are about to dive into qsearch. - if (ss->isTTMove && thisThread->rootDepth > 8) + if (move == ttData.move && thisThread->rootDepth > 8) newDepth = std::max(newDepth, 1); value = -search(pos, ss + 1, -beta, -alpha, newDepth, false); @@ -1423,7 +1421,7 @@ moves_loop: // When in check, search starts here ttData.move, moveCount); if (!PvNode) { - int bonus = ss->isTTMove ? 800 : -879; + int bonus = bestMove == ttData.move ? 800 : -879; ttMoveHistory << bonus; } } @@ -1431,11 +1429,11 @@ moves_loop: // When in check, search starts here // Bonus for prior quiet countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonusScale = std::min(-(ss - 1)->statScore / 113, 293); - bonusScale += std::min(73 * depth - 347, 184); + int bonusScale = -324; + bonusScale += std::min(-(ss - 1)->statScore / 103, 323); + bonusScale += std::min(73 * depth, 531); bonusScale += 33 * !allNode; bonusScale += 174 * ((ss - 1)->moveCount > 8); - bonusScale += 86 * (ss - 1)->isTTMove; bonusScale += 90 * (ss->cutoffCnt <= 3); bonusScale += 144 * (!ss->inCheck && bestValue <= ss->staticEval - 104); bonusScale += 128 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 82); diff --git a/src/search.h b/src/search.h index 6ab98f7c4..0c041c826 100644 --- a/src/search.h +++ b/src/search.h @@ -75,7 +75,6 @@ struct Stack { bool ttHit; int cutoffCnt; int reduction; - bool isTTMove; bool isPvNode; }; From e4b0f374933543efb42baaa03b89c2d479e894cb Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 13 May 2025 22:15:24 -0700 Subject: [PATCH 048/107] Shrink Enum Sizes Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 110848 W: 28974 L: 28564 D: 53310 Ptnml(0-2): 302, 12118, 30132, 12612, 260 https://tests.stockfishchess.org/tests/view/68242770a527315e07ccca38 closes https://github.com/official-stockfish/Stockfish/pull/6063 no functional change --- src/timeman.cpp | 2 ++ src/timeman.h | 2 +- src/types.h | 50 ++++++++++++++++++++++++------------------------- src/uci.h | 2 +- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 29ebffcaa..f0894a262 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -28,6 +28,8 @@ namespace Stockfish { +enum Color : int8_t; + TimePoint TimeManagement::optimum() const { return optimumTime; } TimePoint TimeManagement::maximum() const { return maximumTime; } diff --git a/src/timeman.h b/src/timeman.h index e8602bb7c..a2d1a4364 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -22,11 +22,11 @@ #include #include "misc.h" -#include "types.h" namespace Stockfish { class OptionsMap; +enum Color : int8_t; namespace Search { struct LimitsType; diff --git a/src/types.h b/src/types.h index 6cd6ee744..6c7975807 100644 --- a/src/types.h +++ b/src/types.h @@ -110,13 +110,13 @@ using Bitboard = uint64_t; constexpr int MAX_MOVES = 256; constexpr int MAX_PLY = 246; -enum Color { +enum Color : int8_t { WHITE, BLACK, COLOR_NB = 2 }; -enum CastlingRights { +enum CastlingRights : int8_t { NO_CASTLING, WHITE_OO, WHITE_OOO = WHITE_OO << 1, @@ -132,7 +132,7 @@ enum CastlingRights { CASTLING_RIGHT_NB = 16 }; -enum Bound { +enum Bound : int8_t { BOUND_NONE, BOUND_UPPER, BOUND_LOWER, @@ -183,13 +183,13 @@ constexpr Value QueenValue = 2538; // clang-format off -enum PieceType { +enum PieceType : std::int8_t { NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, ALL_PIECES = 0, PIECE_TYPE_NB = 8 }; -enum Piece { +enum Piece : std::int8_t { NO_PIECE, W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, @@ -203,26 +203,24 @@ constexpr Value PieceValue[PIECE_NB] = { using Depth = int; -enum : int { - // The following DEPTH_ constants are used for transposition table entries - // and quiescence search move generation stages. In regular search, the - // depth stored in the transposition table is literal: the search depth - // (effort) used to make the corresponding transposition table value. In - // quiescence search, however, the transposition table entries only store - // the current quiescence move generation stage (which should thus compare - // lower than any regular search depth). - DEPTH_QS = 0, - // For transposition table entries where no searching at all was done - // (whether regular or qsearch) we use DEPTH_UNSEARCHED, which should thus - // compare lower than any quiescence or regular depth. DEPTH_ENTRY_OFFSET - // is used only for the transposition table entry occupancy check (see tt.cpp), - // and should thus be lower than DEPTH_UNSEARCHED. - DEPTH_UNSEARCHED = -2, - DEPTH_ENTRY_OFFSET = -3 -}; +// The following DEPTH_ constants are used for transposition table entries +// and quiescence search move generation stages. In regular search, the +// depth stored in the transposition table is literal: the search depth +// (effort) used to make the corresponding transposition table value. In +// quiescence search, however, the transposition table entries only store +// the current quiescence move generation stage (which should thus compare +// lower than any regular search depth). +constexpr Depth DEPTH_QS = 0; +// For transposition table entries where no searching at all was done +// (whether regular or qsearch) we use DEPTH_UNSEARCHED, which should thus +// compare lower than any quiescence or regular depth. DEPTH_ENTRY_OFFSET +// is used only for the transposition table entry occupancy check (see tt.cpp), +// and should thus be lower than DEPTH_UNSEARCHED. +constexpr Depth DEPTH_UNSEARCHED = -2; +constexpr Depth DEPTH_ENTRY_OFFSET = -3; // clang-format off -enum Square : int { +enum Square : int8_t { SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3, @@ -238,7 +236,7 @@ enum Square : int { }; // clang-format on -enum Direction : int { +enum Direction : int8_t { NORTH = 8, EAST = 1, SOUTH = -NORTH, @@ -250,7 +248,7 @@ enum Direction : int { NORTH_WEST = NORTH + WEST }; -enum File : int { +enum File : int8_t { FILE_A, FILE_B, FILE_C, @@ -262,7 +260,7 @@ enum File : int { FILE_NB }; -enum Rank : int { +enum Rank : int8_t { RANK_1, RANK_2, RANK_3, diff --git a/src/uci.h b/src/uci.h index 5c1c07f7b..1686b3a72 100644 --- a/src/uci.h +++ b/src/uci.h @@ -33,7 +33,7 @@ namespace Stockfish { class Position; class Move; class Score; -enum Square : int; +enum Square : int8_t; using Value = int; class UCIEngine { From 6f445631ab40fcdca41fb7ccc94ac842796b89c3 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Mon, 28 Apr 2025 14:55:56 -0700 Subject: [PATCH 049/107] Simplify Futility Margin Passed STC Non-regression: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 159008 W: 41500 L: 41414 D: 76094 Ptnml(0-2): 501, 18821, 40759, 18937, 486 https://tests.stockfishchess.org/tests/view/680ff9e23629b02d74b1663a Passed LTC Non-regression: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 163572 W: 41617 L: 41543 D: 80412 Ptnml(0-2): 90, 17755, 46024, 17825, 92 https://tests.stockfishchess.org/tests/view/6814dd973629b02d74b16bac closes https://github.com/official-stockfish/Stockfish/pull/6065 Bench: 2018775 --- src/search.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4fca84f95..857a8d47e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -825,19 +825,17 @@ Value Search::Worker::search( // The depth condition is important for mate finding. { auto futility_margin = [&](Depth d) { - Value futilityMult = 105 - 23 * (cutNode && !ss->ttHit); - Value improvingDeduction = improving * futilityMult * 2; - Value worseningDeduction = opponentWorsening * futilityMult / 3; + Value futilityMult = 93 - 20 * (cutNode && !ss->ttHit); - return futilityMult * d // - - improvingDeduction // - - worseningDeduction // - + (ss - 1)->statScore / 335 // - + std::abs(correctionValue) / 149902; + return futilityMult * d // + - improving * futilityMult * 2 // + - opponentWorsening * futilityMult / 3 // + + (ss - 1)->statScore / 376 // + + std::abs(correctionValue) / 168639; }; - if (!ss->ttPv && depth < 14 && eval + (eval - beta) / 8 - futility_margin(depth) >= beta - && eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval)) + if (!ss->ttPv && depth < 14 && eval - futility_margin(depth) >= beta && eval >= beta + && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval)) return beta + (eval - beta) / 3; } From 1b6975ac4151d503ca48c9d5d4bd18d5fa11f4b9 Mon Sep 17 00:00:00 2001 From: Mapika Date: Sat, 17 May 2025 18:01:25 +0200 Subject: [PATCH 050/107] Add quiet move streak tracking to search stack Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 109344 W: 28473 L: 28053 D: 52818 Ptnml(0-2): 320, 12756, 28085, 13206, 305 https://tests.stockfishchess.org/tests/view/6828c43e6ec7634154f99a10 Passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 76308 W: 19721 L: 19323 D: 37264 Ptnml(0-2): 39, 8145, 21386, 8547, 37 https://tests.stockfishchess.org/tests/view/6828f65a6ec7634154f99b72 closes https://github.com/official-stockfish/Stockfish/pull/6066 Bench: 2161814 --- AUTHORS | 1 + src/search.cpp | 5 +++++ src/search.h | 1 + 3 files changed, 7 insertions(+) diff --git a/AUTHORS b/AUTHORS index f16758609..8caa22858 100644 --- a/AUTHORS +++ b/AUTHORS @@ -148,6 +148,7 @@ Lucas Braesch (lucasart) Lyudmil Antonov (lantonov) Maciej Żenczykowski (zenczykowski) Malcolm Campbell (xoto10) +Mark Marosi (Mapika) Mark Tenzer (31m059) marotear Mathias Parnaudeau (mparnaudeau) diff --git a/src/search.cpp b/src/search.cpp index 857a8d47e..0af54b4f5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1004,6 +1004,8 @@ moves_loop: // When in check, search starts here movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); + (ss + 1)->quietMoveStreak = (!capture && !givesCheck) ? (ss->quietMoveStreak + 1) : 0; + // Calculate new depth for this move newDepth = depth - 1; @@ -1198,6 +1200,9 @@ moves_loop: // When in check, search starts here if ((ss + 1)->cutoffCnt > 2) r += 1036 + allNode * 848; + if (!capture && !givesCheck && ss->quietMoveStreak >= 2) + r += (ss->quietMoveStreak - 1) * 50; + // For first picked move (ttMove) reduce reduction else if (move == ttData.move) r -= 2006; diff --git a/src/search.h b/src/search.h index 0c041c826..5caff10e8 100644 --- a/src/search.h +++ b/src/search.h @@ -76,6 +76,7 @@ struct Stack { int cutoffCnt; int reduction; bool isPvNode; + int quietMoveStreak; }; From 4f76768fcf1aa8a8c576086210875e84cda40705 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 18 May 2025 16:12:59 +0300 Subject: [PATCH 051/107] Remove a moveCount condition Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 70816 W: 18315 L: 18134 D: 34367 Ptnml(0-2): 210, 8213, 18360, 8436, 189 https://tests.stockfishchess.org/tests/view/68248197a527315e07cccb2d Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 121770 W: 31248 L: 31130 D: 59392 Ptnml(0-2): 61, 13338, 33995, 13404, 87 https://tests.stockfishchess.org/tests/view/68272ff46ec7634154f998ad closes https://github.com/official-stockfish/Stockfish/pull/6067 bench: 2319161 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0af54b4f5..5d92e1b1d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1233,7 +1233,7 @@ moves_loop: // When in check, search starts here // std::clamp has been replaced by a more robust implementation. Depth d = std::max(1, std::min(newDepth - r / 1024, newDepth + !allNode + (PvNode && !bestMove))) - + ((ss - 1)->isPvNode && moveCount < 8); + + ((ss - 1)->isPvNode); ss->reduction = newDepth - d; value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); From 6e9b5af0f002ff1175998631e07a1c92735cae62 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 18 May 2025 13:31:35 -0700 Subject: [PATCH 052/107] Check evaluation after ttMove before doing a tt cut Passed STC LLR: 2.97 (-2.94,2.94) <0.00,2.00> Total: 239136 W: 62222 L: 61608 D: 115306 Ptnml(0-2): 675, 28046, 61525, 28634, 688 https://tests.stockfishchess.org/tests/view/681053293629b02d74b1668f Passed LTC LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 448770 W: 115237 L: 114088 D: 219445 Ptnml(0-2): 177, 48128, 126619, 49291, 170 https://tests.stockfishchess.org/tests/view/681902de3629b02d74b16f6d closes https://github.com/official-stockfish/Stockfish/pull/6069 Bench: 2035432 --- src/search.cpp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5d92e1b1d..d71f78ac1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -680,6 +680,8 @@ Value Search::Worker::search( && (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER)) && (cutNode == (ttData.value >= beta) || depth > 5)) { + + // If ttMove is quiet, update move sorting heuristics on TT hit if (ttData.move && ttData.value >= beta) { @@ -696,7 +698,30 @@ Value Search::Worker::search( // Partial workaround for the graph history interaction problem // For high rule50 counts don't produce transposition table cutoffs. if (pos.rule50_count() < 90) - return ttData.value; + { + if (depth >= 8 && ttData.move && pos.pseudo_legal(ttData.move) && pos.legal(ttData.move) + && !is_decisive(ttData.value)) + { + do_move(pos, ttData.move, st); + Key nextPosKey = pos.key(); + auto [ttHitNext, ttDataNext, ttWriterNext] = tt.probe(nextPosKey); + ttDataNext.value = + ttHitNext ? value_from_tt(ttDataNext.value, ss->ply + 1, pos.rule50_count()) + : VALUE_NONE; + undo_move(pos, ttData.move); + + if (!is_valid(ttDataNext.value)) + return ttData.value; + if (ttData.value >= beta && -ttDataNext.value >= beta) + return ttData.value; + if (ttData.value <= alpha && -ttDataNext.value <= alpha) + return ttData.value; + } + else + { + return ttData.value; + } + } } // Step 5. Tablebases probe @@ -1233,7 +1258,7 @@ moves_loop: // When in check, search starts here // std::clamp has been replaced by a more robust implementation. Depth d = std::max(1, std::min(newDepth - r / 1024, newDepth + !allNode + (PvNode && !bestMove))) - + ((ss - 1)->isPvNode); + + ((ss - 1)->isPvNode); ss->reduction = newDepth - d; value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); From 39942db3ff4af2c2135775d5ac3cd3e77a883636 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 6 May 2025 15:05:24 -0700 Subject: [PATCH 053/107] Simplify In-Check Statscore Passed Non-regression STC: LLR: 2.98 (-2.94,2.94) <-1.75,0.25> Total: 129760 W: 33701 L: 33580 D: 62479 Ptnml(0-2): 359, 15248, 33575, 15309, 389 https://tests.stockfishchess.org/tests/view/681a88193629b02d74b17123 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 519612 W: 132224 L: 132512 D: 254876 Ptnml(0-2): 246, 56823, 145960, 56527, 250 https://tests.stockfishchess.org/tests/view/681f9ed43629b02d74b177c3 closes https://github.com/official-stockfish/Stockfish/pull/6070 bench: 2046462 --- src/search.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d71f78ac1..bb9ce68ba 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1237,13 +1237,10 @@ moves_loop: // When in check, search starts here 826 * int(PieceValue[pos.captured_piece()]) / 128 + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())] - 5030; - else if (ss->inCheck) - ss->statScore = thisThread->mainHistory[us][move.from_to()] - + (*contHist[0])[movedPiece][move.to_sq()] - 2766; else ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 3206; + + (*contHist[1])[movedPiece][move.to_sq()] + 1000 * ss->inCheck - 3206; // Decrease/increase reduction for moves with a good/bad history r -= ss->statScore * 826 / 8192; From 009632c465ef8204884b01c0307f7e15106fd94e Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 16 May 2025 13:04:10 -0700 Subject: [PATCH 054/107] Simplify handling of good/bad quiets Simplify the handling of good/bad quiets and make it more similar to the way we handle good/bad captures. The good quiet limit was adjusted from -7998 to -14000 to keep the ratio of good/bad quiets about the same as master. This also fixes a "bug" that previously returned some bad quiets during the GOOD_QUIET stage when some qood quiets weren't sorted at low depths. STC https://tests.stockfishchess.org/tests/view/6827a68c6ec7634154f9992b LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 75936 W: 19722 L: 19547 D: 36667 Ptnml(0-2): 186, 8937, 19589, 9028, 228 LTC https://tests.stockfishchess.org/tests/view/6828f8096ec7634154f99b82 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 104112 W: 26773 L: 26638 D: 50701 Ptnml(0-2): 51, 11363, 29098, 11488, 56 closes https://github.com/official-stockfish/Stockfish/pull/6071 Bench: 2007023 --- src/movepick.cpp | 22 +++++++++------------- src/movepick.h | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 1c278147f..b0059bd31 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -208,8 +208,6 @@ Move MovePicker::select(Pred filter) { // picking the move with the highest score from a list of generated moves. Move MovePicker::next_move() { - auto quiet_threshold = [](Depth d) { return -3560 * d; }; - top: switch (stage) { @@ -250,24 +248,22 @@ top: case QUIET_INIT : if (!skipQuiets) { - endMoves = beginBadQuiets = endBadQuiets = generate(pos, cur); + cur = endBadQuiets = endBadCaptures; + endMoves = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, quiet_threshold(depth)); + partial_insertion_sort(cur, endMoves, -3560 * depth); } ++stage; [[fallthrough]]; case GOOD_QUIET : - if (!skipQuiets && select([]() { return true; })) - { - if ((cur - 1)->value > -7998 || (cur - 1)->value <= quiet_threshold(depth)) - return *(cur - 1); - - // Remaining quiets are bad - beginBadQuiets = cur - 1; - } + if (!skipQuiets && select([&]() { + return cur->value > -14000 ? true + : (*endBadQuiets++ = *cur, false); + })) + return *(cur - 1); // Prepare the pointers to loop over the bad captures cur = moves; @@ -281,7 +277,7 @@ top: return *(cur - 1); // Prepare the pointers to loop over the bad quiets - cur = beginBadQuiets; + cur = endBadCaptures; endMoves = endBadQuiets; ++stage; diff --git a/src/movepick.h b/src/movepick.h index 72a6f4e1c..7da7c3a7e 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -68,7 +68,7 @@ class MovePicker { const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; Move ttMove; - ExtMove * cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; + ExtMove * cur, *endMoves, *endBadCaptures, *endBadQuiets; int stage; int threshold; Depth depth; From 0f102f3692f3b21a2c8add92ba867bc8de362c8d Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 18 May 2025 13:44:29 -0700 Subject: [PATCH 055/107] Simplify Quiet Early Move Penalty Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 185344 W: 47995 L: 47939 D: 89410 Ptnml(0-2): 527, 21898, 47754, 21978, 515 https://tests.stockfishchess.org/tests/view/682a47536ec7634154f9a3bc Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 101706 W: 26050 L: 25912 D: 49744 Ptnml(0-2): 53, 11056, 28499, 11190, 55 https://tests.stockfishchess.org/tests/view/682a61736ec7634154f9a50e closes https://github.com/official-stockfish/Stockfish/pull/6072 Bench: 2012032 --- src/movepick.cpp | 5 ++--- src/search.cpp | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index b0059bd31..1df0b0ccf 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -249,7 +249,7 @@ top: if (!skipQuiets) { cur = endBadQuiets = endBadCaptures; - endMoves = generate(pos, cur); + endMoves = generate(pos, cur); score(); partial_insertion_sort(cur, endMoves, -3560 * depth); @@ -260,8 +260,7 @@ top: case GOOD_QUIET : if (!skipQuiets && select([&]() { - return cur->value > -14000 ? true - : (*endBadQuiets++ = *cur, false); + return cur->value > -14000 ? true : (*endBadQuiets++ = *cur, false); })) return *(cur - 1); diff --git a/src/search.cpp b/src/search.cpp index bb9ce68ba..5f20e5938 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1900,8 +1900,7 @@ void update_all_stats(const Position& pos, // 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 * (512 + depth * 16) / 1024); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * 580 / 1024); // Decrease stats for all non-best capture moves for (Move move : capturesSearched) From ccfa6519680ce2cb602fab33d5605606616218ce Mon Sep 17 00:00:00 2001 From: Daniel Samek Date: Mon, 19 May 2025 10:12:50 +0200 Subject: [PATCH 056/107] Remove full depth search reduction when cutNode Passed STC-simplification bounds: https://tests.stockfishchess.org/tests/view/6829dd6d6ec7634154f99fd3 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 67872 W: 17629 L: 17443 D: 32800 Ptnml(0-2): 167, 7988, 17451, 8152, 178 Passed LTC-simplification bounds: https://tests.stockfishchess.org/tests/view/6829f2176ec7634154f9a01c LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 94818 W: 24328 L: 24184 D: 46306 Ptnml(0-2): 52, 10246, 26667, 10394, 50 closes https://github.com/official-stockfish/Stockfish/pull/6074 bench: 2245168 --- src/search.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5f20e5938..cce01867b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1292,9 +1292,6 @@ moves_loop: // When in check, search starts here r -= ttMoveHistory / 8; - if (cutNode) - r += 520; - // Note that if expected reduction is high, we reduce search depth here value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3564) - (r > 4969 && newDepth > 2), !cutNode); From 56ea1fadf16142c830161503d49c9ae4ea23b1ef Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 19 May 2025 11:42:45 +0300 Subject: [PATCH 057/107] Tweak low ply history Increase low ply history maximum ply by 1 and also allow to use it for check evasions scoring. Pased STC: https://tests.stockfishchess.org/tests/view/682a2db36ec7634154f9a358 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 66464 W: 17440 L: 17081 D: 31943 Ptnml(0-2): 191, 7717, 17056, 8078, 190 Passed LTC: https://tests.stockfishchess.org/tests/view/682a3d406ec7634154f9a39c LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 384030 W: 98476 L: 97452 D: 188102 Ptnml(0-2): 180, 41564, 107522, 42550, 199 closes https://github.com/official-stockfish/Stockfish/pull/6075 Bench: 2422771 --- src/history.h | 2 +- src/movepick.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/history.h b/src/history.h index 259d6eefb..46914789e 100644 --- a/src/history.h +++ b/src/history.h @@ -36,7 +36,7 @@ namespace Stockfish { constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 constexpr int CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 constexpr int CORRECTION_HISTORY_LIMIT = 1024; -constexpr int LOW_PLY_HISTORY_SIZE = 4; +constexpr int LOW_PLY_HISTORY_SIZE = 5; static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, "PAWN_HISTORY_SIZE has to be a power of 2"); diff --git a/src/movepick.cpp b/src/movepick.cpp index 1df0b0ccf..45bb8afe8 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -186,8 +186,12 @@ void MovePicker::score() { if (pos.capture_stage(m)) 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()]; + if (ply < LOW_PLY_HISTORY_SIZE) + m.value += 2 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply); + } } } From 347e328fdbe8104747a258db00a9f034e09d3cb8 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 18 May 2025 18:36:06 -0700 Subject: [PATCH 058/107] Simplify TT Replacement Strategy Passed Non-regression STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 50528 W: 13160 L: 12958 D: 24410 Ptnml(0-2): 132, 5681, 13439, 5877, 135 https://tests.stockfishchess.org/tests/view/682a8b296ec7634154f9a785 Passed Non-regression STC w/ High Hash Pressure: LLR: 2.98 (-2.94,2.94) <-1.75,0.25> Total: 30048 W: 7849 L: 7621 D: 14578 Ptnml(0-2): 75, 3390, 7884, 3582, 93 https://tests.stockfishchess.org/tests/view/682a9caf6ec7634154f9a7ae Passed Non-regression LTC w/ High Hash Pressure: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 17610 W: 4584 L: 4362 D: 8664 Ptnml(0-2): 7, 1799, 4974, 2015, 10 https://tests.stockfishchess.org/tests/view/682ab3966ec7634154f9a8c8 closes https://github.com/official-stockfish/Stockfish/pull/6079 Bench: 2422771 --- src/tt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 5d8457611..d7f7dbdef 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -234,8 +234,8 @@ std::tuple TranspositionTable::probe(const Key key) cons // Find an entry to be replaced according to the replacement strategy TTEntry* replace = tte; for (int i = 1; i < ClusterSize; ++i) - if (replace->depth8 - replace->relative_age(generation8) * 2 - > tte[i].depth8 - tte[i].relative_age(generation8) * 2) + if (replace->depth8 - replace->relative_age(generation8) + > tte[i].depth8 - tte[i].relative_age(generation8)) replace = &tte[i]; return {false, From 54fb42ddf8b242e46f5ef2d2a0d481ed5aa5fe7b Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Sat, 17 May 2025 22:50:47 +0200 Subject: [PATCH 059/107] clean up code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Non functional changes:** in search.cpp: - an unnecessary pair of parenthesis in the IIR condition has been removed. - refactored the stalemate trap detection code in movepick.cpp: - use the variables `from`, `to`, `piece`, `pieceType` and `capturedPiece` instead of calling the same functions multiple times in `MovePicker::score()`. - rename `MovePicker::other_piece_types_mobile()`. **Functional changes:** - make sure the processed move is always legal in `MovePicker::other_piece_types_mobile()`. passed non regression STC: https://tests.stockfishchess.org/tests/view/6829da686ec7634154f99faf LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 95680 W: 24962 L: 24820 D: 45898 Ptnml(0-2): 221, 9622, 28025, 9738, 234 Passed non regression LTC: https://tests.stockfishchess.org/tests/view/682a102c6ec7634154f9a086 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 117666 W: 30065 L: 29957 D: 57644 Ptnml(0-2): 45, 10173, 38291, 10277, 47 Run of 10k games on the stalemate opening book: https://tests.stockfishchess.org/tests/view/682b114e6ec7634154f9aa2d Elo: 0.76 ± 0.9 (95%) LOS: 95.3% Total: 10000 W: 4637 L: 4615 D: 748 Ptnml(0-2): 0, 75, 4828, 97, 0 nElo: 5.83 ± 6.8 (95%) PairsRatio: 1.29 closes https://github.com/official-stockfish/Stockfish/pull/6080 Bench: 2422771 --- src/movepick.cpp | 57 ++++++++++++++++++++---------------------------- src/movepick.h | 3 +-- src/search.cpp | 38 ++++++++++++++++---------------- 3 files changed, 44 insertions(+), 54 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 45bb8afe8..6d00dd941 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -126,11 +126,11 @@ void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); + Color us = pos.side_to_move(); + [[maybe_unused]] Bitboard threatenedPieces, threatByLesser[QUEEN + 1]; if constexpr (Type == QUIETS) { - Color us = pos.side_to_move(); - threatByLesser[KNIGHT] = threatByLesser[BISHOP] = pos.attacks_by(~us); threatByLesser[ROOK] = pos.attacks_by(~us) | pos.attacks_by(~us) | threatByLesser[KNIGHT]; @@ -143,21 +143,21 @@ void MovePicker::score() { } for (auto& m : *this) + { + const Square from = m.from_sq(); + const Square to = m.to_sq(); + const Piece pc = pos.moved_piece(m); + const PieceType pt = type_of(pc); + const Piece capturedPiece = pos.piece_on(to); + if constexpr (Type == CAPTURES) - m.value = - 7 * int(PieceValue[pos.piece_on(m.to_sq())]) - + 1024 * bool(pos.check_squares(type_of(pos.moved_piece(m))) & m.to_sq()) - + (*captureHistory)[pos.moved_piece(m)][m.to_sq()][type_of(pos.piece_on(m.to_sq()))]; + m.value = (*captureHistory)[pc][to][type_of(capturedPiece)] + + 7 * int(PieceValue[capturedPiece]) + 1024 * bool(pos.check_squares(pt) & to); else if constexpr (Type == QUIETS) { - Piece pc = pos.moved_piece(m); - PieceType pt = type_of(pc); - Square from = m.from_sq(); - Square to = m.to_sq(); - // histories - m.value = 2 * (*mainHistory)[pos.side_to_move()][m.from_to()]; + m.value = 2 * (*mainHistory)[us][m.from_to()]; m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to]; m.value += (*continuationHistory[0])[pc][to]; m.value += (*continuationHistory[1])[pc][to]; @@ -184,15 +184,15 @@ void MovePicker::score() { else // Type == EVASIONS { if (pos.capture_stage(m)) - m.value = PieceValue[pos.piece_on(m.to_sq())] + (1 << 28); + m.value = PieceValue[capturedPiece] + (1 << 28); else { - m.value = (*mainHistory)[pos.side_to_move()][m.from_to()] - + (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()]; + m.value = (*mainHistory)[us][m.from_to()] + (*continuationHistory[0])[pc][to]; if (ply < LOW_PLY_HISTORY_SIZE) m.value += 2 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply); } } + } } // Returns the next move satisfying a predicate function. @@ -221,7 +221,6 @@ top: case QSEARCH_TT : case PROBCUT_TT : ++stage; - cur = moves + 1; return ttMove; case CAPTURE_INIT : @@ -237,12 +236,10 @@ top: case GOOD_CAPTURE : if (select([&]() { - if (!pos.see_ge(*cur, -cur->value / 18)) - { - std::swap(*endBadCaptures++, *cur); - return false; - } - return true; + if (pos.see_ge(*cur, -cur->value / 18)) + return true; + std::swap(*endBadCaptures++, *cur); + return false; })) return *(cur - 1); @@ -315,23 +312,17 @@ top: void MovePicker::skip_quiet_moves() { skipQuiets = true; } -bool MovePicker::other_piece_types_mobile(PieceType pt) { +// this function must be called after all quiet moves and captures have been generated +bool MovePicker::can_move_king_or_pawn() { assert(stage == GOOD_QUIET || stage == BAD_QUIET || stage == EVASION); - // verify all generated captures and quiets for (ExtMove* m = moves; m < endMoves; ++m) { - if (*m && type_of(pos.moved_piece(*m)) != pt) - { - if (type_of(pos.moved_piece(*m)) != KING) - return true; - if (pos.legal(*m)) - return true; - } + PieceType movedPieceType = type_of(pos.moved_piece(*m)); + if ((movedPieceType == PAWN || movedPieceType == KING) && pos.legal(*m)) + return true; } return false; } -void MovePicker::mark_current_illegal() { *(cur - 1) = Move::none(); } - } // namespace Stockfish diff --git a/src/movepick.h b/src/movepick.h index 7da7c3a7e..4218ab5a3 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -50,8 +50,7 @@ class MovePicker { MovePicker(const Position&, Move, int, const CapturePieceToHistory*); Move next_move(); void skip_quiet_moves(); - bool other_piece_types_mobile(PieceType pt); - void mark_current_illegal(); + bool can_move_king_or_pawn(); private: template diff --git a/src/search.cpp b/src/search.cpp index cce01867b..f59358182 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -910,7 +910,7 @@ Value Search::Worker::search( // Step 10. Internal iterative reductions // For PV nodes without a ttMove as well as for deep enough cutNodes, we decrease depth. // (*Scaler) Especially if they make IIR less aggressive. - if ((!allNode && depth >= (PvNode ? 5 : 7)) && !ttData.move) + if (!allNode && depth >= (PvNode ? 5 : 7) && !ttData.move) depth--; // Step 11. ProbCut @@ -1002,10 +1002,8 @@ moves_loop: // When in check, search starts here // Check for legality if (!pos.legal(move)) - { - mp.mark_current_illegal(); continue; - } + // At root obey the "searchmoves" option and skip moves not listed in Root // Move List. In MultiPV mode we also skip PV moves that have been already // searched and those of lower "TB rank" if we are in a TB root position. @@ -1074,15 +1072,17 @@ moves_loop: // When in check, search starts here int seeHist = std::clamp(captHist / 31, -137 * depth, 125 * depth); if (!pos.see_ge(move, -158 * depth - seeHist)) { - bool skip = true; - if (depth > 2 && !capture && givesCheck && alpha < 0 - && pos.non_pawn_material(us) == PieceValue[movedPiece] - && PieceValue[movedPiece] >= RookValue - && !(PseudoAttacks[KING][pos.square(us)] & move.from_sq())) - // if the opponent captures last mobile piece it might be stalemate - skip = mp.other_piece_types_mobile(type_of(movedPiece)); + bool mayStalemateTrap = + depth > 2 && givesCheck && alpha < 0 + && !capture // we consider that captures will likely destroy the stalemate configuration + && pos.non_pawn_material(us) == PieceValue[movedPiece] + && PieceValue[movedPiece] >= RookValue + // it can't be stalemate if we moved a piece adjacent to the king + && !(attacks_bb(pos.square(us)) & move.from_sq()) + && !mp.can_move_king_or_pawn(); - if (skip) + // avoid pruning sacrifices of our last piece for stalemate + if (!mayStalemateTrap) continue; } } @@ -1873,8 +1873,8 @@ void update_all_stats(const Position& pos, int moveCount) { CapturePieceToHistory& captureHistory = workerThread.captureHistory; - Piece moved_piece = pos.moved_piece(bestMove); - PieceType captured; + Piece movedPiece = pos.moved_piece(bestMove); + PieceType capturedPiece; int bonus = std::min(143 * depth - 89, 1496) + 302 * (bestMove == ttMove); int malus = std::min(737 * depth - 179, 3141) - 30 * moveCount; @@ -1890,8 +1890,8 @@ void update_all_stats(const Position& pos, 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 * 1213 / 1024; + capturedPiece = type_of(pos.piece_on(bestMove.to_sq())); + captureHistory[movedPiece][bestMove.to_sq()][capturedPiece] << bonus * 1213 / 1024; } // Extra penalty for a quiet early move that was not a TT move in @@ -1902,9 +1902,9 @@ void update_all_stats(const Position& pos, // 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 * 1388 / 1024; + movedPiece = pos.moved_piece(move); + capturedPiece = type_of(pos.piece_on(move.to_sq())); + captureHistory[movedPiece][move.to_sq()][capturedPiece] << -malus * 1388 / 1024; } } From e03898b57cbed0a6e5c830d4693887f067e64629 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Mon, 19 May 2025 16:44:59 +0200 Subject: [PATCH 060/107] ci: add tests and artifacts for windows-11-arm integrate armv8 and armv8-dotprod builds on windows-11-arm in ci, creating the corresponding artifacts. Correct Makefile to drop warnings when providing a CXX, add MINGW ARM64 to get_native_properties.sh fixes https://github.com/official-stockfish/Stockfish/issues/5640 closes https://github.com/official-stockfish/Stockfish/pull/6078 No functional change --- .github/ci/matrix.json | 138 +++++++++++++++++++++++++++++- .github/workflows/compilation.yml | 14 +-- .github/workflows/tests.yml | 20 ++++- scripts/get_native_properties.sh | 8 +- src/Makefile | 12 +-- 5 files changed, 172 insertions(+), 20 deletions(-) diff --git a/.github/ci/matrix.json b/.github/ci/matrix.json index fa720a1c3..c414c51fc 100644 --- a/.github/ci/matrix.json +++ b/.github/ci/matrix.json @@ -40,6 +40,18 @@ "ext": ".exe", "sde": "/d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-win/sde.exe -future --", "archive_ext": "zip" + }, + { + "name": "Windows 11 Mingw-w64 Clang arm64", + "os": "windows-11-arm", + "simple_name": "windows", + "compiler": "clang++", + "comp": "clang", + "msys_sys": "clangarm64", + "msys_env": "clang-aarch64-clang", + "shell": "msys2 {0}", + "ext": ".exe", + "archive_ext": "zip" } ], "binaries": [ @@ -51,7 +63,9 @@ "x86-64-avx512", "x86-64-vnni256", "x86-64-vnni512", - "apple-silicon" + "apple-silicon", + "armv8", + "armv8-dotprod" ], "exclude": [ { @@ -126,12 +140,78 @@ "os": "macos-13" } }, + { + "binaries": "x86-64", + "config": { + "os": "windows-11-arm" + } + }, + { + "binaries": "x86-64-sse41-popcnt", + "config": { + "os": "windows-11-arm" + } + }, + { + "binaries": "x86-64-avx2", + "config": { + "os": "windows-11-arm" + } + }, + { + "binaries": "x86-64-bmi2", + "config": { + "os": "windows-11-arm" + } + }, + { + "binaries": "x86-64-avxvnni", + "config": { + "os": "windows-11-arm" + } + }, + { + "binaries": "x86-64-avx512", + "config": { + "os": "windows-11-arm" + } + }, + { + "binaries": "x86-64-vnni256", + "config": { + "os": "windows-11-arm" + } + }, + { + "binaries": "x86-64-vnni512", + "config": { + "os": "windows-11-arm" + } + }, { "binaries": "apple-silicon", "config": { "os": "windows-2022" } }, + { + "binaries": "apple-silicon", + "config": { + "os": "windows-11-arm" + } + }, + { + "binaries": "apple-silicon", + "config": { + "os": "ubuntu-20.04" + } + }, + { + "binaries": "apple-silicon", + "config": { + "os": "ubuntu-22.04" + } + }, { "binaries": "apple-silicon", "config": { @@ -139,10 +219,64 @@ } }, { - "binaries": "apple-silicon", + "binaries": "armv8", + "config": { + "os": "windows-2022" + } + }, + { + "binaries": "armv8", + "config": { + "os": "ubuntu-20.04" + } + }, + { + "binaries": "armv8", "config": { "os": "ubuntu-22.04" } + }, + { + "binaries": "armv8", + "config": { + "os": "macos-13" + } + }, + { + "binaries": "armv8", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "armv8-dotprod", + "config": { + "os": "windows-2022" + } + }, + { + "binaries": "armv8-dotprod", + "config": { + "os": "ubuntu-20.04" + } + }, + { + "binaries": "armv8-dotprod", + "config": { + "os": "ubuntu-22.04" + } + }, + { + "binaries": "armv8-dotprod", + "config": { + "os": "macos-13" + } + }, + { + "binaries": "armv8-dotprod", + "config": { + "os": "macos-14" + } } ] } diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index 5878adecb..67b2e1c55 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -63,13 +63,13 @@ jobs: - name: Check compiler run: $COMPCXX -v - - name: Show g++ cpu info - if: runner.os != 'macOS' - run: g++ -Q -march=native --help=target - - - name: Show clang++ cpu info - if: runner.os == 'macOS' - run: clang++ -E - -march=native -### + - name: Show compiler cpu info + run: | + if [[ "$COMPCXX" == clang* ]]; then + $COMPCXX -E - -march=native -### + else + $COMPCXX -Q -march=native --help=target + fi # x86-64 with newer extensions tests diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 57d0d53f0..6d35a183d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -98,6 +98,14 @@ jobs: msys_sys: clang64 msys_env: clang-x86_64-clang shell: msys2 {0} + - name: Windows 11 Mingw-w64 Clang arm64 + os: windows-11-arm + compiler: clang++ + comp: clang + run_armv8_tests: true + msys_sys: clangarm64 + msys_env: clang-aarch64-clang + shell: msys2 {0} defaults: run: working-directory: src @@ -302,8 +310,10 @@ jobs: - name: Test armv8 build if: matrix.config.run_armv8_tests run: | - export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH - export LDFLAGS="-static -Wno-unused-command-line-argument" + if [ $COMP == ndk ]; then + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + fi make clean make -j4 ARCH=armv8 build ../tests/signature.sh $benchref @@ -311,8 +321,10 @@ jobs: - name: Test armv8-dotprod build if: matrix.config.run_armv8_tests run: | - export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH - export LDFLAGS="-static -Wno-unused-command-line-argument" + if [ $COMP == ndk ]; then + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + fi make clean make -j4 ARCH=armv8-dotprod build ../tests/signature.sh $benchref diff --git a/scripts/get_native_properties.sh b/scripts/get_native_properties.sh index 132bd6f48..773d6c271 100755 --- a/scripts/get_native_properties.sh +++ b/scripts/get_native_properties.sh @@ -130,7 +130,13 @@ case $uname_s in esac file_ext='tar' ;; - 'CYGWIN'*|'MINGW'*|'MSYS'*) # Windows system with POSIX compatibility layer + 'MINGW'*'ARM64'*) # Windows ARM64 system with POSIX compatibility layer + # TODO: older chips might be armv8, but we have no good way to detect, /proc/cpuinfo shows x86 info + file_os='windows' + true_arch='armv8-dotprod' + file_ext='zip' + ;; + 'CYGWIN'*|'MINGW'*|'MSYS'*) # Windows x86_64system with POSIX compatibility layer get_flags check_znver_1_2 set_arch_x86_64 diff --git a/src/Makefile b/src/Makefile index 17badefde..fc27044f8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -567,11 +567,16 @@ ifeq ($(COMP),ndk) LDFLAGS += -static-libstdc++ -pie -lm -latomic endif +### Allow overwriting CXX from command line +ifdef COMPCXX + CXX=$(COMPCXX) +endif + # llvm-profdata must be version compatible with the specified CXX (be it clang, or the gcc alias) # make -j profile-build CXX=clang++-20 COMP=clang # Locate the version in the same directory as the compiler used, # with fallback to a generic one if it can't be located - LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX))))llvm-profdata + LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX) 2> /dev/null)))llvm-profdata ifeq ($(wildcard $(LLVM_PROFDATA)),) LLVM_PROFDATA := llvm-profdata endif @@ -590,11 +595,6 @@ else endif endif -### Allow overwriting CXX from command line -ifdef COMPCXX - CXX=$(COMPCXX) -endif - ### Sometimes gcc is really clang ifeq ($(COMP),gcc) gccversion := $(shell $(CXX) --version 2>/dev/null) From 4f021cab3b87ac2d2e8214a03750945ec184e6ca Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 18 May 2025 14:53:15 -0700 Subject: [PATCH 061/107] Simplify allNode term in prior countermove Passed simplification STC LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 53632 W: 14008 L: 13805 D: 25819 Ptnml(0-2): 136, 6253, 13869, 6388, 170 https://tests.stockfishchess.org/tests/view/6828f2b26ec7634154f99b5e Passed simplification LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 82482 W: 21202 L: 21045 D: 40235 Ptnml(0-2): 37, 8986, 23052, 9115, 51 https://tests.stockfishchess.org/tests/view/6829010a6ec7634154f99db3 closes https://github.com/official-stockfish/Stockfish/pull/6068 Bench: 2302782 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f59358182..775aaceff 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1451,10 +1451,9 @@ moves_loop: // When in check, search starts here // Bonus for prior quiet countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonusScale = -324; + int bonusScale = -302; bonusScale += std::min(-(ss - 1)->statScore / 103, 323); bonusScale += std::min(73 * depth, 531); - bonusScale += 33 * !allNode; bonusScale += 174 * ((ss - 1)->moveCount > 8); bonusScale += 90 * (ss->cutoffCnt <= 3); bonusScale += 144 * (!ss->inCheck && bestValue <= ss->staticEval - 104); @@ -2245,4 +2244,4 @@ bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& po } -} // namespace Stockfish +} // namespace Stockfish \ No newline at end of file From c13c1d2c30b3b2f9068f1a3b7c239e2264f329e0 Mon Sep 17 00:00:00 2001 From: Disservin Date: Thu, 22 May 2025 18:41:53 +0200 Subject: [PATCH 062/107] Silence "may be used uninitialized" GCC 15 warning closes https://github.com/official-stockfish/Stockfish/pull/6083 No functional change --- src/nnue/nnue_accumulator.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/nnue/nnue_accumulator.cpp b/src/nnue/nnue_accumulator.cpp index 831284364..83b09637a 100644 --- a/src/nnue/nnue_accumulator.cpp +++ b/src/nnue/nnue_accumulator.cpp @@ -33,12 +33,16 @@ namespace Stockfish::Eval::NNUE { #if defined(__GNUC__) && !defined(__clang__) - #define sf_assume(cond) \ - do \ - { \ - if (!(cond)) \ - __builtin_unreachable(); \ - } while (0) + #if __GNUC__ >= 13 + #define sf_assume(cond) __attribute__((assume(cond))) + #else + #define sf_assume(cond) \ + do \ + { \ + if (!(cond)) \ + __builtin_unreachable(); \ + } while (0) + #endif #else // do nothing for other compilers #define sf_assume(cond) From 2662d6bf3544493fef718f05cb9a2c3ff1324d3d Mon Sep 17 00:00:00 2001 From: Disservin Date: Thu, 22 May 2025 21:19:46 +0200 Subject: [PATCH 063/107] Update clang-format to v20 closes https://github.com/official-stockfish/Stockfish/pull/6085 No functional change --- .clang-format | 4 ++-- .github/workflows/clang-format.yml | 8 ++++---- src/Makefile | 4 ++-- src/nnue/nnue_feature_transformer.h | 6 ++---- src/thread.h | 2 +- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.clang-format b/.clang-format index c71f0368e..a470cc0b8 100644 --- a/.clang-format +++ b/.clang-format @@ -9,14 +9,14 @@ AllowAllParametersOfDeclarationOnNextLine: true AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false -AlwaysBreakTemplateDeclarations: Yes +BreakTemplateDeclarations: Yes BasedOnStyle: WebKit BitFieldColonSpacing: After BinPackParameters: false BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom BraceWrapping: - AfterFunction: false + AfterFunction: false AfterClass: false AfterControlStatement: true BeforeElse: true diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index ab6b4350e..46607c1e9 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -25,11 +25,11 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Run clang-format style check - uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0 + uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 # @v4.15.0 id: clang-format continue-on-error: true with: - clang-format-version: "18" + clang-format-version: "20" exclude-regex: "incbin" - name: Comment on PR @@ -37,9 +37,9 @@ jobs: uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0 with: message: | - clang-format 18 needs to be run on this PR. + clang-format 20 needs to be run on this PR. If you do not have clang-format installed, the maintainer will run it when merging. - For the exact version please see https://packages.ubuntu.com/noble/clang-format-18. + For the exact version please see https://packages.ubuntu.com/plucky/clang-format-20. _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ comment_tag: execution diff --git a/src/Makefile b/src/Makefile index fc27044f8..72d06f09e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -163,8 +163,8 @@ lsx = no lasx = no STRIP = strip -ifneq ($(shell which clang-format-18 2> /dev/null),) - CLANG-FORMAT = clang-format-18 +ifneq ($(shell which clang-format-20 2> /dev/null),) + CLANG-FORMAT = clang-format-20 else CLANG-FORMAT = clang-format endif diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index b9b422a65..beb0c7f1c 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -122,8 +122,7 @@ using psqt_vec_t = int32x4_t; #define vec_add_16(a, b) vaddq_s16(a, b) #define vec_sub_16(a, b) vsubq_s16(a, b) #define vec_mulhi_16(a, b) vqdmulhq_s16(a, b) - #define vec_zero() \ - vec_t { 0 } + #define vec_zero() vec_t{0} #define vec_set_16(a) vdupq_n_s16(a) #define vec_max_16(a, b) vmaxq_s16(a, b) #define vec_min_16(a, b) vminq_s16(a, b) @@ -133,8 +132,7 @@ using psqt_vec_t = int32x4_t; #define vec_store_psqt(a, b) *(a) = (b) #define vec_add_psqt_32(a, b) vaddq_s32(a, b) #define vec_sub_psqt_32(a, b) vsubq_s32(a, b) - #define vec_zero_psqt() \ - psqt_vec_t { 0 } + #define vec_zero_psqt() psqt_vec_t{0} #define NumRegistersSIMD 16 #define MaxChunkSize 16 diff --git a/src/thread.h b/src/thread.h index 912d44335..00616097a 100644 --- a/src/thread.h +++ b/src/thread.h @@ -164,7 +164,7 @@ class ThreadPool { std::vector> threads; std::vector boundThreadToNumaNode; - uint64_t accumulate(std::atomic Search::Worker::*member) const { + uint64_t accumulate(std::atomic Search::Worker::* member) const { uint64_t sum = 0; for (auto&& th : threads) From 472cc764be8b46f1f5c30ed879e957a86369fb5c Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Mon, 19 May 2025 14:47:59 -0700 Subject: [PATCH 064/107] Move SIMD code to a separate header Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 115328 W: 29903 L: 29777 D: 55648 Ptnml(0-2): 265, 12293, 32444, 12375, 287 https://tests.stockfishchess.org/tests/view/68300e086ec7634154f9b1d1 closes https://github.com/official-stockfish/Stockfish/pull/6086 no functional change --- src/Makefile | 4 +- src/misc.h | 16 + src/nnue/features/half_ka_v2_hm.cpp | 2 +- src/nnue/layers/affine_transform.h | 24 +- .../layers/affine_transform_sparse_input.h | 68 +-- src/nnue/layers/simd.h | 134 ------ src/nnue/network.h | 1 + src/nnue/nnue_accumulator.cpp | 21 +- src/nnue/nnue_accumulator.h | 4 - src/nnue/nnue_architecture.h | 6 + src/nnue/nnue_common.h | 5 + src/nnue/nnue_feature_transformer.h | 223 +--------- src/nnue/simd.h | 418 ++++++++++++++++++ 13 files changed, 480 insertions(+), 446 deletions(-) delete mode 100644 src/nnue/layers/simd.h create mode 100644 src/nnue/simd.h diff --git a/src/Makefile b/src/Makefile index 72d06f09e..9ea3e01b7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -60,9 +60,9 @@ SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \ nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \ - nnue/layers/affine_transform_sparse_input.h nnue/layers/clipped_relu.h nnue/layers/simd.h \ + nnue/layers/affine_transform_sparse_input.h nnue/layers/clipped_relu.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ - nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ + nnue/nnue_common.h nnue/nnue_feature_transformer.h nnue/simd.h position.h \ search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h memory.h diff --git a/src/misc.h b/src/misc.h index 84f11d6de..756433290 100644 --- a/src/misc.h +++ b/src/misc.h @@ -317,6 +317,22 @@ void move_to_front(std::vector& vec, Predicate pred) { } } +#if defined(__GNUC__) && !defined(__clang__) + #if __GNUC__ >= 13 + #define sf_assume(cond) __attribute__((assume(cond))) + #else + #define sf_assume(cond) \ + do \ + { \ + if (!(cond)) \ + __builtin_unreachable(); \ + } while (0) + #endif +#else + // do nothing for other compilers + #define sf_assume(cond) +#endif + } // namespace Stockfish #endif // #ifndef MISC_H_INCLUDED diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index c91da2cc8..70e9beeb1 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -23,7 +23,7 @@ #include "../../bitboard.h" #include "../../position.h" #include "../../types.h" -#include "../nnue_accumulator.h" +#include "../nnue_common.h" namespace Stockfish::Eval::NNUE::Features { diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 3920efb17..b4440c1e3 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -25,7 +25,7 @@ #include #include "../nnue_common.h" -#include "simd.h" +#include "../simd.h" /* This file contains the definition for a fully connected layer (aka affine transform). @@ -102,7 +102,7 @@ static void affine_transform_non_ssse3(std::int32_t* output, product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); sum = vpadalq_s16(sum, product); } - output[i] = Simd::neon_m128_reduce_add_epi32(sum); + output[i] = SIMD::neon_m128_reduce_add_epi32(sum); #endif } @@ -191,20 +191,20 @@ class AffineTransform { #if defined(USE_AVX512) using vec_t = __m512i; #define vec_set_32 _mm512_set1_epi32 - #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 + #define vec_add_dpbusd_32 SIMD::m512_add_dpbusd_epi32 #elif defined(USE_AVX2) using vec_t = __m256i; #define vec_set_32 _mm256_set1_epi32 - #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #define vec_add_dpbusd_32 SIMD::m256_add_dpbusd_epi32 #elif defined(USE_SSSE3) using vec_t = __m128i; #define vec_set_32 _mm_set1_epi32 - #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32 SIMD::m128_add_dpbusd_epi32 #elif defined(USE_NEON_DOTPROD) using vec_t = int32x4_t; #define vec_set_32 vdupq_n_s32 #define vec_add_dpbusd_32(acc, a, b) \ - Simd::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \ + SIMD::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \ vreinterpretq_s8_s32(b)) #endif @@ -245,20 +245,20 @@ class AffineTransform { #if defined(USE_AVX2) using vec_t = __m256i; #define vec_setzero() _mm256_setzero_si256() - #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 - #define vec_hadd Simd::m256_hadd + #define vec_add_dpbusd_32 SIMD::m256_add_dpbusd_epi32 + #define vec_hadd SIMD::m256_hadd #elif defined(USE_SSSE3) using vec_t = __m128i; #define vec_setzero() _mm_setzero_si128() - #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 - #define vec_hadd Simd::m128_hadd + #define vec_add_dpbusd_32 SIMD::m128_add_dpbusd_epi32 + #define vec_hadd SIMD::m128_hadd #elif defined(USE_NEON_DOTPROD) using vec_t = int32x4_t; #define vec_setzero() vdupq_n_s32(0) #define vec_add_dpbusd_32(acc, a, b) \ - Simd::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \ + SIMD::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \ vreinterpretq_s8_s32(b)) - #define vec_hadd Simd::neon_m128_hadd + #define vec_hadd SIMD::neon_m128_hadd #endif const auto inputVector = reinterpret_cast(input); diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index be5e30b5e..7c74d5e64 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -22,14 +22,12 @@ #define NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED #include -#include #include #include #include "../../bitboard.h" +#include "../simd.h" #include "../nnue_common.h" -#include "affine_transform.h" -#include "simd.h" /* This file contains the definition for a fully connected layer (aka affine transform) with block sparse input. @@ -77,53 +75,16 @@ alignas(CacheLineSize) static constexpr struct OffsetIndices { // Find indices of nonzero numbers in an int32_t array template void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) { - #if defined(USE_SSSE3) - #if defined(USE_AVX512) - using vec_t = __m512i; - #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) - #elif defined(USE_AVX2) - using vec_t = __m256i; - #if defined(USE_VNNI) && !defined(USE_AVXVNNI) - #define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256()) - #else - #define vec_nnz(a) \ - _mm256_movemask_ps( \ - _mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) - #endif - #elif defined(USE_SSSE3) - using vec_t = __m128i; - #define vec_nnz(a) \ - _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128()))) - #endif - using vec128_t = __m128i; - #define vec128_zero _mm_setzero_si128() - #define vec128_set_16(a) _mm_set1_epi16(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) - using vec_t = uint32x4_t; - static const std::uint32_t Mask[4] = {1, 2, 4, 8}; - #define vec_nnz(a) vaddvq_u32(vandq_u32(vtstq_u32(a, a), vld1q_u32(Mask))) - using vec128_t = uint16x8_t; - #define vec128_zero vdupq_n_u16(0) - #define vec128_set_16(a) vdupq_n_u16(a) - #define vec128_load(a) vld1q_u16(reinterpret_cast(a)) - #define vec128_storeu(a, b) vst1q_u16(reinterpret_cast(a), b) - #define vec128_add(a, b) vaddq_u16(a, b) - #endif - constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(std::int32_t); + using namespace SIMD; + + constexpr IndexType InputSimdWidth = sizeof(vec_uint_t) / sizeof(std::int32_t); // Inputs are processed InputSimdWidth at a time and outputs are processed 8 at a time so we process in chunks of max(InputSimdWidth, 8) constexpr IndexType ChunkSize = std::max(InputSimdWidth, 8); constexpr IndexType NumChunks = InputDimensions / ChunkSize; constexpr IndexType InputsPerChunk = ChunkSize / InputSimdWidth; constexpr IndexType OutputsPerChunk = ChunkSize / 8; - const auto inputVector = reinterpret_cast(input); + const auto inputVector = reinterpret_cast(input); IndexType count = 0; vec128_t base = vec128_zero; const vec128_t increment = vec128_set_16(8); @@ -133,7 +94,7 @@ void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_ou unsigned nnz = 0; for (IndexType j = 0; j < InputsPerChunk; ++j) { - const vec_t inputChunk = inputVector[i * InputsPerChunk + j]; + const vec_uint_t inputChunk = inputVector[i * InputsPerChunk + j]; nnz |= unsigned(vec_nnz(inputChunk)) << (j * InputSimdWidth); } for (IndexType j = 0; j < OutputsPerChunk; ++j) @@ -148,12 +109,7 @@ void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_ou } count_out = count; } - #undef vec_nnz - #undef vec128_zero - #undef vec128_set_16 - #undef vec128_load - #undef vec128_storeu - #undef vec128_add + #endif // Sparse input implementation @@ -232,27 +188,27 @@ class AffineTransformSparseInput { using invec_t = __m512i; using outvec_t = __m512i; #define vec_set_32 _mm512_set1_epi32 - #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 + #define vec_add_dpbusd_32 SIMD::m512_add_dpbusd_epi32 #elif defined(USE_AVX2) using invec_t = __m256i; using outvec_t = __m256i; #define vec_set_32 _mm256_set1_epi32 - #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #define vec_add_dpbusd_32 SIMD::m256_add_dpbusd_epi32 #elif defined(USE_SSSE3) using invec_t = __m128i; using outvec_t = __m128i; #define vec_set_32 _mm_set1_epi32 - #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32 SIMD::m128_add_dpbusd_epi32 #elif defined(USE_NEON_DOTPROD) using invec_t = int8x16_t; using outvec_t = int32x4_t; #define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a)) - #define vec_add_dpbusd_32 Simd::dotprod_m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32 SIMD::dotprod_m128_add_dpbusd_epi32 #elif defined(USE_NEON) using invec_t = int8x16_t; using outvec_t = int32x4_t; #define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a)) - #define vec_add_dpbusd_32 Simd::neon_m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32 SIMD::neon_m128_add_dpbusd_epi32 #endif static constexpr IndexType OutputSimdWidth = sizeof(outvec_t) / sizeof(OutputType); diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h deleted file mode 100644 index 70ca68a0c..000000000 --- a/src/nnue/layers/simd.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef STOCKFISH_SIMD_H_INCLUDED -#define STOCKFISH_SIMD_H_INCLUDED - -#if defined(USE_AVX2) - #include - -#elif defined(USE_SSE41) - #include - -#elif defined(USE_SSSE3) - #include - -#elif defined(USE_SSE2) - #include - -#elif defined(USE_NEON) - #include -#endif - -namespace Stockfish::Simd { - -#if defined(USE_AVX512) - -[[maybe_unused]] static int m512_hadd(__m512i sum, int bias) { - return _mm512_reduce_add_epi32(sum) + bias; -} - -[[maybe_unused]] static void m512_add_dpbusd_epi32(__m512i& acc, __m512i a, __m512i b) { - - #if defined(USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a, b); - #else - __m512i product0 = _mm512_maddubs_epi16(a, b); - product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); - acc = _mm512_add_epi32(acc, product0); - #endif -} - -#endif - -#if defined(USE_AVX2) - -[[maybe_unused]] static int m256_hadd(__m256i sum, int bias) { - __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); - return _mm_cvtsi128_si32(sum128) + bias; -} - -[[maybe_unused]] static void m256_add_dpbusd_epi32(__m256i& acc, __m256i a, __m256i b) { - - #if defined(USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a, b); - #else - __m256i product0 = _mm256_maddubs_epi16(a, b); - product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); - acc = _mm256_add_epi32(acc, product0); - #endif -} - -#endif - -#if defined(USE_SSSE3) - -[[maybe_unused]] static int m128_hadd(__m128i sum, int bias) { - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB - return _mm_cvtsi128_si32(sum) + bias; -} - -[[maybe_unused]] static void m128_add_dpbusd_epi32(__m128i& acc, __m128i a, __m128i b) { - - __m128i product0 = _mm_maddubs_epi16(a, b); - product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); - acc = _mm_add_epi32(acc, product0); -} - -#endif - -#if defined(USE_NEON_DOTPROD) - -[[maybe_unused]] static void -dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { - - acc = vdotq_s32(acc, a, b); -} -#endif - -#if defined(USE_NEON) - -[[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) { - #if USE_NEON >= 8 - return vaddvq_s32(s); - #else - return s[0] + s[1] + s[2] + s[3]; - #endif -} - -[[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) { - return neon_m128_reduce_add_epi32(sum) + bias; -} - -#endif - -#if USE_NEON >= 8 -[[maybe_unused]] static void neon_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { - - int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b)); - int16x8_t product1 = vmull_high_s8(a, b); - int16x8_t sum = vpaddq_s16(product0, product1); - acc = vpadalq_s16(acc, sum); -} -#endif -} - -#endif // STOCKFISH_SIMD_H_INCLUDED diff --git a/src/nnue/network.h b/src/nnue/network.h index cd32c5312..c9358823b 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -32,6 +32,7 @@ #include "../types.h" #include "nnue_accumulator.h" #include "nnue_architecture.h" +#include "nnue_common.h" #include "nnue_feature_transformer.h" #include "nnue_misc.h" diff --git a/src/nnue/nnue_accumulator.cpp b/src/nnue/nnue_accumulator.cpp index 83b09637a..d13105aa4 100644 --- a/src/nnue/nnue_accumulator.cpp +++ b/src/nnue/nnue_accumulator.cpp @@ -28,25 +28,12 @@ #include "../position.h" #include "../types.h" #include "nnue_architecture.h" -#include "nnue_feature_transformer.h" +#include "nnue_feature_transformer.h" // IWYU pragma: keep +#include "simd.h" namespace Stockfish::Eval::NNUE { -#if defined(__GNUC__) && !defined(__clang__) - #if __GNUC__ >= 13 - #define sf_assume(cond) __attribute__((assume(cond))) - #else - #define sf_assume(cond) \ - do \ - { \ - if (!(cond)) \ - __builtin_unreachable(); \ - } while (0) - #endif -#else - // do nothing for other compilers - #define sf_assume(cond) -#endif +using namespace SIMD; namespace { @@ -381,7 +368,7 @@ void update_accumulator_refresh_cache(const FeatureTransformer& feat AccumulatorState& accumulatorState, AccumulatorCaches::Cache& cache) { - using Tiling [[maybe_unused]] = SIMDTiling; + using Tiling [[maybe_unused]] = SIMDTiling; const Square ksq = pos.square(Perspective); auto& entry = cache[ksq][Perspective]; diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index aa9e2a676..10aadc917 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -37,10 +37,6 @@ class Position; namespace Stockfish::Eval::NNUE { -using BiasType = std::int16_t; -using PSQTWeightType = std::int32_t; -using IndexType = std::uint32_t; - template struct alignas(CacheLineSize) Accumulator; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 0c9f097dc..c020ce05b 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -49,6 +49,12 @@ constexpr int L3Small = 32; constexpr IndexType PSQTBuckets = 8; constexpr IndexType LayerStacks = 8; +// If vector instructions are enabled, we update and refresh the +// accumulator tile by tile such that each tile fits in the CPU's +// vector registers. +static_assert(PSQTBuckets % 8 == 0, + "Per feature PSQT values cannot be processed at granularity lower than 8 at a time."); + template struct NetworkArchitecture { static constexpr IndexType TransformedFeatureDimensions = L1; diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index f21a8dec7..35cc8a5fe 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -48,6 +48,11 @@ namespace Stockfish::Eval::NNUE { +using BiasType = std::int16_t; +using WeightType = std::int16_t; +using PSQTWeightType = std::int32_t; +using IndexType = std::uint32_t; + // Version of the evaluation file constexpr std::uint32_t Version = 0x7AF32F20u; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index beb0c7f1c..37634cbc6 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -31,174 +31,10 @@ #include "nnue_accumulator.h" #include "nnue_architecture.h" #include "nnue_common.h" +#include "simd.h" namespace Stockfish::Eval::NNUE { -using BiasType = std::int16_t; -using WeightType = std::int16_t; -using PSQTWeightType = std::int32_t; - -// If vector instructions are enabled, we update and refresh the -// accumulator tile by tile such that each tile fits in the CPU's -// vector registers. -#define VECTOR - -static_assert(PSQTBuckets % 8 == 0, - "Per feature PSQT values cannot be processed at granularity lower than 8 at a time."); - -#ifdef USE_AVX512 -using vec_t = __m512i; -using psqt_vec_t = __m256i; - #define vec_load(a) _mm512_load_si512(a) - #define vec_store(a, b) _mm512_store_si512(a, b) - #define vec_add_16(a, b) _mm512_add_epi16(a, b) - #define vec_sub_16(a, b) _mm512_sub_epi16(a, b) - #define vec_mulhi_16(a, b) _mm512_mulhi_epi16(a, b) - #define vec_zero() _mm512_setzero_epi32() - #define vec_set_16(a) _mm512_set1_epi16(a) - #define vec_max_16(a, b) _mm512_max_epi16(a, b) - #define vec_min_16(a, b) _mm512_min_epi16(a, b) - #define vec_slli_16(a, b) _mm512_slli_epi16(a, b) - // Inverse permuted at load time - #define vec_packus_16(a, b) _mm512_packus_epi16(a, b) - #define vec_load_psqt(a) _mm256_load_si256(a) - #define vec_store_psqt(a, b) _mm256_store_si256(a, b) - #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) - #define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b) - #define vec_zero_psqt() _mm256_setzero_si256() - #define NumRegistersSIMD 16 - #define MaxChunkSize 64 - -#elif USE_AVX2 -using vec_t = __m256i; -using psqt_vec_t = __m256i; - #define vec_load(a) _mm256_load_si256(a) - #define vec_store(a, b) _mm256_store_si256(a, b) - #define vec_add_16(a, b) _mm256_add_epi16(a, b) - #define vec_sub_16(a, b) _mm256_sub_epi16(a, b) - #define vec_mulhi_16(a, b) _mm256_mulhi_epi16(a, b) - #define vec_zero() _mm256_setzero_si256() - #define vec_set_16(a) _mm256_set1_epi16(a) - #define vec_max_16(a, b) _mm256_max_epi16(a, b) - #define vec_min_16(a, b) _mm256_min_epi16(a, b) - #define vec_slli_16(a, b) _mm256_slli_epi16(a, b) - // Inverse permuted at load time - #define vec_packus_16(a, b) _mm256_packus_epi16(a, b) - #define vec_load_psqt(a) _mm256_load_si256(a) - #define vec_store_psqt(a, b) _mm256_store_si256(a, b) - #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) - #define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b) - #define vec_zero_psqt() _mm256_setzero_si256() - #define NumRegistersSIMD 16 - #define MaxChunkSize 32 - -#elif USE_SSE2 -using vec_t = __m128i; -using psqt_vec_t = __m128i; - #define vec_load(a) (*(a)) - #define vec_store(a, b) *(a) = (b) - #define vec_add_16(a, b) _mm_add_epi16(a, b) - #define vec_sub_16(a, b) _mm_sub_epi16(a, b) - #define vec_mulhi_16(a, b) _mm_mulhi_epi16(a, b) - #define vec_zero() _mm_setzero_si128() - #define vec_set_16(a) _mm_set1_epi16(a) - #define vec_max_16(a, b) _mm_max_epi16(a, b) - #define vec_min_16(a, b) _mm_min_epi16(a, b) - #define vec_slli_16(a, b) _mm_slli_epi16(a, b) - #define vec_packus_16(a, b) _mm_packus_epi16(a, b) - #define vec_load_psqt(a) (*(a)) - #define vec_store_psqt(a, b) *(a) = (b) - #define vec_add_psqt_32(a, b) _mm_add_epi32(a, b) - #define vec_sub_psqt_32(a, b) _mm_sub_epi32(a, b) - #define vec_zero_psqt() _mm_setzero_si128() - #define NumRegistersSIMD (Is64Bit ? 16 : 8) - #define MaxChunkSize 16 - -#elif USE_NEON -using vec_t = int16x8_t; -using psqt_vec_t = int32x4_t; - #define vec_load(a) (*(a)) - #define vec_store(a, b) *(a) = (b) - #define vec_add_16(a, b) vaddq_s16(a, b) - #define vec_sub_16(a, b) vsubq_s16(a, b) - #define vec_mulhi_16(a, b) vqdmulhq_s16(a, b) - #define vec_zero() vec_t{0} - #define vec_set_16(a) vdupq_n_s16(a) - #define vec_max_16(a, b) vmaxq_s16(a, b) - #define vec_min_16(a, b) vminq_s16(a, b) - #define vec_slli_16(a, b) vshlq_s16(a, vec_set_16(b)) - #define vec_packus_16(a, b) reinterpret_cast(vcombine_u8(vqmovun_s16(a), vqmovun_s16(b))) - #define vec_load_psqt(a) (*(a)) - #define vec_store_psqt(a, b) *(a) = (b) - #define vec_add_psqt_32(a, b) vaddq_s32(a, b) - #define vec_sub_psqt_32(a, b) vsubq_s32(a, b) - #define vec_zero_psqt() psqt_vec_t{0} - #define NumRegistersSIMD 16 - #define MaxChunkSize 16 - -#else - #undef VECTOR - -#endif - -struct Vec16Wrapper { -#ifdef VECTOR - using type = vec_t; - static type add(const type& lhs, const type& rhs) { return vec_add_16(lhs, rhs); } - static type sub(const type& lhs, const type& rhs) { return vec_sub_16(lhs, rhs); } -#else - using type = BiasType; - static type add(const type& lhs, const type& rhs) { return lhs + rhs; } - static type sub(const type& lhs, const type& rhs) { return lhs - rhs; } -#endif -}; - -struct Vec32Wrapper { -#ifdef VECTOR - using type = psqt_vec_t; - static type add(const type& lhs, const type& rhs) { return vec_add_psqt_32(lhs, rhs); } - static type sub(const type& lhs, const type& rhs) { return vec_sub_psqt_32(lhs, rhs); } -#else - using type = PSQTWeightType; - static type add(const type& lhs, const type& rhs) { return lhs + rhs; } - static type sub(const type& lhs, const type& rhs) { return lhs - rhs; } -#endif -}; - -enum UpdateOperation { - Add, - Sub -}; - -template = true> -typename VecWrapper::type fused(const typename VecWrapper::type& in) { - return in; -} - -template, bool> = true, - std::enable_if_t = true> -typename VecWrapper::type -fused(const typename VecWrapper::type& in, const T& operand, const Ts&... operands) { - switch (update_op) - { - case Add : - return fused(VecWrapper::add(in, operand), operands...); - case Sub : - return fused(VecWrapper::sub(in, operand), operands...); - default : - static_assert(update_op == Add || update_op == Sub, - "Only Add and Sub are currently supported."); - return typename VecWrapper::type(); - } -} - // Returns the inverse of a permutation template constexpr std::array @@ -240,61 +76,6 @@ void permute(T (&data)[N], const std::array& order) { } } -// Compute optimal SIMD register count for feature transformer accumulation. -template -class SIMDTiling { -#ifdef VECTOR - // We use __m* types as template arguments, which causes GCC to emit warnings - // about losing some attribute information. This is irrelevant to us as we - // only take their size, so the following pragma are harmless. - #if defined(__GNUC__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wignored-attributes" - #endif - - template - static constexpr int BestRegisterCount() { - constexpr std::size_t RegisterSize = sizeof(SIMDRegisterType); - constexpr std::size_t LaneSize = sizeof(LaneType); - - static_assert(RegisterSize >= LaneSize); - static_assert(MaxRegisters <= NumRegistersSIMD); - static_assert(MaxRegisters > 0); - static_assert(NumRegistersSIMD > 0); - static_assert(RegisterSize % LaneSize == 0); - static_assert((NumLanes * LaneSize) % RegisterSize == 0); - - const int ideal = (NumLanes * LaneSize) / RegisterSize; - if (ideal <= MaxRegisters) - return ideal; - - // Look for the largest divisor of the ideal register count that is smaller than MaxRegisters - for (int divisor = MaxRegisters; divisor > 1; --divisor) - if (ideal % divisor == 0) - return divisor; - - return 1; - } - - #if defined(__GNUC__) - #pragma GCC diagnostic pop - #endif - - public: - static constexpr int NumRegs = - BestRegisterCount(); - static constexpr int NumPsqtRegs = - BestRegisterCount(); - - static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; - static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4; - - static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions"); - static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets"); -#endif -}; - - // Input feature converter template class FeatureTransformer { @@ -397,6 +178,8 @@ class FeatureTransformer { OutputType* output, int bucket) const { + using namespace SIMD; + accumulatorStack.evaluate(pos, *this, *cache); const auto& accumulatorState = accumulatorStack.latest(); diff --git a/src/nnue/simd.h b/src/nnue/simd.h new file mode 100644 index 000000000..560223000 --- /dev/null +++ b/src/nnue/simd.h @@ -0,0 +1,418 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef NNUE_SIMD_H_INCLUDED +#define NNUE_SIMD_H_INCLUDED + +#if defined(USE_AVX2) + #include + +#elif defined(USE_SSE41) + #include + +#elif defined(USE_SSSE3) + #include + +#elif defined(USE_SSE2) + #include + +#elif defined(USE_NEON) + #include +#endif + +#include "../types.h" +#include "nnue_common.h" + +namespace Stockfish::Eval::NNUE::SIMD { + +// If vector instructions are enabled, we update and refresh the +// accumulator tile by tile such that each tile fits in the CPU's +// vector registers. +#define VECTOR + +#ifdef USE_AVX512 +using vec_t = __m512i; +using vec128_t = __m128i; +using psqt_vec_t = __m256i; +using vec_uint_t = __m512i; + #define vec_load(a) _mm512_load_si512(a) + #define vec_store(a, b) _mm512_store_si512(a, b) + #define vec_add_16(a, b) _mm512_add_epi16(a, b) + #define vec_sub_16(a, b) _mm512_sub_epi16(a, b) + #define vec_mulhi_16(a, b) _mm512_mulhi_epi16(a, b) + #define vec_zero() _mm512_setzero_epi32() + #define vec_set_16(a) _mm512_set1_epi16(a) + #define vec_max_16(a, b) _mm512_max_epi16(a, b) + #define vec_min_16(a, b) _mm512_min_epi16(a, b) + #define vec_slli_16(a, b) _mm512_slli_epi16(a, b) + // Inverse permuted at load time + #define vec_packus_16(a, b) _mm512_packus_epi16(a, b) + #define vec_load_psqt(a) _mm256_load_si256(a) + #define vec_store_psqt(a, b) _mm256_store_si256(a, b) + #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) + #define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b) + #define vec_zero_psqt() _mm256_setzero_si256() + + #ifdef USE_SSSE3 + #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) + #endif + + #define vec128_zero _mm_setzero_si128() + #define vec128_set_16(a) _mm_set1_epi16(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) + #define NumRegistersSIMD 16 + #define MaxChunkSize 64 + +#elif USE_AVX2 +using vec_t = __m256i; +using vec128_t = __m128i; +using psqt_vec_t = __m256i; +using vec_uint_t = __m256i; + #define vec_load(a) _mm256_load_si256(a) + #define vec_store(a, b) _mm256_store_si256(a, b) + #define vec_add_16(a, b) _mm256_add_epi16(a, b) + #define vec_sub_16(a, b) _mm256_sub_epi16(a, b) + #define vec_mulhi_16(a, b) _mm256_mulhi_epi16(a, b) + #define vec_zero() _mm256_setzero_si256() + #define vec_set_16(a) _mm256_set1_epi16(a) + #define vec_max_16(a, b) _mm256_max_epi16(a, b) + #define vec_min_16(a, b) _mm256_min_epi16(a, b) + #define vec_slli_16(a, b) _mm256_slli_epi16(a, b) + // Inverse permuted at load time + #define vec_packus_16(a, b) _mm256_packus_epi16(a, b) + #define vec_load_psqt(a) _mm256_load_si256(a) + #define vec_store_psqt(a, b) _mm256_store_si256(a, b) + #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) + #define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b) + #define vec_zero_psqt() _mm256_setzero_si256() + + #ifdef USE_SSSE3 + #if defined(USE_VNNI) && !defined(USE_AVXVNNI) + #define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256()) + #else + #define vec_nnz(a) \ + _mm256_movemask_ps( \ + _mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) + #endif + #endif + + #define vec128_zero _mm_setzero_si128() + #define vec128_set_16(a) _mm_set1_epi16(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) + + #define NumRegistersSIMD 16 + #define MaxChunkSize 32 + +#elif USE_SSE2 +using vec_t = __m128i; +using vec128_t = __m128i; +using psqt_vec_t = __m128i; +using vec_uint_t = __m128i; + #define vec_load(a) (*(a)) + #define vec_store(a, b) *(a) = (b) + #define vec_add_16(a, b) _mm_add_epi16(a, b) + #define vec_sub_16(a, b) _mm_sub_epi16(a, b) + #define vec_mulhi_16(a, b) _mm_mulhi_epi16(a, b) + #define vec_zero() _mm_setzero_si128() + #define vec_set_16(a) _mm_set1_epi16(a) + #define vec_max_16(a, b) _mm_max_epi16(a, b) + #define vec_min_16(a, b) _mm_min_epi16(a, b) + #define vec_slli_16(a, b) _mm_slli_epi16(a, b) + #define vec_packus_16(a, b) _mm_packus_epi16(a, b) + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a, b) *(a) = (b) + #define vec_add_psqt_32(a, b) _mm_add_epi32(a, b) + #define vec_sub_psqt_32(a, b) _mm_sub_epi32(a, b) + #define vec_zero_psqt() _mm_setzero_si128() + + #ifdef USE_SSSE3 + #define vec_nnz(a) \ + _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128()))) + #endif + + #define vec128_zero _mm_setzero_si128() + #define vec128_set_16(a) _mm_set1_epi16(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) + + #define NumRegistersSIMD (Is64Bit ? 16 : 8) + #define MaxChunkSize 16 + +#elif USE_NEON +using vec_t = int16x8_t; +using psqt_vec_t = int32x4_t; +using vec128_t = uint16x8_t; +using vec_uint_t = uint32x4_t; + #define vec_load(a) (*(a)) + #define vec_store(a, b) *(a) = (b) + #define vec_add_16(a, b) vaddq_s16(a, b) + #define vec_sub_16(a, b) vsubq_s16(a, b) + #define vec_mulhi_16(a, b) vqdmulhq_s16(a, b) + #define vec_zero() vec_t{0} + #define vec_set_16(a) vdupq_n_s16(a) + #define vec_max_16(a, b) vmaxq_s16(a, b) + #define vec_min_16(a, b) vminq_s16(a, b) + #define vec_slli_16(a, b) vshlq_s16(a, vec_set_16(b)) + #define vec_packus_16(a, b) reinterpret_cast(vcombine_u8(vqmovun_s16(a), vqmovun_s16(b))) + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a, b) *(a) = (b) + #define vec_add_psqt_32(a, b) vaddq_s32(a, b) + #define vec_sub_psqt_32(a, b) vsubq_s32(a, b) + #define vec_zero_psqt() psqt_vec_t{0} + +static constexpr std::uint32_t Mask[4] = {1, 2, 4, 8}; + #define vec_nnz(a) vaddvq_u32(vandq_u32(vtstq_u32(a, a), vld1q_u32(Mask))) + #define vec128_zero vdupq_n_u16(0) + #define vec128_set_16(a) vdupq_n_u16(a) + #define vec128_load(a) vld1q_u16(reinterpret_cast(a)) + #define vec128_storeu(a, b) vst1q_u16(reinterpret_cast(a), b) + #define vec128_add(a, b) vaddq_u16(a, b) + + #define NumRegistersSIMD 16 + #define MaxChunkSize 16 + +#else + #undef VECTOR + +#endif + +struct Vec16Wrapper { +#ifdef VECTOR + using type = vec_t; + static type add(const type& lhs, const type& rhs) { return vec_add_16(lhs, rhs); } + static type sub(const type& lhs, const type& rhs) { return vec_sub_16(lhs, rhs); } +#else + using type = BiasType; + static type add(const type& lhs, const type& rhs) { return lhs + rhs; } + static type sub(const type& lhs, const type& rhs) { return lhs - rhs; } +#endif +}; + +struct Vec32Wrapper { +#ifdef VECTOR + using type = psqt_vec_t; + static type add(const type& lhs, const type& rhs) { return vec_add_psqt_32(lhs, rhs); } + static type sub(const type& lhs, const type& rhs) { return vec_sub_psqt_32(lhs, rhs); } +#else + using type = PSQTWeightType; + static type add(const type& lhs, const type& rhs) { return lhs + rhs; } + static type sub(const type& lhs, const type& rhs) { return lhs - rhs; } +#endif +}; + +enum UpdateOperation { + Add, + Sub +}; + +template = true> +typename VecWrapper::type fused(const typename VecWrapper::type& in) { + return in; +} + +template, bool> = true, + std::enable_if_t = true> +typename VecWrapper::type +fused(const typename VecWrapper::type& in, const T& operand, const Ts&... operands) { + switch (update_op) + { + case Add : + return fused(VecWrapper::add(in, operand), operands...); + case Sub : + return fused(VecWrapper::sub(in, operand), operands...); + default : + static_assert(update_op == Add || update_op == Sub, + "Only Add and Sub are currently supported."); + return typename VecWrapper::type(); + } +} + +#if defined(USE_AVX512) + +[[maybe_unused]] static int m512_hadd(__m512i sum, int bias) { + return _mm512_reduce_add_epi32(sum) + bias; +} + +[[maybe_unused]] static void m512_add_dpbusd_epi32(__m512i& acc, __m512i a, __m512i b) { + + #if defined(USE_VNNI) + acc = _mm512_dpbusd_epi32(acc, a, b); + #else + __m512i product0 = _mm512_maddubs_epi16(a, b); + product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); + acc = _mm512_add_epi32(acc, product0); + #endif +} + +#endif + +#if defined(USE_AVX2) + +[[maybe_unused]] static int m256_hadd(__m256i sum, int bias) { + __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); + return _mm_cvtsi128_si32(sum128) + bias; +} + +[[maybe_unused]] static void m256_add_dpbusd_epi32(__m256i& acc, __m256i a, __m256i b) { + + #if defined(USE_VNNI) + acc = _mm256_dpbusd_epi32(acc, a, b); + #else + __m256i product0 = _mm256_maddubs_epi16(a, b); + product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); + acc = _mm256_add_epi32(acc, product0); + #endif +} + +#endif + +#if defined(USE_SSSE3) + +[[maybe_unused]] static int m128_hadd(__m128i sum, int bias) { + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB + return _mm_cvtsi128_si32(sum) + bias; +} + +[[maybe_unused]] static void m128_add_dpbusd_epi32(__m128i& acc, __m128i a, __m128i b) { + + __m128i product0 = _mm_maddubs_epi16(a, b); + product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); + acc = _mm_add_epi32(acc, product0); +} + +#endif + +#if defined(USE_NEON_DOTPROD) + +[[maybe_unused]] static void +dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { + + acc = vdotq_s32(acc, a, b); +} +#endif + +#if defined(USE_NEON) + +[[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) { + #if USE_NEON >= 8 + return vaddvq_s32(s); + #else + return s[0] + s[1] + s[2] + s[3]; + #endif +} + +[[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) { + return neon_m128_reduce_add_epi32(sum) + bias; +} + +#endif + +#if USE_NEON >= 8 +[[maybe_unused]] static void neon_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { + + int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b)); + int16x8_t product1 = vmull_high_s8(a, b); + int16x8_t sum = vpaddq_s16(product0, product1); + acc = vpadalq_s16(acc, sum); +} +#endif + + +// Compute optimal SIMD register count for feature transformer accumulation. +template +class SIMDTiling { +#ifdef VECTOR + // We use __m* types as template arguments, which causes GCC to emit warnings + // about losing some attribute information. This is irrelevant to us as we + // only take their size, so the following pragma are harmless. + #if defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wignored-attributes" + #endif + + template + static constexpr int BestRegisterCount() { + constexpr std::size_t RegisterSize = sizeof(SIMDRegisterType); + constexpr std::size_t LaneSize = sizeof(LaneType); + + static_assert(RegisterSize >= LaneSize); + static_assert(MaxRegisters <= NumRegistersSIMD); + static_assert(MaxRegisters > 0); + static_assert(NumRegistersSIMD > 0); + static_assert(RegisterSize % LaneSize == 0); + static_assert((NumLanes * LaneSize) % RegisterSize == 0); + + const int ideal = (NumLanes * LaneSize) / RegisterSize; + if (ideal <= MaxRegisters) + return ideal; + + // Look for the largest divisor of the ideal register count that is smaller than MaxRegisters + for (int divisor = MaxRegisters; divisor > 1; --divisor) + if (ideal % divisor == 0) + return divisor; + + return 1; + } + + #if defined(__GNUC__) + #pragma GCC diagnostic pop + #endif + + public: + static constexpr int NumRegs = + BestRegisterCount(); + static constexpr int NumPsqtRegs = + BestRegisterCount(); + + static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; + static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4; + + static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions"); + static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets"); +#endif +}; +} + +#endif From f58d923fe0e8cb4545237c00ffebd264fe7fecaf Mon Sep 17 00:00:00 2001 From: pb00067 Date: Fri, 23 May 2025 09:29:32 +0200 Subject: [PATCH 065/107] Simplify & improve stalemate detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change is functional because now we verify for stalemate also on captures and when not giving check. Green STC test on stalemate-book https://tests.stockfishchess.org/tests/view/682d878f6ec7634154f9ad2f Elo: 2.29 ± 1.3 (95%) LOS: 100.0% Total: 10000 W: 4637 L: 4571 D: 792 Ptnml(0-2): 2, 132, 4664, 202, 0 nElo: 12.42 ± 6.8 (95%) PairsRatio: 1.51 Green LTC test on stalemate-book https://tests.stockfishchess.org/tests/view/682daa2d6ec7634154f9ad67 Elo: 0.80 ± 0.8 (95%) LOS: 96.9% Total: 10000 W: 4727 L: 4704 D: 569 Ptnml(0-2): 0, 64, 4849, 87, 0 nElo: 6.51 ± 6.8 (95%) PairsRatio: 1.36 Passed non-regression test @ LTC https://tests.stockfishchess.org/tests/view/682dd10d6ec7634154f9adb3 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 148512 W: 38135 L: 38046 D: 72331 Ptnml(0-2): 55, 15759, 42558, 15810, 74 N.B.: The unique concern I have, is that due changes in future a negative SEE capture see might be returned in GOOD_CAPTURE stage. In this case the assert in can_move_king_or_pawn() will trigger since we must guarantee that all moves (also quiets) are generated in movepicker when calling can_move_king_or_pawn(). closes https://github.com/official-stockfish/Stockfish/pull/6088 bench: 2178135 --- src/movepick.cpp | 3 ++- src/search.cpp | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 6d00dd941..f3d6fef8b 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -314,7 +314,8 @@ void MovePicker::skip_quiet_moves() { skipQuiets = true; } // this function must be called after all quiet moves and captures have been generated bool MovePicker::can_move_king_or_pawn() { - assert(stage == GOOD_QUIET || stage == BAD_QUIET || stage == EVASION); + // SEE negative captures shouldn't be returned in GOOD_CAPTURE stage + assert(stage > GOOD_CAPTURE && stage != EVASION_INIT); for (ExtMove* m = moves; m < endMoves; ++m) { diff --git a/src/search.cpp b/src/search.cpp index 775aaceff..ee876f855 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1073,9 +1073,7 @@ moves_loop: // When in check, search starts here if (!pos.see_ge(move, -158 * depth - seeHist)) { bool mayStalemateTrap = - depth > 2 && givesCheck && alpha < 0 - && !capture // we consider that captures will likely destroy the stalemate configuration - && pos.non_pawn_material(us) == PieceValue[movedPiece] + depth > 2 && alpha < 0 && pos.non_pawn_material(us) == PieceValue[movedPiece] && PieceValue[movedPiece] >= RookValue // it can't be stalemate if we moved a piece adjacent to the king && !(attacks_bb(pos.square(us)) & move.from_sq()) From b1b5893a8e344155d01a1658b79670a8ed1fd160 Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Fri, 23 May 2025 18:33:01 +0200 Subject: [PATCH 066/107] Minor code improvements - Remove / add empty lines - fix the `ttcapture` comment - remove the `bonus` variable for `ttMoveHistory` - remove unnecessary parentheses / brackets - refactor the movepick good quiet stage - rename `endMoves` to `endCur`, as the previous name suggests that it points to the end of all generated moves, which it does not. closes https://github.com/official-stockfish/Stockfish/pull/6089 No functional change. Co-Authored-By: xu-shawn <50402888+xu-shawn@users.noreply.github.com> --- src/movepick.cpp | 33 ++++++++++++++++++--------------- src/movepick.h | 4 ++-- src/search.cpp | 15 +++++---------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index f3d6fef8b..faef753be 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -173,7 +173,7 @@ void MovePicker::score() { if (KNIGHT <= pt && pt <= QUEEN) { static constexpr int bonus[QUEEN + 1] = {0, 0, 144, 144, 256, 517}; - int v = (threatByLesser[pt] & to ? -95 : 100 * bool(threatByLesser[pt] & from)); + int v = threatByLesser[pt] & to ? -95 : 100 * bool(threatByLesser[pt] & from); m.value += bonus[pt] * v; } @@ -200,7 +200,7 @@ void MovePicker::score() { template Move MovePicker::select(Pred filter) { - for (; cur < endMoves; ++cur) + for (; cur < endCur; ++cur) if (*cur != ttMove && filter()) return *cur++; @@ -227,10 +227,10 @@ top: case PROBCUT_INIT : case QCAPTURE_INIT : cur = endBadCaptures = moves; - endMoves = generate(pos, cur); + endCur = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, std::numeric_limits::min()); + partial_insertion_sort(cur, endCur, std::numeric_limits::min()); ++stage; goto top; @@ -250,10 +250,10 @@ top: if (!skipQuiets) { cur = endBadQuiets = endBadCaptures; - endMoves = generate(pos, cur); + endCur = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, -3560 * depth); + partial_insertion_sort(cur, endCur, -3560 * depth); } ++stage; @@ -261,13 +261,16 @@ top: case GOOD_QUIET : if (!skipQuiets && select([&]() { - return cur->value > -14000 ? true : (*endBadQuiets++ = *cur, false); + if (cur->value > -14000) + return true; + *endBadQuiets++ = *cur; + return false; })) return *(cur - 1); // Prepare the pointers to loop over the bad captures - cur = moves; - endMoves = endBadCaptures; + cur = moves; + endCur = endBadCaptures; ++stage; [[fallthrough]]; @@ -277,8 +280,8 @@ top: return *(cur - 1); // Prepare the pointers to loop over the bad quiets - cur = endBadCaptures; - endMoves = endBadQuiets; + cur = endBadCaptures; + endCur = endBadQuiets; ++stage; [[fallthrough]]; @@ -290,11 +293,11 @@ top: return Move::none(); case EVASION_INIT : - cur = moves; - endMoves = generate(pos, cur); + cur = moves; + endCur = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, std::numeric_limits::min()); + partial_insertion_sort(cur, endCur, std::numeric_limits::min()); ++stage; [[fallthrough]]; @@ -317,7 +320,7 @@ bool MovePicker::can_move_king_or_pawn() { // SEE negative captures shouldn't be returned in GOOD_CAPTURE stage assert(stage > GOOD_CAPTURE && stage != EVASION_INIT); - for (ExtMove* m = moves; m < endMoves; ++m) + for (ExtMove* m = moves; m < endCur; ++m) { PieceType movedPieceType = type_of(pos.moved_piece(*m)); if ((movedPieceType == PAWN || movedPieceType == KING) && pos.legal(*m)) diff --git a/src/movepick.h b/src/movepick.h index 4218ab5a3..2634fdd78 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -58,7 +58,7 @@ class MovePicker { template void score(); ExtMove* begin() { return cur; } - ExtMove* end() { return endMoves; } + ExtMove* end() { return endCur; } const Position& pos; const ButterflyHistory* mainHistory; @@ -67,7 +67,7 @@ class MovePicker { const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; Move ttMove; - ExtMove * cur, *endMoves, *endBadCaptures, *endBadQuiets; + ExtMove * cur, *endCur, *endBadCaptures, *endBadQuiets; int stage; int threshold; Depth depth; diff --git a/src/search.cpp b/src/search.cpp index ee876f855..673caacb9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -680,8 +680,6 @@ Value Search::Worker::search( && (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER)) && (cutNode == (ttData.value >= beta) || depth > 5)) { - - // If ttMove is quiet, update move sorting heuristics on TT hit if (ttData.move && ttData.value >= beta) { @@ -712,15 +710,15 @@ Value Search::Worker::search( if (!is_valid(ttDataNext.value)) return ttData.value; + if (ttData.value >= beta && -ttDataNext.value >= beta) return ttData.value; + if (ttData.value <= alpha && -ttDataNext.value <= alpha) return ttData.value; } else - { return ttData.value; - } } } @@ -1215,7 +1213,7 @@ moves_loop: // When in check, search starts here if (cutNode) r += 2864 + 966 * !ttData.move; - // Increase reduction if ttMove is a capture but the current move is not a capture + // Increase reduction if ttMove is a capture if (ttCapture) r += 1210 + (depth < 8) * 963; @@ -1253,7 +1251,7 @@ moves_loop: // When in check, search starts here // std::clamp has been replaced by a more robust implementation. Depth d = std::max(1, std::min(newDepth - r / 1024, newDepth + !allNode + (PvNode && !bestMove))) - + ((ss - 1)->isPvNode); + + (ss - 1)->isPvNode; ss->reduction = newDepth - d; value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); @@ -1440,10 +1438,7 @@ moves_loop: // When in check, search starts here update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth, ttData.move, moveCount); if (!PvNode) - { - int bonus = bestMove == ttData.move ? 800 : -879; - ttMoveHistory << bonus; - } + ttMoveHistory << (bestMove == ttData.move ? 800 : -879); } // Bonus for prior quiet countermove that caused the fail low From e6ec4705a868f904fd4237703e4c2d0a7ae1ad6b Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 23 May 2025 11:56:20 -0700 Subject: [PATCH 067/107] Remove deprecated arch from codeql closes https://github.com/official-stockfish/Stockfish/pull/6090 no functional change --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d01ed41fe..a338083cf 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,7 +47,7 @@ jobs: - name: Build working-directory: src - run: make -j build ARCH=x86-64-modern + run: make -j build - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 From fe7b9b14d22bb96a0f6b4dd5aa256e4d02bd84d0 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 24 May 2025 14:39:32 +0300 Subject: [PATCH 068/107] Implement smoother reduction in time management Implement smoother time reduction in time management by replacing a conditional assignment with a continuous sigmoid-based function. The updated logic employs a sigmoid-like function for a more gradual adjustment. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 64448 W: 16838 L: 16492 D: 31118 Ptnml(0-2): 145, 7214, 17207, 7466, 192 https://tests.stockfishchess.org/tests/view/6829dc046ec7634154f99fba Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 407340 W: 104458 L: 103408 D: 199474 Ptnml(0-2): 196, 42281, 117664, 43335, 194 https://tests.stockfishchess.org/tests/view/6829fe1b6ec7634154f9a036 closes https://github.com/official-stockfish/Stockfish/pull/6091 No functional change --- src/search.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 673caacb9..8d2d5720d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -466,7 +466,9 @@ void Search::Worker::iterative_deepening() { fallingEval = std::clamp(fallingEval, 0.5786, 1.6752); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.4857 : 0.7046; + double k = 0.527; + double center = lastBestMoveDepth + 11; + timeReduction = 0.8 + 0.84 / (1.077 + std::exp(-k * (completedDepth - center))); double reduction = (1.4540 + mainThread->previousTimeReduction) / (2.1593 * timeReduction); double bestMoveInstability = 0.9929 + 1.8519 * totBestMoveChanges / threads.size(); @@ -2237,4 +2239,4 @@ bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& po } -} // namespace Stockfish \ No newline at end of file +} // namespace Stockfish From eb27d9420f3c85efc1affe661465224932c541ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=97=D0=B0=D1=80?= =?UTF-8?q?=D0=B8=D0=BF=D0=BE=D0=B2?= <67618307+kokodio@users.noreply.github.com> Date: Sun, 25 May 2025 13:17:35 +0500 Subject: [PATCH 069/107] Make ProbCut search shallower in cutNode Passed STC: https://tests.stockfishchess.org/tests/view/6832d2436ec7634154f9b4fc LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 455072 W: 118162 L: 117237 D: 219673 Ptnml(0-2): 1233, 53409, 117362, 54264, 1268 Passed LTC: https://tests.stockfishchess.org/tests/view/6833323e6ec7634154f9ba17 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 128436 W: 32916 L: 32415 D: 63105 Ptnml(0-2): 50, 13737, 36137, 14250, 44 closes https://github.com/official-stockfish/Stockfish/pull/6093 Bench: 2232447 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 8d2d5720d..8c31fb3a7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -928,7 +928,7 @@ Value Search::Worker::search( assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &thisThread->captureHistory); - Depth probCutDepth = std::max(depth - 4, 0); + Depth probCutDepth = std::max(depth - (4 + cutNode), 0); while ((move = mp.next_move()) != Move::none()) { From 805a2c1672e080106bc9eee00cc7195732c50fa1 Mon Sep 17 00:00:00 2001 From: Daniel Samek Date: Sun, 25 May 2025 20:19:28 +0200 Subject: [PATCH 070/107] Simplify FutilityMoveCount Inlined condition, instead of a function. closes https://github.com/official-stockfish/Stockfish/pull/6096 no functional change --- src/search.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8c31fb3a7..d7abef3d2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -69,10 +69,6 @@ namespace { // so changing them or adding conditions that are similar requires // tests at these types of time controls. -constexpr int futility_move_count(bool improving, Depth depth) { - return (3 + depth * depth) / (2 - improving); -} - int correction_value(const Worker& w, const Position& pos, const Stack* const ss) { const Color us = pos.side_to_move(); const auto m = (ss - 1)->currentMove; @@ -1047,7 +1043,7 @@ moves_loop: // When in check, search starts here if (!rootNode && pos.non_pawn_material(us) && !is_loss(bestValue)) { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold - if (moveCount >= futility_move_count(improving, depth)) + if (moveCount >= (3 + depth * depth) / (2 - improving)) mp.skip_quiet_moves(); // Reduced depth of the next LMR search From 00b1540e01465ca39184fd33f28164c0803a34db Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 17 May 2025 18:01:25 +0200 Subject: [PATCH 071/107] Always Decrease Reduction on TTMove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passed VVLTC w/ LTC Bounds: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 57792 W: 15005 L: 14676 D: 28111 Ptnml(0-2): 2, 5241, 18082, 5568, 3 https://tests.stockfishchess.org/tests/view/682a0e3c6ec7634154f9a07e Passed VVLTC w/ STC Bounds: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 372298 W: 96342 L: 95655 D: 180301 Ptnml(0-2): 37, 34598, 116181, 35307, 26 https://tests.stockfishchess.org/tests/view/682a45b16ec7634154f9a3b3 STC Elo Estimate: Elo: 0.15 ± 1.4 (95%) LOS: 58.3% Total: 59612 W: 15414 L: 15388 D: 28810 Ptnml(0-2): 166, 6959, 15527, 6991, 163 nElo: 0.30 ± 2.8 (95%) PairsRatio: 1.00 https://tests.stockfishchess.org/tests/view/68335d276ec7634154f9c25c closes https://github.com/official-stockfish/Stockfish/pull/6095 bench 2634355 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d7abef3d2..5b16b269d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1223,7 +1223,7 @@ moves_loop: // When in check, search starts here r += (ss->quietMoveStreak - 1) * 50; // For first picked move (ttMove) reduce reduction - else if (move == ttData.move) + if (move == ttData.move) r -= 2006; if (capture) From bebffc5622d8dba4c8db1fbc9ad7bac5b1d9012a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=97=D0=B0=D1=80?= =?UTF-8?q?=D0=B8=D0=BF=D0=BE=D0=B2?= <67618307+kokodio@users.noreply.github.com> Date: Sun, 25 May 2025 17:13:01 +0500 Subject: [PATCH 072/107] Adjust futility pruning thresholds using history Passed STC: https://tests.stockfishchess.org/tests/view/6833095a6ec7634154f9b5b3 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 56896 W: 14946 L: 14604 D: 27346 Ptnml(0-2): 117, 6674, 14561, 6942, 154 Passed LTC: https://tests.stockfishchess.org/tests/view/6833179d6ec7634154f9b5da LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 200742 W: 51660 L: 51012 D: 98070 Ptnml(0-2): 96, 21520, 56473, 22204, 78 Passed Non-regression SMP STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 29080 W: 7591 L: 7373 D: 14116 Ptnml(0-2): 38, 3178, 7881, 3414, 29 https://tests.stockfishchess.org/tests/view/6833689d6ec7634154f9c2ba closes https://github.com/official-stockfish/Stockfish/pull/6092 Bench: 2305697 --- AUTHORS | 1 + src/search.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 8caa22858..be88a8e96 100644 --- a/AUTHORS +++ b/AUTHORS @@ -132,6 +132,7 @@ Kenneth Lee (kennethlee33) Kian E (KJE-98) kinderchocolate Kiran Panditrao (Krgp) +Kirill Zaripov (kokodio) Kojirion Krisztián Peőcz Krystian Kuzniarek (kuzkry) diff --git a/src/search.cpp b/src/search.cpp index 5b16b269d..10e244829 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1095,8 +1095,9 @@ moves_loop: // When in check, search starts here lmrDepth += history / 3388; - Value futilityValue = ss->staticEval + (bestMove ? 46 : 138) + 117 * lmrDepth - + 102 * (ss->staticEval > alpha); + Value baseFutility = (bestMove ? 46 : 138 + std::abs(history / 300)); + Value futilityValue = + ss->staticEval + baseFutility + 117 * lmrDepth + 102 * (ss->staticEval > alpha); // Futility pruning: parent node // (*Scaler): Generally, more frequent futility pruning From e3adfaf8fcb47653d3442f47ea6c3faab161b785 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Thu, 22 May 2025 01:26:02 +0200 Subject: [PATCH 073/107] build & ci: update to NDK r27c API level 29 Update to the latest LTS version NDK r27c (27.2.12479018), the previous NDK are unsupported by Google, see: https://developer.android.com/ndk/downloads A build with NDK r27c and API level < 29 returns this error: "executable's TLS segment is underaligned: alignment is 8 (skew 0), needs to be at least 64 for ARM64 Bionic" Update the API level to 29 to use the native ELF LTS and avoid the error: https://android.googlesource.com/platform/bionic/+/HEAD/docs/elf-tls.md https://android.googlesource.com/platform/bionic/+/HEAD/android-changes-for-ndk-developers.md#elf-tls-available-for-api-level-29 A dynamic link build of Stockfish uses these libraries: ldd stockfish-android-armv8-dynamic-api35 libm.so => /system/lib64/libm.so libdl.so => /system/lib64/libdl.so libc.so => /system/lib64/libc.so ld-android.so => /system/lib64/ld-android.so ld-android.so : the dynamic linker used by Android (on Linux is named ld-linux.so), responsible for loading and linking shared libraries into an executable at runtime. libdl.so : interface/library layer that provides function for dynamic loading, relies on the underlying functionality provided by the dynamic linker libm.so : math library for Android libc.so : standard C library for Android References: Doc for native (C/C++) API https://developer.android.com/ndk/guides/stable_apis C libraries (libc, libm, libdl): https://developer.android.com/ndk/guides/stable_apis#c_library Bionic changes with API levels: https://android.googlesource.com/platform/bionic/+/HEAD/docs/status.md NDK r27c build system: https://android.googlesource.com/platform/ndk/+/ndk-r27-release/docs/BuildSystemMaintainers.md CI: Update to NDK r27c (27.2.12479018), the default version in GitHub runner, to switch to a recent clang 18. A PGO build requires static linking, because the NDK doesn't ship the Android loaders (linker/linker64), see: https://groups.google.com/g/android-ndk/c/3Ep6zD3xxSY The API level should not be an issue when distributing a static build, use the API 29, the oldest one not affected by the LTS alignement issue. closes https://github.com/official-stockfish/Stockfish/pull/6081 No functional change --- .github/ci/arm_matrix.json | 12 ++++++------ .github/workflows/arm_compilation.yml | 2 +- .github/workflows/tests.yml | 6 +++--- src/Makefile | 14 ++++++-------- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/ci/arm_matrix.json b/.github/ci/arm_matrix.json index 70f2efaa2..b53fe03af 100644 --- a/.github/ci/arm_matrix.json +++ b/.github/ci/arm_matrix.json @@ -4,7 +4,7 @@ "name": "Android NDK aarch64", "os": "ubuntu-22.04", "simple_name": "android", - "compiler": "aarch64-linux-android21-clang++", + "compiler": "aarch64-linux-android29-clang++", "emu": "qemu-aarch64", "comp": "ndk", "shell": "bash", @@ -14,7 +14,7 @@ "name": "Android NDK arm", "os": "ubuntu-22.04", "simple_name": "android", - "compiler": "armv7a-linux-androideabi21-clang++", + "compiler": "armv7a-linux-androideabi29-clang++", "emu": "qemu-arm", "comp": "ndk", "shell": "bash", @@ -26,25 +26,25 @@ { "binaries": "armv8-dotprod", "config": { - "compiler": "armv7a-linux-androideabi21-clang++" + "compiler": "armv7a-linux-androideabi29-clang++" } }, { "binaries": "armv8", "config": { - "compiler": "armv7a-linux-androideabi21-clang++" + "compiler": "armv7a-linux-androideabi29-clang++" } }, { "binaries": "armv7", "config": { - "compiler": "aarch64-linux-android21-clang++" + "compiler": "aarch64-linux-android29-clang++" } }, { "binaries": "armv7-neon", "config": { - "compiler": "aarch64-linux-android21-clang++" + "compiler": "aarch64-linux-android29-clang++" } } ] diff --git a/.github/workflows/arm_compilation.yml b/.github/workflows/arm_compilation.yml index 5bf2a93e5..781bd8070 100644 --- a/.github/workflows/arm_compilation.yml +++ b/.github/workflows/arm_compilation.yml @@ -38,7 +38,7 @@ jobs: if: runner.os == 'Linux' run: | if [ $COMP == ndk ]; then - NDKV="21.4.7075529" + NDKV="27.2.12479018" ANDROID_ROOT=/usr/local/lib/android ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6d35a183d..b269bd742 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,13 +29,13 @@ jobs: shell: bash - name: Android NDK aarch64 os: ubuntu-22.04 - compiler: aarch64-linux-android21-clang++ + compiler: aarch64-linux-android29-clang++ comp: ndk run_armv8_tests: true shell: bash - name: Android NDK arm os: ubuntu-22.04 - compiler: armv7a-linux-androideabi21-clang++ + compiler: armv7a-linux-androideabi29-clang++ comp: ndk run_armv7_tests: true shell: bash @@ -126,7 +126,7 @@ jobs: if: runner.os == 'Linux' run: | if [ $COMP == ndk ]; then - NDKV="21.4.7075529" + NDKV="27.2.12479018" ANDROID_ROOT=/usr/local/lib/android ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager diff --git a/src/Makefile b/src/Makefile index 9ea3e01b7..87bf4d697 100644 --- a/src/Makefile +++ b/src/Makefile @@ -533,14 +533,12 @@ ifeq ($(KERNEL),Darwin) XCRUN = xcrun endif -# To cross-compile for Android, NDK version r21 or later is recommended. -# In earlier NDK versions, you'll need to pass -fno-addrsig if using GNU binutils. -# Currently we don't know how to make PGO builds with the NDK yet. +# To cross-compile for Android, use NDK version r27c or later. ifeq ($(COMP),ndk) - CXXFLAGS += -stdlib=libc++ -fPIE + CXXFLAGS += -stdlib=libc++ comp=clang ifeq ($(arch),armv7) - CXX=armv7a-linux-androideabi16-clang++ + CXX=armv7a-linux-androideabi29-clang++ CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon ifneq ($(shell which arm-linux-androideabi-strip 2>/dev/null),) STRIP=arm-linux-androideabi-strip @@ -549,7 +547,7 @@ ifeq ($(COMP),ndk) endif endif ifeq ($(arch),armv8) - CXX=aarch64-linux-android21-clang++ + CXX=aarch64-linux-android29-clang++ ifneq ($(shell which aarch64-linux-android-strip 2>/dev/null),) STRIP=aarch64-linux-android-strip else @@ -557,14 +555,14 @@ ifeq ($(COMP),ndk) endif endif ifeq ($(arch),x86_64) - CXX=x86_64-linux-android21-clang++ + CXX=x86_64-linux-android29-clang++ ifneq ($(shell which x86_64-linux-android-strip 2>/dev/null),) STRIP=x86_64-linux-android-strip else STRIP=llvm-strip endif endif - LDFLAGS += -static-libstdc++ -pie -lm -latomic + LDFLAGS += -static-libstdc++ endif ### Allow overwriting CXX from command line From 73c55e894959b2dcd40c179ade6e1eafc9d952ea Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 25 May 2025 18:33:04 +0300 Subject: [PATCH 074/107] Simplify Double Margin Formula Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 267296 W: 69214 L: 69248 D: 128834 Ptnml(0-2): 760, 31511, 69141, 31475, 761 https://tests.stockfishchess.org/tests/view/682f5d9a6ec7634154f9b01e Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 67872 W: 17460 L: 17289 D: 33123 Ptnml(0-2): 25, 7238, 19243, 7401, 29 https://tests.stockfishchess.org/tests/view/6833074b6ec7634154f9b5ae Passed VLTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 118000 W: 30337 L: 30222 D: 57441 Ptnml(0-2): 15, 11783, 35289, 11898, 15 https://tests.stockfishchess.org/tests/view/683336c56ec7634154f9ba46 closes https://github.com/official-stockfish/Stockfish/pull/6097 Bench: 2312696 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 10e244829..14f235b5c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1147,7 +1147,7 @@ moves_loop: // When in check, search starts here int corrValAdj2 = std::abs(correctionValue) / 249757; int doubleMargin = -4 + 244 * PvNode - 206 * !ttCapture - corrValAdj1 - 997 * ttMoveHistory / 131072 - - (ss->ply * 2 > thisThread->rootDepth * 3) * 47; + - (ss->ply > thisThread->rootDepth) * 47; int tripleMargin = 84 + 269 * PvNode - 253 * !ttCapture + 91 * ss->ttPv - corrValAdj2 - (ss->ply * 2 > thisThread->rootDepth * 3) * 54; From 9b79b75c9b728cf52b00793c4283d630a20e74b8 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 27 Apr 2025 19:17:52 +0300 Subject: [PATCH 075/107] Enforce minimum compiler versions gcc 9.3 clang 10 using unsupported compiler versions will generate an error, older version might miscompile SF CI: improves output on failed bench output closes https://github.com/official-stockfish/Stockfish/pull/6032 No functional change --- src/types.h | 6 ++++++ tests/signature.sh | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/types.h b/src/types.h index 6c7975807..5d7672153 100644 --- a/src/types.h +++ b/src/types.h @@ -57,11 +57,17 @@ // _WIN32 Building on Windows (any) // _WIN64 Building on Windows 64 bit +// Enforce minimum GCC version #if defined(__GNUC__) && !defined(__clang__) \ && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ < 3)) #error "Stockfish requires GCC 9.3 or later for correct compilation" #endif +// Enforce minimum Clang version +#if defined(__clang__) && (__clang_major__ < 10) + #error "Stockfish requires Clang 10.0 or later for correct compilation" +#endif + #define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast(ptr) % alignment == 0) #if defined(_WIN64) && defined(_MSC_VER) // No Makefile used diff --git a/tests/signature.sh b/tests/signature.sh index 06bd1892e..0f6dd7585 100755 --- a/tests/signature.sh +++ b/tests/signature.sh @@ -2,16 +2,26 @@ # obtain and optionally verify Bench / signature # if no reference is given, the output is deliberately limited to just the signature +STDOUT_FILE=$(mktemp) +STDERR_FILE=$(mktemp) + error() { echo "running bench for signature failed on line $1" + echo "===== STDOUT =====" + cat "$STDOUT_FILE" + echo "===== STDERR =====" + cat "$STDERR_FILE" + rm -f "$STDOUT_FILE" "$STDERR_FILE" exit 1 } trap 'error ${LINENO}' ERR # obtain +eval "$WINE_PATH ./stockfish bench" > "$STDOUT_FILE" 2> "$STDERR_FILE" || error ${LINENO} +signature=$(grep "Nodes searched : " "$STDERR_FILE" | awk '{print $4}') -signature=`eval "$WINE_PATH ./stockfish bench 2>&1" | grep "Nodes searched : " | awk '{print $4}'` +rm -f "$STDOUT_FILE" "$STDERR_FILE" if [ $# -gt 0 ]; then # compare to given reference @@ -28,4 +38,4 @@ if [ $# -gt 0 ]; then else # just report signature echo $signature -fi +fi \ No newline at end of file From dfa176fc7ee795b69fa72ea1322486a8d8b0647a Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 25 May 2025 12:58:01 -0700 Subject: [PATCH 076/107] Small tt verify simplification Also fix probcut comment Passed non-regression STC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 69728 W: 18080 L: 17909 D: 33739 Ptnml(0-2): 161, 7157, 20044, 7354, 148 https://tests.stockfishchess.org/tests/view/68324b116ec7634154f9b478 closes https://github.com/official-stockfish/Stockfish/pull/6094 No functional change --- src/search.cpp | 14 +++----------- src/types.h | 8 ++++---- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 14f235b5c..c6f61ec90 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -701,18 +701,12 @@ Value Search::Worker::search( do_move(pos, ttData.move, st); Key nextPosKey = pos.key(); auto [ttHitNext, ttDataNext, ttWriterNext] = tt.probe(nextPosKey); - ttDataNext.value = - ttHitNext ? value_from_tt(ttDataNext.value, ss->ply + 1, pos.rule50_count()) - : VALUE_NONE; undo_move(pos, ttData.move); + // Check that the ttValue after the tt move would also trigger a cutoff if (!is_valid(ttDataNext.value)) return ttData.value; - - if (ttData.value >= beta && -ttDataNext.value >= beta) - return ttData.value; - - if (ttData.value <= alpha && -ttDataNext.value <= alpha) + if ((ttData.value >= beta) == (-ttDataNext.value >= beta)) return ttData.value; } else @@ -916,9 +910,7 @@ Value Search::Worker::search( if (depth >= 3 && !is_decisive(beta) // If value from transposition table is lower than probCutBeta, don't attempt - // probCut there and in further interactions with transposition table cutoff - // depth is set to depth - 3 because probCut search has depth set to depth - 4 - // but we also do a move before it. So effective depth is equal to depth - 3. + // probCut there && !(is_valid(ttData.value) && ttData.value < probCutBeta)) { assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); diff --git a/src/types.h b/src/types.h index 5d7672153..d40e1e292 100644 --- a/src/types.h +++ b/src/types.h @@ -63,10 +63,10 @@ #error "Stockfish requires GCC 9.3 or later for correct compilation" #endif -// Enforce minimum Clang version -#if defined(__clang__) && (__clang_major__ < 10) - #error "Stockfish requires Clang 10.0 or later for correct compilation" -#endif + // Enforce minimum Clang version + #if defined(__clang__) && (__clang_major__ < 10) + #error "Stockfish requires Clang 10.0 or later for correct compilation" + #endif #define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast(ptr) % alignment == 0) From 9fd40b9ea887bbec989b0beee248d4ebf70705af Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 25 May 2025 10:34:52 -0700 Subject: [PATCH 077/107] Simplify tt depth in stat eval history adjustment Passed simplification STC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 102208 W: 26498 L: 26349 D: 49361 Ptnml(0-2): 284, 12095, 26166, 12306, 253 https://tests.stockfishchess.org/tests/view/683354c76ec7634154f9be88 Passed simplification LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 133422 W: 34050 L: 33945 D: 65427 Ptnml(0-2): 56, 14473, 37559, 14556, 67 https://tests.stockfishchess.org/tests/view/683363626ec7634154f9c298 closes https://github.com/official-stockfish/Stockfish/pull/6099 Bench: 2652411 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c6f61ec90..670d343e8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -807,8 +807,7 @@ Value Search::Worker::search( } // Use static evaluation difference to improve quiet move ordering - if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture - && (ttData.depth - 2) <= depth) + if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture && ttData.depth <= 2) { int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1858, 1492) + 661; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1057 / 1024; From dc85c5a4c983d3a957ca7c6affb709d966360412 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 25 May 2025 19:12:43 -0700 Subject: [PATCH 078/107] Remove nnz lookup table load optimization Passed Non-regression STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 63296 W: 16491 L: 16311 D: 30494 Ptnml(0-2): 129, 6624, 17972, 6784, 139 https://tests.stockfishchess.org/tests/view/6833ce486ec7634154f9cb22 Passed 2nd Non-regression STC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 369568 W: 95314 L: 95451 D: 178803 Ptnml(0-2): 897, 40231, 102601, 40222, 833 https://tests.stockfishchess.org/tests/view/68355c956ec7634154f9ce07 closes https://github.com/official-stockfish/Stockfish/pull/6100 no functional change --- .../layers/affine_transform_sparse_input.h | 4 ---- src/nnue/simd.h | 18 +++--------------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 7c74d5e64..51f86fd6e 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -49,11 +49,7 @@ constexpr int constexpr_lsb(uint64_t bb) { alignas(CacheLineSize) static constexpr struct OffsetIndices { - #if (USE_SSE41) - std::uint8_t offset_indices[256][8]; - #else std::uint16_t offset_indices[256][8]; - #endif constexpr OffsetIndices() : offset_indices() { diff --git a/src/nnue/simd.h b/src/nnue/simd.h index 560223000..f37eeb930 100644 --- a/src/nnue/simd.h +++ b/src/nnue/simd.h @@ -74,11 +74,7 @@ using vec_uint_t = __m512i; #define vec128_zero _mm_setzero_si128() #define vec128_set_16(a) _mm_set1_epi16(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_load(a) _mm_load_si128(a) #define vec128_storeu(a, b) _mm_storeu_si128(a, b) #define vec128_add(a, b) _mm_add_epi16(a, b) #define NumRegistersSIMD 16 @@ -119,11 +115,7 @@ using vec_uint_t = __m256i; #define vec128_zero _mm_setzero_si128() #define vec128_set_16(a) _mm_set1_epi16(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_load(a) _mm_load_si128(a) #define vec128_storeu(a, b) _mm_storeu_si128(a, b) #define vec128_add(a, b) _mm_add_epi16(a, b) @@ -159,11 +151,7 @@ using vec_uint_t = __m128i; #define vec128_zero _mm_setzero_si128() #define vec128_set_16(a) _mm_set1_epi16(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_load(a) _mm_load_si128(a) #define vec128_storeu(a, b) _mm_storeu_si128(a, b) #define vec128_add(a, b) _mm_add_epi16(a, b) From d27298d7dc0a6222ce21b5ff3b9f16fe72c108af Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 26 May 2025 18:26:31 -0700 Subject: [PATCH 079/107] Remove unused threatenedPieces threatenedPieces is no longer used since #6023 Also can_move_king_or_pawn() can be const. Also remove a couple of redundant declarations. closes https://github.com/official-stockfish/Stockfish/pull/6101 No functional change --- src/movepick.cpp | 11 +++-------- src/movepick.h | 2 +- src/nnue/nnue_common.h | 1 - src/timeman.cpp | 2 -- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index faef753be..d2764fa84 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -128,18 +128,13 @@ void MovePicker::score() { Color us = pos.side_to_move(); - [[maybe_unused]] Bitboard threatenedPieces, threatByLesser[QUEEN + 1]; + [[maybe_unused]] Bitboard threatByLesser[QUEEN + 1]; if constexpr (Type == QUIETS) { threatByLesser[KNIGHT] = threatByLesser[BISHOP] = pos.attacks_by(~us); threatByLesser[ROOK] = pos.attacks_by(~us) | pos.attacks_by(~us) | threatByLesser[KNIGHT]; threatByLesser[QUEEN] = pos.attacks_by(~us) | threatByLesser[ROOK]; - - // Pieces threatened by pieces of lesser material value - threatenedPieces = (pos.pieces(us, QUEEN) & threatByLesser[QUEEN]) - | (pos.pieces(us, ROOK) & threatByLesser[ROOK]) - | (pos.pieces(us, KNIGHT, BISHOP) & threatByLesser[KNIGHT]); } for (auto& m : *this) @@ -316,11 +311,11 @@ top: void MovePicker::skip_quiet_moves() { skipQuiets = true; } // this function must be called after all quiet moves and captures have been generated -bool MovePicker::can_move_king_or_pawn() { +bool MovePicker::can_move_king_or_pawn() const { // SEE negative captures shouldn't be returned in GOOD_CAPTURE stage assert(stage > GOOD_CAPTURE && stage != EVASION_INIT); - for (ExtMove* m = moves; m < endCur; ++m) + for (const ExtMove* m = moves; m < endCur; ++m) { PieceType movedPieceType = type_of(pos.moved_piece(*m)); if ((movedPieceType == PAWN || movedPieceType == KING) && pos.legal(*m)) diff --git a/src/movepick.h b/src/movepick.h index 2634fdd78..b6784fb78 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -50,7 +50,7 @@ class MovePicker { MovePicker(const Position&, Move, int, const CapturePieceToHistory*); Move next_move(); void skip_quiet_moves(); - bool can_move_king_or_pawn(); + bool can_move_king_or_pawn() const; private: template diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 35cc8a5fe..550bcaad4 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -81,7 +81,6 @@ constexpr std::size_t MaxSimdWidth = 32; // Type of input feature after conversion using TransformedFeatureType = std::uint8_t; -using IndexType = std::uint32_t; // Round n up to be a multiple of base template diff --git a/src/timeman.cpp b/src/timeman.cpp index f0894a262..29ebffcaa 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -28,8 +28,6 @@ namespace Stockfish { -enum Color : int8_t; - TimePoint TimeManagement::optimum() const { return optimumTime; } TimePoint TimeManagement::maximum() const { return maximumTime; } From 29b0c07ac88d32cb879248eccd31cb6a2cac93c5 Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 27 May 2025 18:27:48 -0700 Subject: [PATCH 080/107] Simplify Position::pieces() closes https://github.com/official-stockfish/Stockfish/pull/6104 No functional change --- src/position.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/position.h b/src/position.h index 724165b00..cf6b1c472 100644 --- a/src/position.h +++ b/src/position.h @@ -86,9 +86,9 @@ class Position { std::string fen() const; // Position representation - Bitboard pieces(PieceType pt = ALL_PIECES) const; + Bitboard pieces() const; // All pieces template - Bitboard pieces(PieceType pt, PieceTypes... pts) const; + Bitboard pieces(PieceTypes... pts) const; Bitboard pieces(Color c) const; template Bitboard pieces(Color c, PieceTypes... pts) const; @@ -214,11 +214,11 @@ inline bool Position::empty(Square s) const { return piece_on(s) == NO_PIECE; } inline Piece Position::moved_piece(Move m) const { return piece_on(m.from_sq()); } -inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; } +inline Bitboard Position::pieces() const { return byTypeBB[ALL_PIECES]; } template -inline Bitboard Position::pieces(PieceType pt, PieceTypes... pts) const { - return pieces(pt) | pieces(pts...); +inline Bitboard Position::pieces(PieceTypes... pts) const { + return (byTypeBB[pts] | ...); } inline Bitboard Position::pieces(Color c) const { return byColorBB[c]; } From d0212906bde4bea7f39357c1729f2492c6531ed3 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Mon, 26 May 2025 00:28:18 -0700 Subject: [PATCH 081/107] Simplify stat eval history adjustment further closes https://github.com/official-stockfish/Stockfish/pull/6106 bench 2074807 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 670d343e8..a58ac3e87 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -807,7 +807,7 @@ Value Search::Worker::search( } // Use static evaluation difference to improve quiet move ordering - if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture && ttData.depth <= 2) + if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture && !ttHit) { int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1858, 1492) + 661; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1057 / 1024; From 9debc540e5455e25ca5091045d81a033429bbaaa Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Fri, 30 May 2025 08:48:03 +0200 Subject: [PATCH 082/107] Fix clang-format version in CONTRIBUTING.md closes https://github.com/official-stockfish/Stockfish/pull/6107 No functional change --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b6fbce0d..07fe9f542 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,7 +59,7 @@ discussion._ Changes to Stockfish C++ code should respect our coding style defined by [.clang-format](.clang-format). You can format your changes by running -`make format`. This requires clang-format version 18 to be installed on your system. +`make format`. This requires clang-format version 20 to be installed on your system. ## Navigate From 5695486db99de3883188331eb1c7565e68d092cf Mon Sep 17 00:00:00 2001 From: Carlos Esparza Date: Fri, 30 May 2025 13:36:38 -0700 Subject: [PATCH 083/107] Fix outdated comment closes https://github.com/official-stockfish/Stockfish/pull/6108 No functional change --- src/search.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a58ac3e87..93fe7e588 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -778,10 +778,7 @@ Value Search::Worker::search( goto moves_loop; } else if (excludedMove) - { - // Providing the hint that this node's accumulator will be used often unadjustedStaticEval = eval = ss->staticEval; - } else if (ss->ttHit) { // Never assume anything about values stored in TT From 8da3c2155a86a78d24aaf454264cf1bc93cb2d3a Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Sat, 31 May 2025 15:43:19 +0200 Subject: [PATCH 084/107] Simplify NMP eval in qsearch Passed non-regression STC: https://tests.stockfishchess.org/tests/view/6834e9436ec7634154f9cd6e LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 24864 W: 6626 L: 6394 D: 11844 Ptnml(0-2): 62, 2806, 6477, 3012, 75 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/683598fd6ec7634154f9ce82 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 200148 W: 51461 L: 51424 D: 97263 Ptnml(0-2): 92, 21672, 56503, 21721, 86 closes https://github.com/official-stockfish/Stockfish/pull/6109 Bench: 2316591 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 93fe7e588..197da7a4f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1589,9 +1589,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) } else { - // In case of null move search, use previous static eval with opposite sign - unadjustedStaticEval = - (ss - 1)->currentMove != Move::null() ? evaluate(pos) : -(ss - 1)->staticEval; + unadjustedStaticEval = evaluate(pos); + ss->staticEval = bestValue = to_corrected_static_eval(unadjustedStaticEval, correctionValue); } From ddefd6eb6b6213eabbfbbcf4d1535cb30f620826 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Mon, 26 May 2025 21:29:43 -0700 Subject: [PATCH 085/107] Simplify away check term in statscore Passed simplification STC LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 61696 W: 16031 L: 15841 D: 29824 Ptnml(0-2): 151, 7160, 16046, 7330, 161 https://tests.stockfishchess.org/tests/view/68353fcc6ec7634154f9cdd5 Passed simplification LTC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 237990 W: 60994 L: 60995 D: 116001 Ptnml(0-2): 95, 25964, 66903, 25913, 120 https://tests.stockfishchess.org/tests/view/683642256ec7634154f9cf5e closes https://github.com/official-stockfish/Stockfish/pull/6110 Bench: 2521003 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 197da7a4f..8b8c3d6d0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1223,7 +1223,7 @@ moves_loop: // When in check, search starts here else ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] + 1000 * ss->inCheck - 3206; + + (*contHist[1])[movedPiece][move.to_sq()] - 3206; // Decrease/increase reduction for moves with a good/bad history r -= ss->statScore * 826 / 8192; From 70ff5e31635384504f6f5cce9c0f089d60103922 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Mon, 26 May 2025 23:47:35 -0700 Subject: [PATCH 086/107] Simplify away cutoff term in prior countermove bonus Passed simplification STC LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 61120 W: 16010 L: 15819 D: 29291 Ptnml(0-2): 150, 7105, 15869, 7276, 160 https://tests.stockfishchess.org/tests/view/683560226ec7634154f9ce0f Passed simplification LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 225090 W: 57555 L: 57543 D: 109992 Ptnml(0-2): 104, 24367, 63603, 24355, 116 https://tests.stockfishchess.org/tests/view/6836420c6ec7634154f9cf5c closes https://github.com/official-stockfish/Stockfish/pull/6111 Bench: 2472910 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8b8c3d6d0..f7390bc2c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1431,11 +1431,10 @@ moves_loop: // When in check, search starts here // Bonus for prior quiet countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonusScale = -302; + int bonusScale = -220; bonusScale += std::min(-(ss - 1)->statScore / 103, 323); bonusScale += std::min(73 * depth, 531); bonusScale += 174 * ((ss - 1)->moveCount > 8); - bonusScale += 90 * (ss->cutoffCnt <= 3); bonusScale += 144 * (!ss->inCheck && bestValue <= ss->staticEval - 104); bonusScale += 128 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 82); From 3747a1993722bfb9d5e7c784b2225e5aa742276f Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 25 May 2025 12:32:32 -0700 Subject: [PATCH 087/107] Simplify away depth condition in IIR Passed simplification STC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 359520 W: 92714 L: 92849 D: 173957 Ptnml(0-2): 977, 42640, 92614, 42599, 930 https://tests.stockfishchess.org/tests/view/6833705d6ec7634154f9c302 Passed simplification LTC LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 201756 W: 51544 L: 51507 D: 98705 Ptnml(0-2): 89, 21965, 56728, 22012, 84 https://tests.stockfishchess.org/tests/view/68338e386ec7634154f9c790 Passed simplification VLTC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 48558 W: 12675 L: 12492 D: 23391 Ptnml(0-2): 9, 4779, 14516, 4970, 5 https://tests.stockfishchess.org/tests/view/6838e0b26ec7634154f9d25b closes https://github.com/official-stockfish/Stockfish/pull/6112 Bench: 2302583 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f7390bc2c..b686808f3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -896,7 +896,7 @@ Value Search::Worker::search( // Step 10. Internal iterative reductions // For PV nodes without a ttMove as well as for deep enough cutNodes, we decrease depth. // (*Scaler) Especially if they make IIR less aggressive. - if (!allNode && depth >= (PvNode ? 5 : 7) && !ttData.move) + if (!allNode && depth >= 6 && !ttData.move) depth--; // Step 11. ProbCut From c9af7674bc120b4f75153e00c40315ff0f54b86c Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 30 May 2025 00:03:23 -0700 Subject: [PATCH 088/107] Introduce Secondary TT Aging When a high-depth TT entry fail to produce a cutoff, decrease the stored depth by 1. This is intended to help cases such as #5023 (https://github.com/official-stockfish/Stockfish/issues/5023#issuecomment-2814209391), where entries with extremely high depths prevent TT cutoffs, contributing to search explosions. Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 128800 W: 33502 L: 33053 D: 62245 Ptnml(0-2): 331, 15084, 33149, 15477, 359 https://tests.stockfishchess.org/tests/view/683958e56ec7634154f9d2a9 Passed LTC: LLR: 2.97 (-2.94,2.94) <0.50,2.50> Total: 63288 W: 16376 L: 16005 D: 30907 Ptnml(0-2): 26, 6712, 17798, 7081, 27 https://tests.stockfishchess.org/tests/view/683aa4026ec7634154f9d469 closes https://github.com/official-stockfish/Stockfish/pull/6113 Bench: 2144705 --- src/tt.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tt.cpp b/src/tt.cpp index d7f7dbdef..953348987 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -110,6 +110,8 @@ void TTEntry::save( value16 = int16_t(v); eval16 = int16_t(ev); } + else if (depth8 + DEPTH_ENTRY_OFFSET >= 5 && Bound(genBound8 & 0x3) != BOUND_EXACT) + depth8--; } From 259bdaaa9faf20cdc1c56faf97504f7dd2f62032 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 31 May 2025 15:15:29 -0700 Subject: [PATCH 089/107] Remove an unnecessary bound check When failing high, it is always true that `alpha < beta` and `beta <= bestValue`. Therefore if alpha and bestValue is not in decisive range, it is guaranteed that beta is not. closes https://github.com/official-stockfish/Stockfish/pull/6115 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 b686808f3..e3cb1cdb8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1412,7 +1412,7 @@ moves_loop: // When in check, search starts here assert(moveCount || !ss->inCheck || excludedMove || !MoveList(pos).size()); // Adjust best value for fail high cases - if (bestValue >= beta && !is_decisive(bestValue) && !is_decisive(beta) && !is_decisive(alpha)) + if (bestValue >= beta && !is_decisive(bestValue) && !is_decisive(alpha)) bestValue = (bestValue * depth + beta) / (depth + 1); if (!moveCount) From 254b6d5e85d50b057f47a99b2515606a2626813c Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sat, 31 May 2025 11:04:42 -0700 Subject: [PATCH 090/107] Simplify corrections in extension margins Passed simplification STC LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 96192 W: 25002 L: 24852 D: 46338 Ptnml(0-2): 242, 10868, 25716, 11038, 232 https://tests.stockfishchess.org/tests/view/683b44cb6ec7634154f9d6ac Passed simplification LTC LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 83334 W: 21473 L: 21317 D: 40544 Ptnml(0-2): 37, 8877, 23674, 9051, 28 https://tests.stockfishchess.org/tests/view/683b79786ec7634154f9d75a closes https://github.com/official-stockfish/Stockfish/pull/6117 Bench: 2294814 --- src/search.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e3cb1cdb8..25c173f23 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1131,13 +1131,12 @@ moves_loop: // When in check, search starts here if (value < singularBeta) { - int corrValAdj1 = std::abs(correctionValue) / 248400; - int corrValAdj2 = std::abs(correctionValue) / 249757; - int doubleMargin = -4 + 244 * PvNode - 206 * !ttCapture - corrValAdj1 + int corrValAdj = std::abs(correctionValue) / 248400; + int doubleMargin = -4 + 244 * PvNode - 206 * !ttCapture - corrValAdj - 997 * ttMoveHistory / 131072 - (ss->ply > thisThread->rootDepth) * 47; - int tripleMargin = 84 + 269 * PvNode - 253 * !ttCapture + 91 * ss->ttPv - - corrValAdj2 - (ss->ply * 2 > thisThread->rootDepth * 3) * 54; + int tripleMargin = 84 + 269 * PvNode - 253 * !ttCapture + 91 * ss->ttPv - corrValAdj + - (ss->ply * 2 > thisThread->rootDepth * 3) * 54; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); From 9ac756695e215e6c343d294fb8ade3eec2a41127 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 1 Jun 2025 13:27:52 -0700 Subject: [PATCH 091/107] reduce depth by 5 in probcut Passed simplification STC LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 63328 W: 16402 L: 16213 D: 30713 Ptnml(0-2): 174, 7378, 16340, 7629, 143 https://tests.stockfishchess.org/tests/view/6833530e6ec7634154f9be7f Passed simplification LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 69936 W: 17795 L: 17625 D: 34516 Ptnml(0-2): 29, 7631, 19474, 7809, 25 https://tests.stockfishchess.org/tests/view/68335e386ec7634154f9c266 closes https://github.com/official-stockfish/Stockfish/pull/6120 Bench: 2307268 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 25c173f23..ddc27156e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -912,7 +912,7 @@ Value Search::Worker::search( assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &thisThread->captureHistory); - Depth probCutDepth = std::max(depth - (4 + cutNode), 0); + Depth probCutDepth = std::max(depth - 5, 0); while ((move = mp.next_move()) != Move::none()) { From 5337edfdb6c9593e224be58225907682903db1a9 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 31 May 2025 18:24:19 -0700 Subject: [PATCH 092/107] remove non-functional else since we break out of the loop in the other branch closes https://github.com/official-stockfish/Stockfish/pull/6116 no functional change --- src/search.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ddc27156e..9f44defbe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1380,15 +1380,13 @@ moves_loop: // When in check, search starts here assert(value >= beta); // Fail high break; } - else - { - // Reduce other moves if we have found at least one score improvement - if (depth > 2 && depth < 16 && !is_decisive(value)) - depth -= 2; - assert(depth > 0); - alpha = value; // Update alpha! Always alpha < beta - } + // Reduce other moves if we have found at least one score improvement + if (depth > 2 && depth < 16 && !is_decisive(value)) + depth -= 2; + + assert(depth > 0); + alpha = value; // Update alpha! Always alpha < beta } } From 15555e8f4ab0ee0a020ab8bd9e9ae56214ca00c1 Mon Sep 17 00:00:00 2001 From: disservin Date: Sun, 29 Jun 2025 12:33:20 +0200 Subject: [PATCH 093/107] Disable linux gcc riscv64 (#6145) Temporarily disable it, until we figure out the toolchain issues which are causing the crashes. closes https://github.com/official-stockfish/Stockfish/pull/6145 No functional change --- .github/workflows/tests.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b269bd742..95ca12092 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,14 +39,15 @@ jobs: comp: ndk run_armv7_tests: true shell: bash - - name: Linux GCC riscv64 - os: ubuntu-22.04 - compiler: g++ - comp: gcc - run_riscv64_tests: true - base_image: "riscv64/alpine:edge" - platform: linux/riscv64 - shell: bash + # Currently segfaults in the CI unrelated to a Stockfish change. + # - name: Linux GCC riscv64 + # os: ubuntu-22.04 + # compiler: g++ + # comp: gcc + # run_riscv64_tests: true + # base_image: "riscv64/alpine:edge" + # platform: linux/riscv64 + # shell: bash - name: Linux GCC ppc64 os: ubuntu-22.04 compiler: g++ From 34b75f1575698759ab180da369be5e65364a9d1e Mon Sep 17 00:00:00 2001 From: pb00067 Date: Tue, 3 Jun 2025 13:52:17 +0200 Subject: [PATCH 094/107] Restore integrity of MovePicker::can_move_king_or_pawn PR6005 broken by PR6071 passed STC non regression https://tests.stockfishchess.org/tests/view/6839791f6ec7634154f9d312 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 31776 W: 8353 L: 8130 D: 15293 Ptnml(0-2): 74, 3566, 8382, 3795, 71 passed LTC non-regression https://tests.stockfishchess.org/tests/view/6839c87a6ec7634154f9d367 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 120756 W: 31015 L: 30899 D: 58842 Ptnml(0-2): 50, 12732, 34703, 12838, 55 closes https://github.com/official-stockfish/Stockfish/pull/6119 Bench: 1945300 --- src/movepick.cpp | 26 +++++++++++--------------- src/movepick.h | 2 +- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index d2764fa84..3e7c10160 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -56,6 +56,7 @@ enum Stages { QCAPTURE }; + // Sort moves in descending order up to and including a given limit. // The order of moves smaller than the limit is left unspecified. void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { @@ -207,6 +208,7 @@ Move MovePicker::select(Pred filter) { // picking the move with the highest score from a list of generated moves. Move MovePicker::next_move() { + constexpr int goodQuietThreshold = -14000; top: switch (stage) { @@ -222,7 +224,7 @@ top: case PROBCUT_INIT : case QCAPTURE_INIT : cur = endBadCaptures = moves; - endCur = generate(pos, cur); + endCur = endCaptures = generate(pos, cur); score(); partial_insertion_sort(cur, endCur, std::numeric_limits::min()); @@ -244,8 +246,7 @@ top: case QUIET_INIT : if (!skipQuiets) { - cur = endBadQuiets = endBadCaptures; - endCur = generate(pos, cur); + endCur = endGenerated = generate(pos, cur); score(); partial_insertion_sort(cur, endCur, -3560 * depth); @@ -255,12 +256,7 @@ top: [[fallthrough]]; case GOOD_QUIET : - if (!skipQuiets && select([&]() { - if (cur->value > -14000) - return true; - *endBadQuiets++ = *cur; - return false; - })) + if (!skipQuiets && select([&]() { return cur->value > goodQuietThreshold; })) return *(cur - 1); // Prepare the pointers to loop over the bad captures @@ -274,22 +270,22 @@ top: if (select([]() { return true; })) return *(cur - 1); - // Prepare the pointers to loop over the bad quiets - cur = endBadCaptures; - endCur = endBadQuiets; + // Prepare the pointers to loop over quiets again + cur = endCaptures; + endCur = endGenerated; ++stage; [[fallthrough]]; case BAD_QUIET : if (!skipQuiets) - return select([]() { return true; }); + return select([&]() { return cur->value <= goodQuietThreshold; }); return Move::none(); case EVASION_INIT : cur = moves; - endCur = generate(pos, cur); + endCur = endGenerated = generate(pos, cur); score(); partial_insertion_sort(cur, endCur, std::numeric_limits::min()); @@ -315,7 +311,7 @@ bool MovePicker::can_move_king_or_pawn() const { // SEE negative captures shouldn't be returned in GOOD_CAPTURE stage assert(stage > GOOD_CAPTURE && stage != EVASION_INIT); - for (const ExtMove* m = moves; m < endCur; ++m) + for (const ExtMove* m = moves; m < endGenerated; ++m) { PieceType movedPieceType = type_of(pos.moved_piece(*m)); if ((movedPieceType == PAWN || movedPieceType == KING) && pos.legal(*m)) diff --git a/src/movepick.h b/src/movepick.h index b6784fb78..bf0c96c7c 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -67,7 +67,7 @@ class MovePicker { const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; Move ttMove; - ExtMove * cur, *endCur, *endBadCaptures, *endBadQuiets; + ExtMove * cur, *endCur, *endBadCaptures, *endCaptures, *endGenerated; int stage; int threshold; Depth depth; From a7a56c41f6429d5ab2b210f2ffca85ed76c4ead2 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Tue, 3 Jun 2025 11:50:26 -0700 Subject: [PATCH 095/107] Simplify history term in futility pruning Passed simplification STC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 298816 W: 76814 L: 76881 D: 145121 Ptnml(0-2): 726, 35477, 77057, 35434, 714 https://tests.stockfishchess.org/tests/view/683f440f6ec7634154f9dc7f Passed simplification LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 237774 W: 60801 L: 60802 D: 116171 Ptnml(0-2): 91, 26088, 66532, 26083, 93 https://tests.stockfishchess.org/tests/view/68441189ffbc71bd236778de closes https://github.com/official-stockfish/Stockfish/pull/6130 Bench: 2411502 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9f44defbe..820bc8cdd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1083,7 +1083,7 @@ moves_loop: // When in check, search starts here lmrDepth += history / 3388; - Value baseFutility = (bestMove ? 46 : 138 + std::abs(history / 300)); + Value baseFutility = (bestMove ? 46 : 230); Value futilityValue = ss->staticEval + baseFutility + 117 * lmrDepth + 102 * (ss->staticEval > alpha); From 318c948c4d3dfdd0ff9537ea328ac2e1de72c870 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 27 Jun 2025 21:30:47 -0700 Subject: [PATCH 096/107] Remove non-functional low-ply history fill lowPlyHistory is always cleared at the start of `iterative_deepening`, so clearing it here is non-functional. closes https://github.com/official-stockfish/Stockfish/pull/6144 No functional change --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 820bc8cdd..a82b4edf0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -536,7 +536,6 @@ void Search::Worker::undo_null_move(Position& pos) { pos.undo_null_move(); } // Reset histories, usually before a new game void Search::Worker::clear() { mainHistory.fill(67); - lowPlyHistory.fill(107); captureHistory.fill(-688); pawnHistory.fill(-1287); pawnCorrectionHistory.fill(5); From 3a0fff96cf4d7ba8f50f0f86f4d00e254147bfd6 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Fri, 13 Jun 2025 09:52:26 -0700 Subject: [PATCH 097/107] Simplify quiet move streak logic Passed non-regression STC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 148960 W: 38409 L: 38312 D: 72239 Ptnml(0-2): 372, 17664, 38318, 17747, 379 https://tests.stockfishchess.org/tests/view/684c5773703522d4f129c5f7 Passed non-regression LTC LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 180720 W: 46188 L: 46130 D: 88402 Ptnml(0-2): 84, 19608, 50929, 19644, 95 https://tests.stockfishchess.org/tests/view/68505fa5703522d4f129cbab closes https://github.com/official-stockfish/Stockfish/pull/6143 Bench: 2055894 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a82b4edf0..cdbc95318 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1206,8 +1206,7 @@ moves_loop: // When in check, search starts here if ((ss + 1)->cutoffCnt > 2) r += 1036 + allNode * 848; - if (!capture && !givesCheck && ss->quietMoveStreak >= 2) - r += (ss->quietMoveStreak - 1) * 50; + r += (ss + 1)->quietMoveStreak * 50; // For first picked move (ttMove) reduce reduction if (move == ttData.move) From 84e2f3851d5465a5dfd08a65bde72d555832a2a1 Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Thu, 26 Jun 2025 17:19:09 +0200 Subject: [PATCH 098/107] Introduce a constant for ValueList size in search() Having the size of these lists in two separate places likely contributed to the crashes seen during the recent tuning attempt https://tests.stockfishchess.org/tests/view/685c413343ce022d15794536. Thanks to @MinetaS for spotting this. closes https://github.com/official-stockfish/Stockfish/pull/6142 No functional change --- src/search.cpp | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cdbc95318..4d0e64b32 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,6 +63,9 @@ using namespace Search; namespace { +constexpr int SEARCHEDLIST_CAPACITY = 32; +using SearchedList = ValueList; + // (*Scalers): // The values with Scaler asterisks have proven non-linear scaling. // They are optimized to time controls of 180 + 1.8 and longer, @@ -119,16 +122,16 @@ void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_quiet_histories( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); -void update_all_stats(const Position& pos, - Stack* ss, - Search::Worker& workerThread, - Move bestMove, - Square prevSq, - ValueList& quietsSearched, - ValueList& capturesSearched, - Depth depth, - Move TTMove, - int moveCount); +void update_all_stats(const Position& pos, + Stack* ss, + Search::Worker& workerThread, + Move bestMove, + Square prevSq, + SearchedList& quietsSearched, + SearchedList& capturesSearched, + Depth depth, + Move TTMove, + int moveCount); } // namespace @@ -605,8 +608,8 @@ Value Search::Worker::search( int priorReduction; Piece movedPiece; - ValueList capturesSearched; - ValueList quietsSearched; + SearchedList capturesSearched; + SearchedList quietsSearched; // Step 1. Initialize node Worker* thisThread = this; @@ -1390,7 +1393,7 @@ moves_loop: // When in check, search starts here // If the move is worse than some previously searched move, // remember it, to update its stats later. - if (move != bestMove && moveCount <= 32) + if (move != bestMove && moveCount <= SEARCHEDLIST_CAPACITY) { if (capture) capturesSearched.push_back(move); @@ -1833,16 +1836,16 @@ void update_pv(Move* pv, Move move, const Move* childPv) { // Updates stats at the end of search() when a bestMove is found -void update_all_stats(const Position& pos, - Stack* ss, - Search::Worker& workerThread, - Move bestMove, - Square prevSq, - ValueList& quietsSearched, - ValueList& capturesSearched, - Depth depth, - Move ttMove, - int moveCount) { +void update_all_stats(const Position& pos, + Stack* ss, + Search::Worker& workerThread, + Move bestMove, + Square prevSq, + SearchedList& quietsSearched, + SearchedList& capturesSearched, + Depth depth, + Move ttMove, + int moveCount) { CapturePieceToHistory& captureHistory = workerThread.captureHistory; Piece movedPiece = pos.moved_piece(bestMove); From ea85a54fef82aaa760460c1dea8826bba1ed9597 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Wed, 25 Jun 2025 21:33:47 +0900 Subject: [PATCH 099/107] Fix trivial errors in Makefile 1. Remove "default" rule as "default" has no special meaning as a rule name. Make runs the very first rule whose name doesn't begin with a dot (which is "help" currently). 2. Make "format" rule not update dependencies. closes https://github.com/official-stockfish/Stockfish/pull/6140 No functional change --- src/Makefile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 87bf4d697..50bb2082a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -996,10 +996,6 @@ net: format: $(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file -# default target -default: - help - ### ========================================================================== ### Section 5. Private Targets ### ========================================================================== @@ -1125,6 +1121,6 @@ icx-profile-use: .depend: $(SRCS) -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null -ifeq (, $(filter $(MAKECMDGOALS), help strip install clean net objclean profileclean config-sanity)) +ifeq (, $(filter $(MAKECMDGOALS), help strip install clean net objclean profileclean format config-sanity)) -include .depend endif From ce7254b5ea3b9b7accb37c8e07fb64eeb5fcfcfa Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 24 Jun 2025 15:09:51 -0700 Subject: [PATCH 100/107] Optimize find_nnz() using AVX512 About a 1% speedup for ARCH x86-64-avx512 and x86-64-vnni512. Note: This could be optimized further if we wanted to add an ARCH supporting VBMI2 which is even more modern than VNNI. https://en.wikichip.org/wiki/x86/avx512_vbmi2 closes https://github.com/official-stockfish/Stockfish/pull/6139 No functional change --- src/Makefile | 2 +- .../layers/affine_transform_sparse_input.h | 36 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 50bb2082a..14c3c50c9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -701,7 +701,7 @@ endif ifeq ($(avx512),yes) CXXFLAGS += -DUSE_AVX512 ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) - CXXFLAGS += -mavx512f -mavx512bw + CXXFLAGS += -mavx512f -mavx512bw -mavx512dq -mavx512vl endif endif diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 51f86fd6e..e77c98f8c 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -68,9 +68,42 @@ alignas(CacheLineSize) static constexpr struct OffsetIndices { } Lookup; + #if defined(__GNUC__) || defined(__clang__) + #define RESTRICT __restrict__ + #elif defined(_MSC_VER) + #define RESTRICT __restrict + #else + #define RESTRICT + #endif + // Find indices of nonzero numbers in an int32_t array template -void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) { +void find_nnz(const std::int32_t* RESTRICT input, + std::uint16_t* RESTRICT out, + IndexType& count_out) { + + #ifdef USE_AVX512 + constexpr IndexType SimdWidth = 16; // 512 bits / 32 bits + constexpr IndexType NumChunks = InputDimensions / SimdWidth; + const __m512i increment = _mm512_set1_epi32(SimdWidth); + __m512i base = _mm512_set_epi32(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + + IndexType count = 0; + for (IndexType i = 0; i < NumChunks; ++i) + { + const __m512i inputV = _mm512_load_si512(input + i * SimdWidth); + + // Get a bitmask and gather non zero indices + const __mmask16 nnzMask = _mm512_test_epi32_mask(inputV, inputV); + const __m512i nnzV = _mm512_maskz_compress_epi32(nnzMask, base); + _mm512_mask_cvtepi32_storeu_epi16(out + count, 0xFFFF, nnzV); + count += popcount(nnzMask); + base = _mm512_add_epi32(base, increment); + } + count_out = count; + + #else + using namespace SIMD; constexpr IndexType InputSimdWidth = sizeof(vec_uint_t) / sizeof(std::int32_t); @@ -104,6 +137,7 @@ void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_ou } } count_out = count; + #endif } #endif From e695b9537ec1d7b19306deefe24d889db563dbb0 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 14 Jun 2025 16:15:15 +0300 Subject: [PATCH 101/107] Remove eval & beta diff from NM reduction Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 43456 W: 11178 L: 10966 D: 21312 Ptnml(0-2): 114, 5078, 11114, 5326, 96 https://tests.stockfishchess.org/tests/view/6849ae13e84567164b5c9de9 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 63090 W: 16302 L: 16125 D: 30663 Ptnml(0-2): 37, 6837, 17603, 7048, 20 https://tests.stockfishchess.org/tests/view/684ab516e84567164b5ca02f closes https://github.com/official-stockfish/Stockfish/pull/6134 Bench: 2249459 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4d0e64b32..394070458 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -859,8 +859,8 @@ Value Search::Worker::search( { assert(eval - beta >= 0); - // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 213, 6) + depth / 3 + 5; + // Null move dynamic reduction based on depth + Depth R = 7 + depth / 3; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; From ce73441f2013e0b8fd3eb7a0c9fd391d52adde70 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 14 Jun 2025 16:10:59 +0300 Subject: [PATCH 102/107] Simplify sudden death time optimization Passed Sudden Death STC: https://tests.stockfishchess.org/tests/view/68455fe5375c2b77d9855351 LLR: 2.91 (-2.94,2.94) <-1.75,0.25> Total: 49248 W: 13008 L: 12798 D: 23442 Ptnml(0-2): 309, 5491, 12821, 5687, 316 Passed Sudden Death LTC: https://tests.stockfishchess.org/tests/view/6845a392375c2b77d98553cf LLR: 3.01 (-2.94,2.94) <-1.75,0.25> Total: 551070 W: 141699 L: 142031 D: 267340 Ptnml(0-2): 1923, 60608, 150916, 60054, 2034 Passed Standard STC: https://tests.stockfishchess.org/tests/view/683c5ebb6ec7634154f9d989 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 142624 W: 36808 L: 36709 D: 69107 Ptnml(0-2): 302, 15448, 39745, 15483, 334 Passed Standard LTC: https://tests.stockfishchess.org/tests/view/683f1a4f6ec7634154f9dc5a LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 146922 W: 37381 L: 37296 D: 72245 Ptnml(0-2): 69, 13552, 46117, 13671, 52 closes https://github.com/official-stockfish/Stockfish/pull/6132 Bench: 2249459 --- src/timeman.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 29ebffcaa..5840e2556 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -85,16 +85,13 @@ void TimeManagement::init(Search::LimitsType& limits, // with constants are involved. const int64_t scaleFactor = useNodesTime ? npmsec : 1; const TimePoint scaledTime = limits.time[us] / scaleFactor; - const TimePoint scaledInc = limits.inc[us] / scaleFactor; // Maximum move horizon int centiMTG = limits.movestogo ? std::min(limits.movestogo * 100, 5000) : 5051; // If less than one second, gradually reduce mtg - if (scaledTime < 1000 && double(centiMTG) / scaledInc > 5.051) - { + if (scaledTime < 1000) centiMTG = scaledTime * 5.051; - } // Make sure timeLeft is > 0 since we may use it as a divisor TimePoint timeLeft = From 62f08568cd724f10f86781bc58dabce616546478 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 15 Jun 2025 12:49:44 -0700 Subject: [PATCH 103/107] Simplify PV term in lmr Passed simplification STC LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 204000 W: 52541 L: 52506 D: 98953 Ptnml(0-2): 561, 24133, 52589, 24144, 573 https://tests.stockfishchess.org/tests/view/684f24ce703522d4f129cab5 Passed simplification LTC LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 156150 W: 39890 L: 39807 D: 76453 Ptnml(0-2): 82, 16882, 44043, 17007, 61 https://tests.stockfishchess.org/tests/view/6855d8bf1d0d9fc6587538f8 closes https://github.com/official-stockfish/Stockfish/pull/6148 bench 2766493 --- src/search.cpp | 3 +-- src/search.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 394070458..6430c3e32 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -655,7 +655,6 @@ Value Search::Worker::search( priorReduction = (ss - 1)->reduction; (ss - 1)->reduction = 0; ss->statScore = 0; - ss->isPvNode = PvNode; (ss + 2)->cutoffCnt = 0; // Step 4. Transposition table lookup @@ -1238,7 +1237,7 @@ moves_loop: // When in check, search starts here // std::clamp has been replaced by a more robust implementation. Depth d = std::max(1, std::min(newDepth - r / 1024, newDepth + !allNode + (PvNode && !bestMove))) - + (ss - 1)->isPvNode; + + PvNode; ss->reduction = newDepth - d; value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); diff --git a/src/search.h b/src/search.h index 5caff10e8..e0b57e30b 100644 --- a/src/search.h +++ b/src/search.h @@ -75,7 +75,6 @@ struct Stack { bool ttHit; int cutoffCnt; int reduction; - bool isPvNode; int quietMoveStreak; }; From 6a09a24cd89e81662d55131b2d91396d4d1fe72a Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 2 Jul 2025 22:50:51 +0300 Subject: [PATCH 104/107] Remove depth condition from ttCapture reduction Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 25920 W: 6822 L: 6593 D: 12505 Ptnml(0-2): 54, 2932, 6783, 3113, 78 https://tests.stockfishchess.org/tests/view/6853f9c2038630d25f468672 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 177318 W: 45339 L: 45278 D: 86701 Ptnml(0-2): 85, 19333, 49765, 19388, 88 https://tests.stockfishchess.org/tests/view/6854468a038630d25f4686c0 closes https://github.com/official-stockfish/Stockfish/pull/6149 bench: 2437275 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6430c3e32..8decb5d1e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1202,7 +1202,7 @@ moves_loop: // When in check, search starts here // Increase reduction if ttMove is a capture if (ttCapture) - r += 1210 + (depth < 8) * 963; + r += 1350; // Increase reduction if next ply has a lot of fail high if ((ss + 1)->cutoffCnt > 2) From a43f12ef08a14c5ab5303d223e7ae4e77754e982 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sat, 14 Jun 2025 17:52:59 -0700 Subject: [PATCH 105/107] Simplify away constants in statscore Passed simplification STC LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 317280 W: 81589 L: 81678 D: 154013 Ptnml(0-2): 799, 37651, 81847, 37526, 817 https://tests.stockfishchess.org/tests/view/684e197a703522d4f129c9f0 Passed simplification LTC LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 174816 W: 44656 L: 44593 D: 85567 Ptnml(0-2): 83, 19064, 49058, 19113, 90 https://tests.stockfishchess.org/tests/view/6858aa54a596a06817bb924f closes https://github.com/official-stockfish/Stockfish/pull/6150 bench: 2508140 --- src/search.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8decb5d1e..cfd3583bc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1192,7 +1192,7 @@ moves_loop: // When in check, search starts here // These reduction adjustments have no proven non-linear scaling - r += 316; // Base reduction offset to compensate for other tweaks + r += 650; // Base reduction offset to compensate for other tweaks r -= moveCount * 66; r -= std::abs(correctionValue) / 28047; @@ -1217,12 +1217,11 @@ moves_loop: // When in check, search starts here if (capture) ss->statScore = 826 * int(PieceValue[pos.captured_piece()]) / 128 - + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())] - - 5030; + + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())]; else ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 3206; + + (*contHist[1])[movedPiece][move.to_sq()]; // Decrease/increase reduction for moves with a good/bad history r -= ss->statScore * 826 / 8192; From c13f883dc43b35ccc39738b1611815fe6ae12948 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sat, 14 Jun 2025 17:42:07 -0700 Subject: [PATCH 106/107] simplify away TT history term Passed simplification STC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 163168 W: 42136 L: 42055 D: 78977 Ptnml(0-2): 410, 19224, 42212, 19351, 387 https://tests.stockfishchess.org/tests/view/684e16ee703522d4f129c9e9 Passed simplification LTC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 303144 W: 77580 L: 77647 D: 147917 Ptnml(0-2): 150, 33110, 85134, 33013, 165 https://tests.stockfishchess.org/tests/view/6852ff97703522d4f129d5f7 closes https://github.com/official-stockfish/Stockfish/pull/6152 bench: 2294774 --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cfd3583bc..1b5a2bbbc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1271,11 +1271,9 @@ moves_loop: // When in check, search starts here if (!ttData.move) r += 1128; - r -= ttMoveHistory / 8; - // Note that if expected reduction is high, we reduce search depth here value = -search(pos, ss + 1, -(alpha + 1), -alpha, - newDepth - (r > 3564) - (r > 4969 && newDepth > 2), !cutNode); + newDepth - (r > 3200) - (r > 4600 && newDepth > 2), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail high, From 8c2d21f91a5840a67c36267e5043070ffad06860 Mon Sep 17 00:00:00 2001 From: 87 Date: Sun, 6 Jul 2025 18:18:35 +0100 Subject: [PATCH 107/107] Speedup movegen with VBMI2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passed STC LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 166720 W: 43191 L: 42701 D: 80828 Ptnml(0-2): 348, 18567, 45069, 18999, 377 https://tests.stockfishchess.org/tests/view/686ae98dfe0f2fe354c0c867 Refactor movegen to emit to a vector with 16-bit elements, which enables a speedup with AVX512-VBMI2 when writing moves to the move list. Very crude timing measurements of perft via timing ./stockfish "go perft 7" demonstrates approximately 17% perft speedup: Summary ./Stockfish-dev/src/stockfish 'go perft 7' ran 1.17 ± 0.04 times faster than ./Stockfish-base/src/stockfish 'go perft 7' Estimated overall nps increase of 0.4% via speedtest: 33605229 -> 33749825 (many thanks JonathanHallstrom). The corresponding arch is avx512icl as it is a good baseline for consumer avx-512 feature set support; Intel Ice Lake was the first consumer AVX-512 CPU and it is a decent subset of what AMD Zen 4 supports. closes https://github.com/official-stockfish/Stockfish/pull/6153 No functional change --- AUTHORS | 1 + scripts/get_native_properties.sh | 4 +- src/Makefile | 31 +++++++- src/misc.cpp | 3 + src/movegen.cpp | 131 ++++++++++++++++++++++--------- src/movegen.h | 12 +-- src/movepick.cpp | 30 ++++--- src/movepick.h | 4 +- 8 files changed, 156 insertions(+), 60 deletions(-) diff --git a/AUTHORS b/AUTHORS index be88a8e96..b43a9e5b6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,6 +10,7 @@ Motohiro Isozaki (yaneurao) Hisayori Noda (nodchip) # All other authors of Stockfish code (in alphabetical order) +87flowers Aditya (absimaldata) Adrian Petrescu (apetresc) Ahmed Kerimov (wcdbmv) diff --git a/scripts/get_native_properties.sh b/scripts/get_native_properties.sh index 773d6c271..e8c8f23f2 100755 --- a/scripts/get_native_properties.sh +++ b/scripts/get_native_properties.sh @@ -39,7 +39,9 @@ set_arch_loongarch64() { # Set the file CPU x86_64 architecture set_arch_x86_64() { - if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then + if check_flags 'avx512f' 'avx512cd' 'avx512vl' 'avx512dq' 'avx512bw' 'avx512ifma' 'avx512vbmi' 'avx512vbmi2' 'avx512vpopcntdq' 'avx512bitalg' 'avx512vnni' 'vpclmulqdq' 'gfni' 'vaes'; then + true_arch='x86-64-avx512icl' + elif check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then true_arch='x86-64-vnni256' elif check_flags 'avx512f' 'avx512bw'; then true_arch='x86-64-avx512' diff --git a/src/Makefile b/src/Makefile index 14c3c50c9..40dceae0b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -99,6 +99,7 @@ VPATH = syzygy:nnue:nnue/features # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 # vnni256 = yes/no --- -mavx256vnni --- Use Intel Vector Neural Network Instructions 512 with 256bit operands # vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 +# avx512icl = yes/no --- ... multiple ... --- Use All AVX-512 features available on both Intel Ice Lake and AMD Zen 4 # altivec = yes/no --- -maltivec --- Use PowerPC Altivec SIMD extension # vsx = yes/no --- -mvsx --- Use POWER VSX SIMD extension # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture @@ -125,10 +126,10 @@ ifeq ($(ARCH), native) endif # explicitly check for the list of supported architectures (as listed with make help), -# the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true` +# the user can override with `make ARCH=x86-64-avx512icl SUPPORTED_ARCH=true` ifeq ($(ARCH), $(filter $(ARCH), \ - x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ - x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ + x86-64-avx512icl x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni \ + x86-64-bmi2 x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-64-altivec ppc-64-vsx ppc-32 e2k \ armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \ loongarch64 loongarch64-lsx loongarch64-lasx)) @@ -154,6 +155,7 @@ avxvnni = no avx512 = no vnni256 = no vnni512 = no +avx512icl = no altivec = no vsx = no neon = no @@ -290,6 +292,19 @@ ifeq ($(findstring -vnni512,$(ARCH)),-vnni512) vnni512 = yes endif +ifeq ($(findstring -avx512icl,$(ARCH)),-avx512icl) + popcnt = yes + sse = yes + sse2 = yes + ssse3 = yes + sse41 = yes + avx2 = yes + pext = yes + avx512 = yes + vnni512 = yes + avx512icl = yes +endif + ifeq ($(sse),yes) prefetch = yes endif @@ -719,6 +734,13 @@ ifeq ($(vnni512),yes) endif endif +ifeq ($(avx512icl),yes) + CXXFLAGS += -DUSE_AVX512 -DUSE_VNNI -DUSE_AVX512ICL + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) + CXXFLAGS += -mavx512f -mavx512cd -mavx512vl -mavx512dq -mavx512bw -mavx512ifma -mavx512vbmi -mavx512vbmi2 -mavx512vpopcntdq -mavx512bitalg -mavx512vnni -mvpclmulqdq -mgfni -mvaes + endif +endif + ifeq ($(sse41),yes) CXXFLAGS += -DUSE_SSE41 ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) @@ -877,6 +899,7 @@ help: echo "Supported archs:" && \ echo "" && \ echo "native > select the best architecture for the host processor (default)" && \ + echo "x86-64-avx512icl > x86 64-bit with minimum avx512 support of Intel Ice Lane or AMD Zen 4" && \ echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" && \ echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" && \ echo "x86-64-avx512 > x86 64-bit with avx512 support" && \ @@ -1025,6 +1048,7 @@ config-sanity: net echo "avx512: '$(avx512)'" && \ echo "vnni256: '$(vnni256)'" && \ echo "vnni512: '$(vnni512)'" && \ + echo "avx512icl: '$(avx512icl)'" && \ echo "altivec: '$(altivec)'" && \ echo "vsx: '$(vsx)'" && \ echo "neon: '$(neon)'" && \ @@ -1061,6 +1085,7 @@ config-sanity: net (test "$(avx512)" = "yes" || test "$(avx512)" = "no") && \ (test "$(vnni256)" = "yes" || test "$(vnni256)" = "no") && \ (test "$(vnni512)" = "yes" || test "$(vnni512)" = "no") && \ + (test "$(avx512icl)" = "yes" || test "$(avx512icl)" = "no") && \ (test "$(altivec)" = "yes" || test "$(altivec)" = "no") && \ (test "$(vsx)" = "yes" || test "$(vsx)" = "no") && \ (test "$(neon)" = "yes" || test "$(neon)" = "no") && \ diff --git a/src/misc.cpp b/src/misc.cpp index f85356c59..3bdde000f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -237,6 +237,9 @@ std::string compiler_info() { compiler += "\nCompilation settings : "; compiler += (Is64Bit ? "64bit" : "32bit"); +#if defined(USE_AVX512ICL) + compiler += " AVX512ICL"; +#endif #if defined(USE_VNNI) compiler += " VNNI"; #endif diff --git a/src/movegen.cpp b/src/movegen.cpp index a73bd8501..10adc70be 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -24,12 +24,88 @@ #include "bitboard.h" #include "position.h" +#if defined(USE_AVX512ICL) + #include + #include + #include +#endif + namespace Stockfish { namespace { +#if defined(USE_AVX512ICL) + +inline Move* write_moves(Move* moveList, uint32_t mask, __m512i vector) { + _mm512_storeu_si512(reinterpret_cast<__m512i*>(moveList), + _mm512_maskz_compress_epi16(mask, vector)); + return moveList + popcount(mask); +} + +template +inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) { + alignas(64) static constexpr auto SPLAT_TABLE = [] { + std::array table{}; + for (int8_t i = 0; i < 64; i++) + { + Square from{std::clamp(i - offset, 0, 63)}; + table[i] = {Move(from, Square{i})}; + } + return table; + }(); + + auto table = reinterpret_cast(SPLAT_TABLE.data()); + + moveList = + write_moves(moveList, static_cast(to_bb >> 0), _mm512_load_si512(table + 0)); + moveList = + write_moves(moveList, static_cast(to_bb >> 32), _mm512_load_si512(table + 1)); + + return moveList; +} + +inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) { + alignas(64) static constexpr auto SPLAT_TABLE = [] { + std::array table{}; + for (int8_t i = 0; i < 64; i++) + table[i] = {Move(SQUARE_ZERO, Square{i})}; + return table; + }(); + + __m512i fromVec = _mm512_set1_epi16(Move(from, SQUARE_ZERO).raw()); + + auto table = reinterpret_cast(SPLAT_TABLE.data()); + + moveList = write_moves(moveList, static_cast(to_bb >> 0), + _mm512_or_si512(_mm512_load_si512(table + 0), fromVec)); + moveList = write_moves(moveList, static_cast(to_bb >> 32), + _mm512_or_si512(_mm512_load_si512(table + 1), fromVec)); + + return moveList; +} + +#else + +template +inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) { + while (to_bb) + { + Square to = pop_lsb(to_bb); + *moveList++ = Move(to - offset, to); + } + return moveList; +} + +inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) { + while (to_bb) + *moveList++ = Move(from, pop_lsb(to_bb)); + return moveList; +} + +#endif + template -ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) { +Move* make_promotions(Move* moveList, [[maybe_unused]] Square to) { constexpr bool all = Type == EVASIONS || Type == NON_EVASIONS; @@ -48,7 +124,7 @@ ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) { template -ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { +Move* generate_pawn_moves(const Position& pos, Move* moveList, Bitboard target) { constexpr Color Them = ~Us; constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); @@ -75,17 +151,8 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta b2 &= target; } - while (b1) - { - Square to = pop_lsb(b1); - *moveList++ = Move(to - Up, to); - } - - while (b2) - { - Square to = pop_lsb(b2); - *moveList++ = Move(to - Up - Up, to); - } + moveList = splat_pawn_moves(moveList, b1); + moveList = splat_pawn_moves(moveList, b2); } // Promotions and underpromotions @@ -114,17 +181,8 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta Bitboard b1 = shift(pawnsNotOn7) & enemies; Bitboard b2 = shift(pawnsNotOn7) & enemies; - while (b1) - { - Square to = pop_lsb(b1); - *moveList++ = Move(to - UpRight, to); - } - - while (b2) - { - Square to = pop_lsb(b2); - *moveList++ = Move(to - UpLeft, to); - } + moveList = splat_pawn_moves(moveList, b1); + moveList = splat_pawn_moves(moveList, b2); if (pos.ep_square() != SQ_NONE) { @@ -148,7 +206,7 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta template -ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { +Move* generate_moves(const Position& pos, Move* moveList, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); @@ -159,8 +217,7 @@ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; - while (b) - *moveList++ = Move(from, pop_lsb(b)); + moveList = splat_moves(moveList, from, b); } return moveList; @@ -168,7 +225,7 @@ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) template -ExtMove* generate_all(const Position& pos, ExtMove* moveList) { +Move* generate_all(const Position& pos, Move* moveList) { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); @@ -192,8 +249,7 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) { Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); - while (b) - *moveList++ = Move(ksq, pop_lsb(b)); + moveList = splat_moves(moveList, ksq, b); if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE}) @@ -213,7 +269,7 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) { // // Returns a pointer to the end of the move list. template -ExtMove* generate(const Position& pos, ExtMove* moveList) { +Move* generate(const Position& pos, Move* moveList) { static_assert(Type != LEGAL, "Unsupported type in generate()"); assert((Type == EVASIONS) == bool(pos.checkers())); @@ -225,21 +281,20 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { } // Explicit template instantiations -template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); - +template Move* generate(const Position&, Move*); +template Move* generate(const Position&, Move*); +template Move* generate(const Position&, Move*); +template Move* generate(const Position&, Move*); // generate generates all the legal moves in the given position template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { +Move* generate(const Position& pos, Move* moveList) { Color us = pos.side_to_move(); Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us); Square ksq = pos.square(us); - ExtMove* cur = moveList; + Move* cur = moveList; moveList = pos.checkers() ? generate(pos, moveList) : generate(pos, moveList); diff --git a/src/movegen.h b/src/movegen.h index 7c6cceb7c..287fd8927 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -49,7 +49,7 @@ struct ExtMove: public Move { inline bool operator<(const ExtMove& f, const ExtMove& s) { return f.value < s.value; } template -ExtMove* generate(const Position& pos, ExtMove* moveList); +Move* generate(const Position& pos, Move* moveList); // The MoveList struct wraps the generate() function and returns a convenient // list of moves. Using MoveList is sometimes preferable to directly calling @@ -59,13 +59,13 @@ struct MoveList { explicit MoveList(const Position& pos) : last(generate(pos, moveList)) {} - const ExtMove* begin() const { return moveList; } - const ExtMove* end() const { return last; } - size_t size() const { return last - moveList; } - bool contains(Move move) const { return std::find(begin(), end(), move) != end(); } + const Move* begin() const { return moveList; } + const Move* end() const { return last; } + size_t size() const { return last - moveList; } + bool contains(Move move) const { return std::find(begin(), end(), move) != end(); } private: - ExtMove moveList[MAX_MOVES], *last; + Move moveList[MAX_MOVES], *last; }; } // namespace Stockfish diff --git a/src/movepick.cpp b/src/movepick.cpp index 3e7c10160..79b6f55a2 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -123,7 +123,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceTo // Captures are ordered by Most Valuable Victim (MVV), preferring captures // with a good history. Quiets moves are ordered using the history tables. template -void MovePicker::score() { +ExtMove* MovePicker::score(MoveList& ml) { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); @@ -138,8 +138,12 @@ void MovePicker::score() { threatByLesser[QUEEN] = pos.attacks_by(~us) | threatByLesser[ROOK]; } - for (auto& m : *this) + ExtMove* it = cur; + for (auto move : ml) { + ExtMove& m = *it++; + m = move; + const Square from = m.from_sq(); const Square to = m.to_sq(); const Piece pc = pos.moved_piece(m); @@ -189,6 +193,7 @@ void MovePicker::score() { } } } + return it; } // Returns the next move satisfying a predicate function. @@ -222,14 +227,16 @@ top: case CAPTURE_INIT : case PROBCUT_INIT : - case QCAPTURE_INIT : + case QCAPTURE_INIT : { + MoveList ml(pos); + cur = endBadCaptures = moves; - endCur = endCaptures = generate(pos, cur); + endCur = endCaptures = score(ml); - score(); partial_insertion_sort(cur, endCur, std::numeric_limits::min()); ++stage; goto top; + } case GOOD_CAPTURE : if (select([&]() { @@ -246,9 +253,10 @@ top: case QUIET_INIT : if (!skipQuiets) { - endCur = endGenerated = generate(pos, cur); + MoveList ml(pos); + + endCur = endGenerated = score(ml); - score(); partial_insertion_sort(cur, endCur, -3560 * depth); } @@ -283,14 +291,16 @@ top: return Move::none(); - case EVASION_INIT : + case EVASION_INIT : { + MoveList ml(pos); + cur = moves; - endCur = endGenerated = generate(pos, cur); + endCur = endGenerated = score(ml); - score(); partial_insertion_sort(cur, endCur, std::numeric_limits::min()); ++stage; [[fallthrough]]; + } case EVASION : case QCAPTURE : diff --git a/src/movepick.h b/src/movepick.h index bf0c96c7c..9d6c02b0e 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -55,8 +55,8 @@ class MovePicker { private: template Move select(Pred); - template - void score(); + template + ExtMove* score(MoveList&); ExtMove* begin() { return cur; } ExtMove* end() { return endCur; }