summaryrefslogtreecommitdiff
path: root/src/generate.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-08-29 00:30:53 -0700
committerScott Gasch <[email protected]>2026-08-29 00:30:53 -0700
commit917bf1260ae217bdcbdbc11c2c37cdb5d3a8eae9 (patch)
tree8013781f51e53eaf1fe3d4f45367e920d4c39786 /src/generate.c
parent366eebaf6b72fc6b56ea06a71f0276069a501dc7 (diff)
Switch to Crafty-style killer ordering; fix mvNullmoveRefutations type-mixing bug and add a quiet-refutation killer backfill.
Killer tiers now try both of this ply's own killers before either ply-2-back one, matching Crafty's ordering. Two earlier attempts at this same swap were reverted for regressing; this pass lands on top of NumLeftoverMovesToSelect (more SelectBestWithHistory budget to reach these lower-tier slots) and a real bug fix below, and beats interleaved order head-to-head on solves, node count, and first-move beta cutoff across the three curated suites. The bug: mvNullmoveRefutations's empty-killer-slot backfill could only ever contain a capturing move (TryNullmovePruning only wrote it inside the capture-refutation branch), but IS_SAME_MOVE's mask includes the pCaptured bits, so that backfilled value could never match a real quiet candidate -- the backfill was silently dead code. Fixed by recording genuinely quiet null-move refutations into a new, separate mvNullmoveQuietRefutations array (kept separate so it can't clobber the capture history mvNullmoveRefutations still needs for the Botvinnik-Markoff same-piece-two-squares extension check) and backfilling the regular killer table from that instead. The check-evasion killer table intentionally does *not* get this backfill: a null-move refutation can never legitimately be an escaping-check move (null moves can't deliver check), so backfilling there risks IS_SAME_MOVE cross-context false positives instead of the old guaranteed-inert no-op. Measured at sd10 across ecm_ringers/ecm_confident_quick/ecm_hard_quick against head_reference (commit d11e973): 115/191 solves (vs. 116 baseline), 924.36M total nodes (vs. 933.23M), first-move beta cutoff within 0.1-0.9 points of baseline on all three suites -- and clearly better than the same fix under interleaved order (113/191 solves, 963.10M nodes), which loses to head_reference on every metric. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EortUUkDVpsfrbqshBJYJg
Diffstat (limited to 'src/generate.c')
-rwxr-xr-xsrc/generate.c31
1 files changed, 22 insertions, 9 deletions
diff --git a/src/generate.c b/src/generate.c
index 7362fba..3e3c3a5 100755
--- a/src/generate.c
+++ b/src/generate.c
@@ -2467,20 +2467,33 @@ Return value:
//
//
- // Pre-populate killer/bonuses. (Tried Crafty-style ordering here
- // -- both of this ply's own killers before either ply-2-back one
- // -- measured worse EBF on ecm_quick than this interleaved order.
- // Back to interleaved as the known-good baseline.)
+ // Pre-populate killer/bonuses. Crafty-style ordering: both of this
+ // ply's own killers before either ply-2-back one. (Reverted twice
+ // before -- see git history -- but this pass is on top of both
+ // NumLeftoverMovesToSelect and the mvNullmoveQuietRefutations fix
+ // (dynamic.c/searchsup.c), and beats the interleaved order (this
+ // ply's killer1, ply-2's killer1, this ply's killer2, ply-2's
+ // killer2) head-to-head on every metric with the fix applied to
+ // both: more solves, fewer nodes, equal-or-higher first-move beta
+ // cutoff on 2 of 3 curated suites. Both orderings lose first-move
+ // cutoff rate vs. head_reference once the backfill is added -- that
+ // appears to be a cost of the backfill itself, not of tier order --
+ // but Crafty order is the one where the backfilled data lands in a
+ // tier (this ply's own killer[1], promoted to SECOND_KILLER here)
+ // that's otherwise structurally almost always empty, so it's pure
+ // upside there; under interleaved order the same backfill instead
+ // lands in THIRD_KILLER, behind two already-real proven killers,
+ // where it appears to cost more (misordering) than it gives.)
//
sKillers[0].mv = ctx->mvKiller[uPly][0];
sKillers[0].uBonus = FIRST_KILLER;
- sKillers[1].mv.uMove = sKillers[3].mv.uMove = 0;
- sKillers[2].mv = ctx->mvKiller[uPly][1];
- sKillers[2].uBonus = THIRD_KILLER;
+ sKillers[1].mv = ctx->mvKiller[uPly][1];
+ sKillers[1].uBonus = SECOND_KILLER;
+ sKillers[2].mv.uMove = sKillers[3].mv.uMove = 0;
if (uPly > 1)
{
- sKillers[1].mv = ctx->mvKiller[uPly - 2][0];
- sKillers[1].uBonus = SECOND_KILLER;
+ sKillers[2].mv = ctx->mvKiller[uPly - 2][0];
+ sKillers[2].uBonus = THIRD_KILLER;
sKillers[3].mv = ctx->mvKiller[uPly - 2][1];
sKillers[3].uBonus = FOURTH_KILLER;
}