| Age | Commit message (Collapse) | Author |
|
Recalibrate iKingSwingP90 against the bitboard-rewritten
CountKingSafetyDefects (~1.28B samples via new CALIBRATE_POSITIONAL/
CALIBRATE_BASE_MARGIN/CALIBRATE_MARGIN_SAFETY diagnostic build flags,
board_representation/EVAL.md section 9). Add LAZY_EVAL_MIN_MATERIAL:
measured the regular lazy exit's real swing exceeding its own assumed
margin 20.6% of the time in near-bare-king endgames (vs <=0.36%
elsewhere) -- skip lazy eval entirely below that material floor.
Double the stale search.c/searchsup.c CountKingSafetyDefects extension
thresholds as a stopgap pending their own recalibration.
Eval hot-path trimming (measured via EVAL_TIME, ~1759 -> ~1386 avg
cycles/eval on a representative middlegame position):
- Pull _GetFileStormDefects out of EstimatePositionalScore's hot path
(cost more than the "cheap cached lookup" it was assumed to be,
running on ~90% of all Eval() calls).
- Add pos->bbOccupiedSide[2], incrementally maintained alongside
bbOccupied, so _BuildFriendlySideBB is a field read instead of a
6-term OR.
- Switch CoorFromBitBoardRank8ToRank1/Rank1ToRank8 to the existing
static-inline FastFirstBit/FastLastBit (same bsf/bsr instruction,
no call/ret overhead).
- Defer EvalPasserRaces' uRacerDist/fDontCountMeOut past its
no-passer early return.
- Remove the mailbox-era "max mobility in a row" term from
_EvalBishop/_EvalRook (no bitboard-mobility equivalent need for it).
- Simplify _EvalBishopPairs and rook file-openness/passer bonuses to
flat DNA-tunable constants instead of distance/pawn-count-scaled
tables, rook file-openness now a branchless bitboard-indexed lookup.
- Remove pos->cPiece (write-only, no reader anywhere).
- Collapse WHITE/BLACK mirror-branches (castle-rights block,
rook-trapped-in-corner) to color-indexed constants.
- Close the PAWN_BIT..KING_BIT gap (bits 7-3 -> bits 4-0), removing
the bvPattern >>= 3 before its KING_COUNTER_BY_ATTACK_PATTERN
lookup. This also fixes a real bug introduced earlier this session
when _WhoControlsSquareFast was converted to read these constants
directly: g_SwapTable is only [32][32], but the old bit values
(up to 0xF8) indexed far out of bounds on any attacked square --
data.c's InitializeSwapTable was always built assuming the bits
0-4 range this change now actually produces.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XxmVi2sTMwpPp4i6WYFjan
|
|
Now that king (the last piece writing it) has converted to a bitboard
accumulator, nothing writes rgSquare[c|8].bvAttacks any more -- deletes
the whole mechanism rather than leaving a known-dead struct around:
- chess.h: ATTACK_BITV union gone. SQUARE collapses from a
union-with-ATTACK_BITV to a plain {pPiece, uIndex} struct (the #pragma
pack(1) that only existed for ATTACK_BITV's bitfield layout goes too).
UNSAFE_FOR_ROOK/UNSAFE_FOR_QUEEN and the whole-word MINOR_XRAY_BIT/
ROOK_XRAY_BIT/QUEEN_XRAY_BIT constants deleted -- confirmed unused
(only ever referenced in stale comments, not code) now that every
consumer reads bbXAttacks bitboards directly. PAWN_BIT/MINOR_BIT/
ROOK_BIT/QUEEN_BIT/KING_BIT stay: they're a separate, still-live
local bit-packing scheme _EvalKing/_WhoControlsSquareFast use to
build a per-square attack-pattern index into KING_COUNTER_BY_
ATTACK_PATTERN/g_SwapTable, unrelated to the retired storage struct.
- eval.c: _ClearAttackTables drops its entire macro-unrolled,
128-square clearing loop (CLEAR_A_SQ/CLEAR_A_RANK/CLEAR_SHORT_RANK,
all deleted) -- it only ever existed to zero the old per-square
struct; clearing the 7 bbXAttacks accumulators is the whole function
now. The transitional _IsSquareAttackedByX/_IsSquareXrayedByX helpers
(minor/rook/queen/king, 8 functions total) are deleted outright, not
just simplified -- their only remaining purpose was bridging to the
now-gone struct, and their DEBUG cross-checks were explicitly
migration-only scaffolding, not a permanent invariant. Call sites
(_WhoControlsSquareFast, _EvalKing's bvAttack/bvXray/bvDefend) read
the bbXAttacks bitboards directly instead. _WhoControlsSquareFast
simplifies to a flat OR of 8 bitboard membership tests per color,
down from raw struct reads plus 7 helper calls each.
Found and fixed one real, pre-existing bug while doing this (flagged
and confirmed with the user before touching it, kept as its own
documented change rather than silently folded into the mechanical
rename): _EvaluateCandidatePasser's helper-pawn-safety gate read
rgSquare[c1+8].bvAttacks[...].uWholeThing, but this function runs from
_EvalPawns -- the first piece type Eval() evaluates each call, before
any non-pawn piece (or, since commit 57502d6 retired pawns' own
bvAttacks write, even pawns) has written anything there. That word has
therefore been unconditionally zero, and the gate unconditionally true
(a silent no-op), since 57502d6 landed -- not something today's cleanup
introduced. Left exactly as dead/unconditional (deleted the
now-meaningless condition, kept the body it always ran anyway) rather
than fixed, since a real fix changes eval scoring and deserves its own
before/after check, documented inline for a future session.
Verification: precommit_check.sh (self-test + DEBUG smoke test) passes.
tests/ecm_ringers.ep_ at sd10 vs. the immediately preceding commit:
solve parity holds exactly (10/11 both), and final (depth-10) node
counts are byte-identical for all 11 positions -- the bar for a change
meant to be purely mechanical, unlike king's own conversion. One
harmless artifact noted: ECM.750 has a different depth-6 *intermediate*
best move (a shallow tie-break flip) that already resolves to the
identical PV and node count by depth 7 and holds through depth 10 --
not chased further since the actual (depth-10) result matches exactly
and search is deterministic at --cpus 1.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XxmVi2sTMwpPp4i6WYFjan
|
|
Board-representation migration section 4 item 4: seescores <filename>
(command.c, registered alongside script/sd) reads setboard lines from
an EPD file (same convention tests/ecm*.ep_ already use), generates
legal moves per position, and prints (FEN, SAN move, SEE value) for
every capture -- meant to be run once per GetAttacksBB toggle state
and diffed, closing a gap TestGetAttacks's attacker-list comparison
can't: whether _GetAttacksBB's attacker lists, though set-identical to
asm GetAttacks, still produce identical SEE() output once fed through
_MinLegalPiece's exchange simulation.
Run against all three curated suites (747 captures total: 51 + 351 +
345), same-commit asm-vs-GETATTACKS_BITBOARD=1 A/B build (same pair
used for the earlier sd10 comparison) -- output byte-identical, zero
diff, on all three. Confirms _GetAttacksBB is correct all the way
through to the final SEE() score every move-ordering decision actually
uses, not just at the raw attacker-list level.
precommit_check.sh clean. Default (no flag) release binary restored
after testing.
Only match_play.py remains unmet before full retirement of the old
mailbox GetAttacks implementation (section 7) -- deliberately deferred,
not run this pass.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Jntky4yGUTyQVaGCXms4F2
|
|
Clean gmake GENETIC=1 PERF_COUNTERS=1 MP=1 SIXTYFOUR=1 build had 100
warnings; DEBUG=1 and TEST=1 builds had more once actually exercised.
- OFFSET_OF/CONTAINING_STRUCT (chess.h) and PTR_TO_ALLOC_HASH (unix.c)
truncated pointers through 32-bit ULONG before use in offset/hash
arithmetic on this 64-bit build -- routed through size_t instead.
- Diagnostic int<->void* round-trips (command.c, root.c, split.c,
sig.c, data.c, unix.c, util.c) widened/narrowed via size_t to avoid
implicit truncation.
- ABS_DIFF on unsigned COOR now casts to int before abs().
- Dropped -fexpensive-optimizations (GCC-only, clang silently ignores
it) from GNUmakefile.
- Removed genuinely dead variables (book.c, gamelist.c, split.c,
testgenerate.c, testhash.c).
- Guarded DEBUG/PERF_COUNTERS/_X86_-only variables and the
_CMEvidenceBucket helper under the #ifdef that actually reads them,
since ASSERT/EVAL_TERM/KEEP_TRACK_OF_FIRST_MOVE_FHs compile away
outside those builds.
- Added missing prototypes for SlidePawn, SlidePawnWithoutSigs,
SlidePieceWithoutSigs (move.c), previously undeclared in chess.h.
- Removed dead _SystemIsRoot (unix.c).
Verified via precommit_check.sh: TEST=1 self-test suite passes,
DEBUG=1 smoke test (10 random ECM positions, sd 4) passes with no
crashes/assertions, release build restored -- all three profiles now
build with zero warnings.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014Cmv11sJZqVfanrPh6UnWE
|
|
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
|
|
uPositional was a per-thread EWMA of abs(material - true score) used to
size lazy-eval and futility margins. It was history-derived (reflecting
whatever recent, unrelated positions looked like) rather than derived
from the position actually being margined, and its update/consumption
was tangled with EVAL_HASH (now disabled).
Eval() now takes an optional SCORE *piPositional out-param and fills it
in on every return path: exact (abs(material-delta)) on a full eval,
or an estimate from a new EstimatePositionalScore() on a lazy exit.
EstimatePositionalScore()'s two terms (king-safety-defect-bucketed, and
a flat residual for mobility/passers/everything else) are calibrated
from ~1.6M measured full-eval samples (p90 of the actual swing), not
guessed -- an initial guessed version measurably regressed ECM solve
rate (630 vs a 650 baseline at sn=4M); the recalibrated version is back
at parity (649/879).
search.c's qsearch futility now reads the value Eval() just computed
instead of the stale/shared ctx field. Also removes QSearchInDangerNoStandPat
and SideCanStandPat, dead since the danger-hash check that fed them was
already commented out (e08387a) -- they depended on the same enprise/
trapped-piece data this conversation is about to move off of
g_PositionHash entirely.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
|
|
|
|
|