From 1509b3e593258690cbe3eee761a54f105111fc72 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Fri, 28 Aug 2026 20:41:48 -0700 Subject: Rewire GetMoveFailHighPercentage: index by (cFrom, cTo, pMoved) instead of (cFrom, cTo, color), and expose sample size. MOVE_TO_INDEX (used elsewhere for the counter-move table) folds any two moves with the same from/to/color into one fail-high bucket -- e.g. a king shuffle and a queen sac to the same square/color shared a slot. New MOVE_TO_FH_INDEX uses the low 20 bits of mv.uMove (cFrom+cTo+pMoved), which already encodes color in pMoved's low bit, so this is strictly more granular for free. Table grown 0x20000 -> 0x100000 entries to match. Also adds an optional ULONG *puAttempts out-param so future callers can weight by sample confidence instead of trusting a percentage computed from as few as one observation. GetLMRReduction (proto-LMR, live at HEAD) is the only real consumer right now; verified against head_reference at sd10 across ecm_ringers/ecm_confident_quick/ecm_hard_quick: net +2 solves (88 vs 87 on confident_quick, 18 vs 17 on hard_quick), node counts flat within noise (-1.5%/+1.4%/+0.5%). --- src/chess.h | 2 +- src/dynamic.c | 24 ++++++++++++++++++------ src/searchsup.c | 2 +- 3 files changed, 20 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/chess.h b/src/chess.h index 3a78b2b..9722414 100755 --- a/src/chess.h +++ b/src/chess.h @@ -2782,7 +2782,7 @@ TestBitboards(void); extern ULONG g_HistoryCounters[14][128]; ULONG -GetMoveFailHighPercentage(MOVE mv); +GetMoveFailHighPercentage(MOVE mv, ULONG *puAttempts); void UpdateDynamicMoveOrdering(SEARCHER_THREAD_CONTEXT *ctx, diff --git a/src/dynamic.c b/src/dynamic.c index 33a673c..c6e86e3 100755 --- a/src/dynamic.c +++ b/src/dynamic.c @@ -44,7 +44,14 @@ extern double log(double); ULONG g_HistoryCounters[14][128]; SCORE g_iLMRQuietReduction[MAX_PLY_PER_SEARCH + 1][LMR_TABLE_MAX_MOVES + 1]; -#define FH_STATS_TABLE_SIZE (0x20000) + +// Keyed by (cFrom, cTo, pMoved) -- the low 20 bits of mv.uMove -- rather +// than MOVE_TO_INDEX's (cFrom, cTo, color) so that e.g. a king shuffle +// and a queen sac to the same square/color aren't folded into the same +// fail-high bucket. pMoved's low bit is already the color, so this +// subsumes MOVE_TO_INDEX's color term for free. +#define MOVE_TO_FH_INDEX(mv) ((mv).uMove & 0xFFFFF) +#define FH_STATS_TABLE_SIZE (0x100000) typedef struct _FH_STATS { @@ -197,7 +204,7 @@ Return value: **/ { - ULONG u = MOVE_TO_INDEX(mv); + ULONG u = MOVE_TO_FH_INDEX(mv); ULONG v = g_FailHighs[u].uWholeThing; ASSERT(DYN_IS_LOCKED); @@ -233,7 +240,7 @@ Return value: **/ { - ULONG u = MOVE_TO_INDEX(mv); + ULONG u = MOVE_TO_FH_INDEX(mv); ASSERT(DYN_IS_LOCKED); if (g_FailHighs[u].u16Attempts == 0xFFFF) @@ -827,8 +834,8 @@ Return value: -ULONG -GetMoveFailHighPercentage(IN MOVE mv) +ULONG +GetMoveFailHighPercentage(IN MOVE mv, OUT ULONG *puAttempts) /** Routine description: @@ -839,6 +846,10 @@ Routine description: Parameters: MOVE mv + ULONG *puAttempts : optional (may be NULL) -- receives the number of + observations the percentage is based on, so callers can weight + by sample confidence instead of trusting a percentage computed + from as few as one attempt. Return value: @@ -846,11 +857,12 @@ Return value: **/ { - ULONG u = MOVE_TO_INDEX(mv); + ULONG u = MOVE_TO_FH_INDEX(mv); ULONG n, d; n = g_FailHighs[u].u16FailHighs; d = g_FailHighs[u].u16Attempts; + if (puAttempts) *puAttempts = d; if (d == 0) { return(0); diff --git a/src/searchsup.c b/src/searchsup.c index b532d9a..5673bbe 100644 --- a/src/searchsup.c +++ b/src/searchsup.c @@ -227,7 +227,7 @@ Return value: ((ctx->uPly < 3) || (!IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-3][0]) && !IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-3][1]))) && - (GetMoveFailHighPercentage(mv) <= 10)) + (GetMoveFailHighPercentage(mv, NULL) <= 10)) { ASSERT(!InCheck(&ctx->sPosition, ctx->sPosition.uToMove)); return(-ONE_PLY); -- cgit v1.3