From 0d9d595403e12ad652dead5d3e9e2c9f762e35f3 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 10 Oct 2026 18:43:04 -0500 Subject: [PATCH] Add guarded Strelka-style razoring --- src/search.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 93ff16e..22d8f5a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -18,11 +18,25 @@ namespace ironphoenix { constexpr int LMP_HISTORY_THRESHOLD = 2'000; + // Strelka-style razoring margins. At depth 1 the static evaluation gets + // 125 cp of slack; depths 2-3 use a total 300 cp margin before qsearch. + constexpr int RAZOR_DEPTH1_MARGIN = 125; + constexpr int RAZOR_DEPTH3_MARGIN = 300; + constexpr int RAZOR_MAX_DEPTH = 3; + IRONPHOENIX_FORCE_INLINE Bitboard teamOccupancy(const Position& pos, Color c) noexcept { const Color partner = static_cast(static_cast(c) ^ 2u); return pos.occupancy(c) | pos.occupancy(partner); } + IRONPHOENIX_FORCE_INLINE bool anyPlayerInCheck(const Position& pos) noexcept { + for (unsigned ci = 0; ci < COLOR_NB; ++ci) { + if (pos.inCheck(static_cast(ci))) + return true; + } + return false; + } + } SearchEngine::SearchEngine() = default; @@ -413,6 +427,36 @@ namespace ironphoenix { const bool inCheckNode = pos.inCheck(); const auto& tuning = Tuning::search(); + // Strelka-style razoring: at shallow expected fail-low nodes, use qsearch + // to decide whether the full search is worth doing. In 4PC this is guarded + // more strictly than Strelka: razoring is disabled if ANY player is in + // check, not merely the side to move. Keep it away from PV/root nodes and + // mate windows as well. + if (!pvNode + && ply > 0 + && depth <= RAZOR_MAX_DEPTH + && !anyPlayerInCheck(pos) + && alpha > -MATE_THRESHOLD + && beta < MATE_THRESHOLD) { + + const int staticEval = evaluate(pos); + int razorValue = staticEval + RAZOR_DEPTH1_MARGIN; + + if (razorValue < beta) { + if (depth == 1) { + const int qscore = qsearch(pos, alpha, beta, ply, false); + return std::max(qscore, razorValue); + } + + razorValue = staticEval + RAZOR_DEPTH3_MARGIN; + if (razorValue < beta) { + const int qscore = qsearch(pos, alpha, beta, ply, false); + if (qscore < beta) + return std::max(qscore, razorValue); + } + } + } + MoveList moves; generatePseudoLegalMoves(pos, moves); orderMoves(pos, moves.begin(), moves.end(), ttMove, ply);