From 917bf1260ae217bdcbdbc11c2c37cdb5d3a8eae9 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Sat, 29 Aug 2026 00:30:53 -0700 Subject: 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 Claude-Session: https://claude.ai/code/session_01EortUUkDVpsfrbqshBJYJg --- src/generate.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'src/generate.c') 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; } -- cgit v1.3