From ddc9f48bc3e0b1d22b2d1259d5d45d4640e0374d Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 23 Aug 2024 17:13:49 -0700 Subject: [PATCH] Introduce Material Correction History Idea from Caissa (https://github.com/Witek902/Caissa) chess engine. Add a secondary correction history indexed by the material key of a position. The material key is the zobrist hash representing the number of pieces left in a position. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 189472 W: 49360 L: 48813 D: 91299 Ptnml(0-2): 666, 22453, 47953, 22996, 668 https://tests.stockfishchess.org/tests/view/66cbddafbf8c9d8780fda9f1 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 224190 W: 57022 L: 56312 D: 110856 Ptnml(0-2): 197, 24723, 61540, 25443, 192 https://tests.stockfishchess.org/tests/view/66cd529bbf8c9d8780fdab4c closes https://github.com/official-stockfish/Stockfish/pull/5556 Bench: 1462697 --- src/movepick.h | 30 ++++++++++++++++++++++-------- src/search.cpp | 11 ++++++++--- src/search.h | 11 ++++++----- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 61f6368e..651091b0 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -34,14 +34,15 @@ namespace Stockfish { -constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 -constexpr int CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2 -constexpr int CORRECTION_HISTORY_LIMIT = 1024; +constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 +constexpr int PAWN_CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2 +constexpr int MATERIAL_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 +constexpr int CORRECTION_HISTORY_LIMIT = 1024; static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, "PAWN_HISTORY_SIZE has to be a power of 2"); -static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0, +static_assert((PAWN_CORRECTION_HISTORY_SIZE & (PAWN_CORRECTION_HISTORY_SIZE - 1)) == 0, "CORRECTION_HISTORY_SIZE has to be a power of 2"); enum PawnHistoryType { @@ -51,7 +52,11 @@ enum PawnHistoryType { template inline int pawn_structure_index(const Position& pos) { - return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1); + return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : PAWN_CORRECTION_HISTORY_SIZE) - 1); +} + +inline int material_index(const Position& pos) { + return pos.material_key() & (MATERIAL_CORRECTION_HISTORY_SIZE - 1); } // StatsEntry stores the stat table value. It is usually a number but could @@ -133,9 +138,18 @@ using ContinuationHistory = Stats // PawnHistory is addressed by the pawn structure and a move's [piece][to] using PawnHistory = Stats; -// CorrectionHistory is addressed by color and pawn structure -using CorrectionHistory = - Stats; + +// Correction histories record differences between the static evaluation of +// positions and their search score. It is used to improve the static evaluation +// used by some search heuristics. + +// PawnCorrectionHistory is addressed by color and pawn structure +using PawnCorrectionHistory = + Stats; + +// MaterialCorrectionHistory is addressed by color and material configuration +using MaterialCorrectionHistory = + Stats; // 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 diff --git a/src/search.cpp b/src/search.cpp index 9d950c0e..bc85a5c3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -81,7 +81,10 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation // does not hit the tablebase range. Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { - auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; + const auto pcv = + w.pawnCorrectionHistory[pos.side_to_move()][pawn_structure_index(pos)]; + const auto mcv = w.materialCorrectionHistory[pos.side_to_move()][material_index(pos)]; + const auto cv = (2 * pcv + mcv) / 3; v += 66 * cv / 512; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } @@ -487,7 +490,8 @@ void Search::Worker::clear() { mainHistory.fill(0); captureHistory.fill(-700); pawnHistory.fill(-1188); - correctionHistory.fill(0); + pawnCorrectionHistory.fill(0); + materialCorrectionHistory.fill(0); for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) @@ -1390,7 +1394,8 @@ moves_loop: // When in check, search starts here { auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); - thisThread->correctionHistory[us][pawn_structure_index(pos)] << bonus; + thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] << bonus; + thisThread->materialCorrectionHistory[us][material_index(pos)] << bonus; } assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); diff --git a/src/search.h b/src/search.h index 0f635186..c9fe9e18 100644 --- a/src/search.h +++ b/src/search.h @@ -277,11 +277,12 @@ class Worker { void ensure_network_replicated(); // Public because they need to be updatable by the stats - ButterflyHistory mainHistory; - CapturePieceToHistory captureHistory; - ContinuationHistory continuationHistory[2][2]; - PawnHistory pawnHistory; - CorrectionHistory correctionHistory; + ButterflyHistory mainHistory; + CapturePieceToHistory captureHistory; + ContinuationHistory continuationHistory[2][2]; + PawnHistory pawnHistory; + PawnCorrectionHistory pawnCorrectionHistory; + MaterialCorrectionHistory materialCorrectionHistory; private: void iterative_deepening();