From 93ed4b53c4f602c4cc41dbdb67961a2a4712c60b Mon Sep 17 00:00:00 2001 From: tony <35334345+TonyCongqianWang@users.noreply.github.com> Date: Fri, 10 Jul 2026 20:17:55 +0200 Subject: [PATCH] Dynamic root score ema2 Improves calculation of root move score averages by dynamically calculating weight based on effort Passed STC https://tests.stockfishchess.org/tests/view/6a4ada97f97ff95f78795d5a ``` LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 157248 W: 41003 L: 40515 D: 75730 Ptnml(0-2): 419, 18320, 40644, 18836, 405 ``` Passed LTC ``` LLR: 3.03 (-2.94,2.94) <0.50,2.50> Total: 197460 W: 51626 L: 50982 D: 94852 Ptnml(0-2): 85, 20849, 56237, 21455, 104 ``` closes https://github.com/official-stockfish/Stockfish/pull/6960 Bench: 2388656 --- src/search.cpp | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index bc922119c..28367605e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1408,12 +1409,32 @@ moves_loop: // When in check, search starts here rm.effort += nodes - nodeCount; - rm.averageScore = - rm.averageScore != -VALUE_INFINITE ? (value + rm.averageScore) / 2 : value; + u64 N = nodes - nodeCount; + u64 E_prev = std::max(u64(1), rm.effort - N); - rm.meanSquaredScore = rm.meanSquaredScore != -VALUE_INFINITE * VALUE_INFINITE - ? (value * std::abs(value) + rm.meanSquaredScore) / 2 - : value * std::abs(value); + // Dynamic EMA parameters for root move + constexpr u64 Scale = 32; + constexpr u64 ChiNumerator = 3; + constexpr u64 ChiDenominator = 2; // Chi = 3/2 = 1.5 + constexpr u64 MinWeight = 12; // 37.5% minimum weight + constexpr u64 MaxWeight = 24; // 75% maximum weight + + u64 w = std::clamp((Scale * N * ChiDenominator) + / (N * ChiDenominator + ChiNumerator * E_prev), + MinWeight, MaxWeight); + u64 w_mss = std::min(w, u64(16)); + i64 v2 = i64(value) * std::abs(value); + + if (rm.averageScore == -VALUE_INFINITE) + rm.averageScore = value; + else + rm.averageScore = Value((value * w + rm.averageScore * (Scale - w)) / Scale); + + if (rm.meanSquaredScore == -VALUE_INFINITE * VALUE_INFINITE) + rm.meanSquaredScore = value * std::abs(value); + else + rm.meanSquaredScore = + Value((v2 * w_mss + int64_t(rm.meanSquaredScore) * (Scale - w_mss)) / Scale); // PV move or new best move? if (moveCount == 1 || value > alpha)