| Age | Commit message (Collapse) | Author |
|
QSearch mate-magnitude asserts, MATEMOVE PV display, --command truncation, and test.sh's stale egtbpath.
Several small, independent correctness fixes bundled together since
they were all exercised together through today's precommit_check.sh
and curated-suite runs:
- command.c: batch-mode's "Exhausted input" exit was exit(-1), which
truncates to 255 (an 8-bit status) and is indistinguishable from a
real crash's nonzero exit. Changed to exit(0) so debug_smoke_test.sh
can reliably tell a clean batch run apart from a crash by exit status
alone.
- dynamic.c: _NewKillerMove's slot[1] backfill from
mvNullmoveQuietRefutations[uPly] had no check that the backfilled
move differed from the move just placed in slot[0]. When they
coincided, both slots held the identical move, silently wasting a
killer slot in release builds (ASSERT is a no-op there) and tripping
_NewKillerMove's own IS_SAME_MOVE invariant in DEBUG builds. Fixed by
skipping the backfill on collision.
- search.c: removed two ASSERT(iBestScore > -NMATE) calls in QSearch
that encoded an invariant that isn't actually guaranteed -- at an
early full-width root iteration, or after aspiration-window widening
following repeated fail-highs, an ancestor frame's iAlpha/iBeta can
itself already be more extreme than -NMATE with no mate anywhere in
the line, so a legitimate fail-low placeholder or fail-high score can
land in mate-magnitude territory purely as a window artifact.
hash.c's storage path already treats any value <= -NMATE as a sound
upper bound regardless of origin, so this was a false invariant, not
a caught bug. Also: minor whitespace cleanup, an added ASSERT
documenting the futility-margin depth precondition it replaced a
redundant runtime check for, and PV/leaf-count bookkeeping on the
mate/draw-at-root leaf paths that was previously skipped.
- util.c: MATEMOVE sentinel moves weren't handled in PV-to-string
conversion, so a PV ending in a detected mate would either display
garbage or hit the same-move assert. Added an explicit "<#>" marker.
- test.sh: --egtbpath pointed at a nonexistent /egtb/three;/egtb/four;
/egtb/five; corrected to /zscratch/egtb, this box's actual EGTB
location.
- main.c/input.c: --command's initial-command buffer (g_szInitialCommand)
was a fixed 256-byte array; strncpy(..., SMALL_STRING_LEN_CHAR - 2)
silently truncated any longer --command string, and -- worse -- when
the source was long enough not to fit, strncpy doesn't null-terminate
the destination, so the immediately-following strcat(..., "\r\n") could
read/write past the buffer. Long move-replay command strings used
during this session's debugging hit the truncation directly (a ~600
char move list silently cut off mid-token, desyncing the input queue).
Changed g_szInitialCommand to a heap allocation sized to the actual
input length instead of a fixed cap.
- CLAUDE.md: documents the above (this file's own diff is prior
session's writeup of these same fixes, committed now alongside the
code).
All exercised together via precommit_check.sh (self-test suite + DEBUG
smoke test against random ecm.ep_ samples) and the sd10/sn5m curated
suite sweep run for the eval.c hand-tuning commit just before this one.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01M9ZDiJhiUajUxh95mTXCFJ
|
|
countermove promotion, retired hung-piece-escape and NumLeftoverMovesToSelect.
Full session was built on a "measure the pick, not the game" methodology:
aggregate solve counts on curated suites are too noisy to tune move-ordering
knobs against, so most decisions here came from per-move fail-high/alpha-raise
rates at much larger sample sizes (leftover FH% instrumentation, a zero-
selection-budget diagnostic that isolates a single best-of-remaining pick,
and evidence-bucket calibration), not solve-count deltas alone. See
CLAUDE.md's "Dynamic move ordering experiments" section for the reusable
methodology and generate.c's _ScoreAllMoves comment for the resulting
ordering hierarchy.
Changes:
- Added g_ContinuationHistory: same growth/decay math as the existing
g_HistoryCounters butterfly table, additionally keyed by the previous
move, so its magnitude is self-calibrated rather than a hand-picked
constant. Flat, sufficient response across a 256x scale sweep.
- Countermove-table matches now get a real GOOD_MOVE-tier promotion
(previously the table was write-only, tracked for stats but never read
for ordering), but only when the match's own accumulated
history+continuation evidence clears COUNTERMOVE_EVIDENCE_THRESHOLD
(10,000) -- a raw match with no track record was shown to perform
identically to an ordinary leftover (~0.6-0.85% FH), so promoting on
match alone would have repeated hung-piece-escape's mistake below.
- Retired hung-piece-escape's unconditional GOOD_MOVE-tier promotion.
Evidence-calibration showed the overwhelming majority of triggers (a
zero-evidence population 250-1000x larger than countermove's) performed
at the plain-leftover baseline -- the promotion was mostly free tier-
escape treatment for moves that hadn't earned it. Replaced with
FLEE_BONUS, a flat same-tier nudge inside SelectBestWithHistory (never
escapes GOOD_MOVE/leftover classification, unlike a generation-time
promotion) at the magnitude found to plateau a same-tier-nudge sweep.
- Retired NumLeftoverMovesToSelect (the depth-indexed budget on how many
leftover moves got a full selection scan before falling back to
unsorted order). search.c's main move loop now always fully selects --
the leftover pool was shown to contain real, findable signal a bailout
budget was discarding for a node-count savings that didn't hold up net-
net once measured by solve counts and fail-high rates rather than raw
node counts (noisy on small suites independent of this change).
- Collapsed leftover-move instrumentation from sorted/raw pairs down to a
single set now that "raw" (unsorted fallback) is structurally
impossible; kept the countermove evidence-bucket calibration counters
(ongoing check that COUNTERMOVE_EVIDENCE_THRESHOLD stays well-
calibrated); removed the contested-node A/B harness and hung-piece
evidence calibration now that the decisions they were built to inform
are made.
Net effect on the three curated suites (sd 10): solve counts wash (tied,
+1, -1 across ringers/confident/hard), leftover fail-high rate improved
consistently on all three (the intended, directly-measured target of this
work). Not yet validated beyond sd 10 -- an sn-based run or
eval_tune/match_play.py head-to-head gate is the natural next check before
leaning on this as a proven strength gain rather than a directionally-
sound, sd-10-clean change.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014XePz6Sk4qQsTaP2jVJWJu
|
|
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
|
|
head_reference protocol.
Realized while asking "where is this documented" that it wasn't --
scattered implicitly across lmr_testing/RESULTS.md and
head_reference/README.md but never stated as a methodology anywhere
durable. Also records the sd-vs-sn guidance (pick based on what the
change is expected to affect) and flags the specific gap that cost
real time this session: assuming a working tree's disabled-mechanism
state matched HEAD's actual committed behavior, when it didn't.
|
|
|