summaryrefslogtreecommitdiff
path: root/src/lmr_testing/searchsup_GetLMRReduction.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lmr_testing/searchsup_GetLMRReduction.c')
-rw-r--r--src/lmr_testing/searchsup_GetLMRReduction.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/src/lmr_testing/searchsup_GetLMRReduction.c b/src/lmr_testing/searchsup_GetLMRReduction.c
new file mode 100644
index 0000000..0ee69f6
--- /dev/null
+++ b/src/lmr_testing/searchsup_GetLMRReduction.c
@@ -0,0 +1,114 @@
+// Replace searchsup.c's GetLMRReduction body with this (signature/name
+// unchanged, so search.c/split.c call sites need no changes).
+
+INT
+GetLMRReduction(IN SCORE iRoughEval,
+ IN SCORE iAlpha,
+ IN SCORE iBeta,
+ IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN ULONG uRemainingDepth,
+ IN ULONG uLegalMoves,
+ IN MOVE mv,
+ IN ULONG uMoveNum,
+ IN INT iExtend)
+/**
+
+Routine description:
+
+ Decide how much (if any) to reduce this move's search depth by --
+ graded LMR, replacing the old fixed -ONE_PLY history pruning.
+ Note: this function is called after the move has been played on
+ the board (ctx->uPly is already the *child's* ply; ctx->uPly - 1
+ is the node whose move loop we're in, i.e. the parent of the
+ search we're about to reduce).
+
+ PV-parent protection: if the node whose move this is (uPly - 1)
+ was itself reached with a wide window (a genuine PV node, not
+ just non-null-window), don't reduce at all here. This is
+ deliberately about the *parent*, not "am I a PV node myself" --
+ every non-first move at a PV node is searched null-window
+ regardless (standard PVS), so that alone doesn't distinguish
+ "one ply below a real PV" from "deep inside an already-non-PV
+ subtree". Confirmed empirically (not just theoretically) to
+ matter: without this, graded LMR measured worse than baseline;
+ with it, break-even. Grandparent protection (uPly - 2) was
+ tried and measured worse (23/30, EBF 4.258 vs this config's
+ 24/30, EBF 4.223 on ecm_quick.ep_ @ sn=4M) -- not worth it.
+
+Parameters:
+
+ SEARCHER_THREAD_CONTEXT *ctx,
+ ULONG uRemainingDepth,
+ ULONG uLegalMoves,
+ MOVE mv,
+ INT iExtend
+
+Return value:
+
+ INT : 0 if no reduction, else a negative ply-fraction (ONE_PLY
+ units) suitable for adding directly into iExtend.
+
+**/
+{
+ ULONG uDepthPly, uMoveIdx;
+ INT iReduction;
+
+ ASSERT(ctx->uPly > 0);
+ ASSERT(mv.uMove);
+ ASSERT((uMoveNum > 0) || (uLegalMoves == 0));
+ if ((uRemainingDepth >= TWO_PLY) &&
+ (iBeta == (iAlpha + 1)) &&
+ (uLegalMoves > 3) &&
+ (0 == iExtend) &&
+ (!IS_ESCAPING_CHECK(mv)) &&
+ (!IS_CAPTURE_OR_PROMOTION(mv)) &&
+ (!IS_CHECKING_MOVE(mv)) &&
+ (!IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-1][0])) &&
+ (!IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-1][1])) &&
+ ((ctx->uPly < 3) ||
+ (!IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-3][0]) &&
+ !IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-3][1]))) &&
+ (!IS_SAME_MOVE(mv, ctx->mvCounter[MOVE_TO_INDEX(ctx->sPlyInfo[ctx->uPly-1].mv)][0])) &&
+ (!IS_SAME_MOVE(mv, ctx->mvCounter[MOVE_TO_INDEX(ctx->sPlyInfo[ctx->uPly-1].mv)][1])) &&
+ (GetMoveFailHighPercentage(mv) <= 10))
+ {
+ ASSERT(!InCheck(&ctx->sPosition, ctx->sPosition.uToMove));
+ uDepthPly = MINU(uRemainingDepth / ONE_PLY, MAX_PLY_PER_SEARCH);
+ uMoveIdx = MINU(uLegalMoves, LMR_TABLE_MAX_MOVES);
+
+ // Base reduction (matches the old fixed -ONE_PLY history-pruning
+ // behavior) plus a graded extra on top from the table, same
+ // structure as the prior validated implementation.
+ iReduction = -(ONE_PLY + g_iLMRQuietReduction[uDepthPly][uMoveIdx]);
+
+ // NOTE: tried an "improving" signal here (Crafty/Berserk/SF-style,
+ // comparing static eval to 2 plies ago), both with
+ // GetRoughEvalScore's material-only fallback and with a real
+ // Eval() call (LAZY_EVAL-fast-pathed) feeding pi->iEval -- both
+ // measured identically worse (23/30, EBF 4.252 vs this config's
+ // 24/30, EBF 4.223). The signal itself isn't paying off here, not
+ // just the eval-quality proxy; not worth pursuing further without
+ // a different formulation.
+
+ // Soft PV-adjacency discount (Crafty-style): if the parent node
+ // (whose move loop we're in) was itself a genuine PV node, reduce
+ // one ply less rather than skipping the reduction outright.
+ if (TRUE == ctx->sPlyInfo[ctx->uPly - 1].fPvNode)
+ {
+ iReduction += ONE_PLY;
+ if (iReduction > 0) iReduction = 0;
+ }
+ if (0 == iReduction) return(0);
+
+ // Never reduce past leaving less than TWO_PLY of remaining depth --
+ // a reduced child must still get a real search, not be treated as
+ // a leaf/qsearch node by accident.
+ if ((INT)uRemainingDepth + iReduction < TWO_PLY)
+ {
+ iReduction = -MAX((INT)uRemainingDepth - TWO_PLY, 0);
+ }
+ ASSERT(iReduction <= 0);
+ return(iReduction);
+ }
+ return(0);
+}