From fbb138cc1dd13da2f30129206cdcd3d128344f54 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Tue, 8 Sep 2026 16:50:55 -0700 Subject: Retire asm GetAttacks, recogn.c/fen.c bugfixes, misc bugfixes verified at parity Confirmed self-play regression traced to a stale test_vs_head.sh reference binary (typhoon_allbitboards/550ea81, deleted): every "vs head" comparison since 92fc412 (Sep 4) was checking new work against that fixed Sep-4 snapshot, never against real HEAD or the working tree. Rebuilt clean reference binaries directly from git and re-verified everything from scratch. This commit lands only the pieces confirmed safe against clean 434fa04 (fast st1 match, ~30-40 games, score ~0.44-0.55, consistent with parity; plus a DEBUG-build smoke test pass): - recogn.c, fen.c: real bugfixes - data.c, draw.c, ics.c: whitespace only - x64.asm: retires the asm GetAttacks implementation now that chess.h's GetAttacks macro unconditionally selects the already-verified-faster _GetAttacksBB bitboard version instead of a three-way build-flag toggle (GETATTACKS_BITBOARD/CROUTINES/asm default) - see.c, testsee.c: SEE/test-harness updates supporting that default - root.c: per-tier eval-exit reporting (super-lazy counters currently always read 0 -- accurate, since no super-lazy exit exists yet) - main.c: startup banner update, InitEval() call, TestRecogn() added to the #ifdef TEST self-test sequence - command.c: InitEval() DNA-reload hook, new qsearchfutility diagnostic - dynamic.c: minor changes - chess.h: the GetAttacks default change above, three FUTILITY_BASE_MARGIN_* compatibility aliases (all still equal to the original flat FUTILITY_BASE_MARGIN -- search.c has not been split into per-tier margins here), placeholder super-lazy counters, and an EvalPasserRaces -> _EvalPasserRacesAgainstLoneKings rename (confirmed byte-identical body) to match recogn.c's call site - eval.c: the same rename, plus a no-op InitEval() stub (nothing to initialize until the ROOK_FULL_HALF_OPEN_BONUS cache below exists) Deliberately NOT included: the full eval.c overhaul (~1770 lines) and search.c's qsearch-futility rework (~650 lines), including yesterday's loosened SUPER_LAZY_MARGIN_BY_ARMY/FUTILITY_BASE_MARGIN_BY_SOURCE tables. Reverting just those two tables while keeping the rest of the eval.c overhaul still lost badly to 434fa04 (0.20 over 10 games), so the regression isn't fully explained by the margins alone -- the eval.c overhaul needs careful, incremental re-verification against this commit as the new baseline, not a bulk re-apply. Full original work preserved in git stash (stash@{0} as of this commit) for that follow-up. Note: two pre-existing, position/state-dependent assertion crashes were found during this verification (util.c:1093 WalkPV, recogn.c:1359 _SanityCheckRecognizers), both reproducing on unmodified 434fa04 -- not introduced by anything here, not yet root-caused. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Ka9o3S2eKqh4jNfmxVZ6fH --- src/see.c | 177 +++++++++----------------------------------------------------- 1 file changed, 26 insertions(+), 151 deletions(-) (limited to 'src/see.c') diff --git a/src/see.c b/src/see.c index dc308d5..893a9bb 100755 --- a/src/see.c +++ b/src/see.c @@ -28,144 +28,17 @@ Revision History: pList->data[pList->uCount].uVal = (v); \ pList->uCount++; -void CDECL -SlowGetAttacks(IN OUT SEE_LIST *pList, - IN POSITION *pos, - IN COOR cSquare, - IN ULONG uSide) -/*++ - -Routine description: - - SlowGetAttacks is the C version of GetAttacks; it should be - identical to the GetAttacks code in x86.asm. The job of the - function is, given a position, square and side, to populate the - SEE_LIST with the locations and types of enemy pieces attacking - the square. - -Parameters: - - SEE_LIST *pList : list to populate - POSITION *pos : the board - COOR cSquare : square in question - ULONG uSide : side we are looking for attacks from - -Return value: - - void - ---*/ -{ - register ULONG x; - PIECE p; - COOR c; - int iIndex; - COOR cBlockIndex; - int iDelta; - static PIECE pPawn[2] = { BLACK_PAWN, WHITE_PAWN }; - static int iSeeDelta[2] = { -17, +15 }; - -#ifdef DEBUG - ASSERT(IS_ON_BOARD(cSquare)); - ASSERT(IS_VALID_COLOR(uSide)); - VerifyPositionConsistency(pos, FALSE); -#endif - pList->uCount = 0; - - // - // Check for pawns attacking cSquare - // - c = cSquare + (iSeeDelta[uSide]); - if (IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (p == pPawn[uSide]) - { - // - // N.B. Don't use ADD_ATTACKER here because we know we're - // at element zero. - // - pList->data[0].pPiece = p; - pList->data[0].cLoc = c; - pList->data[0].uVal = VALUE_PAWN; - pList->uCount = 1; - } - } - - c += 2; - if (IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (p == pPawn[uSide]) - { - ADD_ATTACKER(p, c, VALUE_PAWN); - } - } - - // - // Check for pieces attacking cSquare - // - for (x = pos->uNonPawnCount[uSide][0] - 1; - x != (ULONG)-1; - x--) - { - c = pos->cNonPawns[uSide][x]; - ASSERT(IS_ON_BOARD(c)); - - p = pos->rgSquare[c].pPiece; - ASSERT(p && !IS_PAWN(p)); - ASSERT(GET_COLOR(p) == uSide); - - iIndex = (int)c - (int)cSquare; - if (0 == (CHECK_VECTOR_WITH_INDEX(iIndex, GET_COLOR(p)) & - (1 << PIECE_TYPE(p)))) - { - continue; - } - - if (IS_KNIGHT_OR_KING(p)) - { - ASSERT(IS_KNIGHT(p) || IS_KING(p)); - ADD_ATTACKER(p, c, PIECE_VALUE(p)); - continue; - } - - // - // Check to see if there is a piece in the path from cSquare - // to c that blocks the attack. - // - iDelta = NEG_DELTA_WITH_INDEX(iIndex); - ASSERT(iDelta == -1 * CHECK_DELTA_WITH_INDEX(iIndex)); - ASSERT(iDelta != 0); - for (cBlockIndex = cSquare + iDelta; - cBlockIndex != c; - cBlockIndex += iDelta) - { - if (!IS_EMPTY(pos->rgSquare[cBlockIndex].pPiece)) - { - goto done; - } - } - - // - // Nothing in the way. - // - ADD_ATTACKER(p, c, PIECE_VALUE(p)); - - done: - ; - } -} - // -// board_representation/MIGRATION.md section 3: bbPieces-backed -// "who attacks square X" primitive, and a GetAttacks PoC built on -// it. Not wired into the GetAttacks macro yet -- see MIGRATION.md -// section 6 for the eventual toggle. Uses chess.h's FastFirstBit/ -// FastLastBit (static inline bsf/bsr wrappers) rather than the real -// out-of-line FirstBit/LastBit -- worth avoiding call overhead in a -// per-move-generated, per-node hot path like this one. +// board_representation/MIGRATION.md sections 3/6/7: bbPieces-backed +// "who attacks square X" primitive. _GetAttacksBB below is now the +// only GetAttacks implementation -- the old mailbox SlowGetAttacks/ +// asm GetAttacks were retired 2026-09-06 once all of section 7's +// retirement criteria (correctness sweep, isolated + whole-engine +// benchmarks, match_play.py gate) cleared. Uses chess.h's +// FastFirstBit/FastLastBit (static inline bsf/bsr wrappers) rather +// than the real out-of-line FirstBit/LastBit -- worth avoiding call +// overhead in a per-move-generated, per-node hot path like this one. // // (This file used to have its own static _BuildOccupiedBB here, // byte-for-byte identical to generate.c's _BuildFullOccupiedBB -- @@ -191,8 +64,8 @@ Routine description: Return a bitboard of every uSide knight/bishop/rook/queen/king that attacks cSquare in the current position, blockers included. - Pawns are deliberately excluded -- see GetAttacksBB, which handles - them the same 2-square-delta way SlowGetAttacks always has (already + Pawns are deliberately excluded -- see _GetAttacksBB, which handles + them the same 2-square-delta way the old mailbox code did (already O(1), nothing to improve). Knights and the king are pure O(1) table/delta lookups (no @@ -319,19 +192,21 @@ _GetAttacksBB(IN OUT SEE_LIST *pList, Routine description: - PROOF OF CONCEPT -- not called from anywhere yet, and not a - replacement for GetAttacks/SlowGetAttacks until section 4/5/6 of - board_representation/MIGRATION.md (correctness sweep, benchmark, - toggle) are done. Reproduces SlowGetAttacks's exact semantics - (same deliberately-approximate no-pin/no-en-passant contract) via - _WhoAttacksSquareBB instead of the O(non-pawn-piece-count) mailbox - walk -- pawns handled identically to SlowGetAttacks (2-square - delta, unchanged, already O(1)). - - Attacker order is not guaranteed to match SlowGetAttacks -- see() - sorts/heaps the list immediately after GetAttacks returns, so only - the *set* of attackers needs to match, not the sequence - (board_representation/MIGRATION.md section 4). + The only GetAttacks implementation as of 2026-09-06 -- the old + mailbox SlowGetAttacks/asm GetAttacks retired once all of + board_representation/MIGRATION.md section 7's retirement criteria + cleared (correctness sweep, isolated + whole-engine benchmarks, + match_play.py gate). Reproduces the old mailbox code's exact + semantics (same deliberately-approximate no-pin/no-en-passant + contract) via _WhoAttacksSquareBB instead of an + O(non-pawn-piece-count) mailbox walk -- pawns handled the same way + the mailbox version always did (2-square delta, unchanged, already + O(1)). + + Attacker order is not guaranteed to match the old mailbox + implementation -- see() sorts/heaps the list immediately after + GetAttacks returns, so only the *set* of attackers needs to match, + not the sequence (board_representation/MIGRATION.md section 4). Parameters: -- cgit v1.3