summaryrefslogtreecommitdiff
path: root/src/testgenerate.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-15 14:43:17 -0700
committerScott Gasch <[email protected]>2026-10-15 14:43:17 -0700
commitac7572166bd24868fcaf5980a91bb82fe61ba677 (patch)
tree8c583c3c631690e752d62bff674733358b48d1e2 /src/testgenerate.c
parent993b6eeb2a9ac7b659c473b6883fa53af1ad3a0b (diff)
NNUE accumulator struct scaffolding; fix 3 bugs found by DEBUG+TEST; add that gate
Partial checkpoint on the nnue-accumulator-plumbing branch, before the MakeMove/UnmakeMove hookup itself: - chess.h: NNUE_ACCUMULATOR struct (256 floats, pre-activation, float32 for now -- quantization to int16 deliberately deferred until a real quantization-aware export pass exists, per docs/NNUE.md). Added the live, always-current SEARCHER_THREAD_CONTEXT.sNnueAccum[2] (one per color) and PLY_INFO.sNnueAccumBackup (the king-move full-recompute backup slot, DEBUG-only color tag declared for a later ASSERT). LIGHTWEIGHT_SEARCHER_CONTEXT also mirrors sNnueAccum[2] at the same relative position -- omitting that broke its byte-compatible-prefix contract with the full context the moment MakeMove/UnmakeMove will start touching it unconditionally (caught by this session's own TestSan self-test failure). - Combining DEBUG=1 and TEST=1 for the first time (apparently ever) while chasing that failure immediately turned up three more real, pre-existing bugs, all in self-test-only code whose assert-guarded invariants had never been checked with asserts actually compiled in: - board.c: IsBoardLegal now rejects an en passant square whose rank doesn't match the side to move (and verifies the double-moved pawn/empty origin square), closing a gap VALID_EP_SQUARE alone can't catch. - testgenerate.c: TestCapturesAndPromotionsGenerator's fixture table had a self-contradictory FEN this new IsBoardLegal check would have caught; replaced with a genuine Black-to-move en passant position. - testgenerate.c: TestFindUnblockedSquaresSpeed called _GenerateAllMoves directly instead of the GenerateMoves wrapper, skipping the DEBUG-only pStack->board[uPly] snapshot WouldGiveCheck's own ASSERT checks against. - precommit_check.sh / CLAUDE.md: added the DEBUG=1 TEST=1 combination as a permanent third pre-commit gate, given what its first-ever run turned up. Real cost (~235s of ~266s total, unoptimized build dominates), judged worth it. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/testgenerate.c')
-rw-r--r--src/testgenerate.c33
1 files changed, 32 insertions, 1 deletions
diff --git a/src/testgenerate.c b/src/testgenerate.c
index 95c5000..83c187d 100644
--- a/src/testgenerate.c
+++ b/src/testgenerate.c
@@ -772,6 +772,24 @@ TestFindUnblockedSquaresSpeed(void)
uNumMoves = MOVE_COUNT(ctx, uPly);
ASSERT(uNumMoves > 0);
+#ifdef DEBUG
+ // Calling _GenerateAllMoves directly (above), not the
+ // GenerateMoves wrapper, skips the DEBUG-only
+ // pStack->board[uPly] snapshot GenerateMoves normally takes
+ // (generate.c, right before it dispatches to a generator) --
+ // WouldGiveCheck's own ASSERT(PositionsAreEquivalent(...))
+ // checks the move it's given against exactly that snapshot,
+ // so without this, WouldGiveCheck below trips on whatever
+ // stale snapshot happened to be sitting in pStack->board[uPly]
+ // from some earlier, unrelated call. posBench never changes
+ // for the rest of this position's timed loop (no MakeMove
+ // happens here), so one copy here is sufficient, not one per
+ // iteration. 2026-10-15: found via the first DEBUG+TEST
+ // combined build this self-test suite had ever been run
+ // under.
+ memcpy(&(pStack->board[uPly]), &posBench, sizeof(POSITION));
+#endif
+
u64Total = 0;
for (uIter = 0; uIter < uCallsPerPosition; uIter++)
{
@@ -838,7 +856,20 @@ TestCapturesAndPromotionsGenerator(void)
"3Q4/1Q4Q1/4Q3/2Q4R/Q4Q2/3Q4/1Q4Rp/1K1BBNNk w - - 0 1",
"rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3",
"rnbqkbnr/pp1p1ppp/8/2pPp3/8/8/PPP1PPPP/RNBQKBNR w KQkq c6 0 3",
- "rnbqkbnr/pp1ppppp/8/2pP4/8/8/PPP1PPPP/RNBQKBNR b KQkq c6 0 2",
+ // 2026-10-15: this used to read "...b KQkq c6 0 2" -- an ep
+ // square on rank 6 is only reachable with WHITE to move (it
+ // records a BLACK pawn's just-played double push), so pairing
+ // it with "b" (Black to move) was self-contradictory and
+ // unreachable through real play. FenToPosition never rejected
+ // it (it isn't routed through SetRootPosition's IsBoardLegal
+ // gate), so the move generator ran en passant generation
+ // against a position whose ep square didn't correspond to
+ // the side to move at all -- see board.c's IsBoardLegal for
+ // the check that would now catch this same mistake if it
+ // arrived via `setboard`. Replaced with a genuine Black-to-
+ // move en passant position (White just played c2-c4, passing
+ // over c3; Black's pawn already on d4 can capture there).
+ "rnbqkbnr/ppp1pppp/8/8/2Pp4/8/PP1PPPPP/RNBQKBNR b KQkq c3 0 3",
"8/1P6/8/8/8/8/7p/1k4K1 w - - 0 1",
"8/1P6/8/8/8/8/7p/1k4K1 b - - 0 1",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1",