diff options
| author | Scott Gasch <[email protected]> | 2026-09-04 09:55:15 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-04 09:55:15 -0700 |
| commit | 550ea81a5a2ee2561c3feb91dc55686f4d3cb872 (patch) | |
| tree | 29e70ecf9fd2e115754bca4635b9ddd493f9f33d | |
| parent | 5c3e541b3bf62704eb8950441646deacaea6f887 (diff) | |
Add seescores diagnostic command, verify zero SEE-value drift
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
| -rw-r--r-- | src/board_representation/MIGRATION.md | 26 | ||||
| -rwxr-xr-x | src/command.c | 132 |
2 files changed, 150 insertions, 8 deletions
diff --git a/src/board_representation/MIGRATION.md b/src/board_representation/MIGRATION.md index 313c74d..c6673ce 100644 --- a/src/board_representation/MIGRATION.md +++ b/src/board_representation/MIGRATION.md @@ -15,12 +15,13 @@ inline as a note rather than deleted, so a future session designing the separate migration doesn't have to rediscover it from git history. **Status: sections 1-3 (`bbPieces`/`bbPawns`-backed -`_WhoAttacksSquareBB`/`_GetAttacksBB`), section 4 items 1-3 and 5, and -section 6 (the `GETATTACKS_BITBOARD` toggle) all implemented, verified, -and committed -- including a whole-engine `sd10` check across all three -curated suites showing zero solve-count regression and +8.38% -aggregate NPS. Only `match_play.py` (section 4 item 6 / section 7) -remains unmet before full retirement of the old mailbox +`_WhoAttacksSquareBB`/`_GetAttacksBB`), all of section 4, and section 6 +(the `GETATTACKS_BITBOARD` toggle) implemented, verified, and +committed -- including a whole-engine `sd10` check across all three +curated suites (zero solve-count regression, +8.38% aggregate NPS) and +a `seescores` SEE-value diff across all 747 captures in those suites +(byte-identical, zero diff). Only `match_play.py` (section 4 item 6 / +section 7) remains unmet before full retirement of the old mailbox implementation, deliberately deferred for now. Section 5's whole-engine item and section 7's remaining items are otherwise done; section 8 not started.** See the per-section status notes below for specifics. @@ -461,7 +462,18 @@ sequence. ECM/random positions, run once per `GetAttacksBB` toggle state, diffed. Catches any behavioral drift `TestGetAttacks`'s raw-list comparison might miss once results feed into `_MinLegalPiece`'s - exchange simulation. Not started. + exchange simulation. **DONE.** `seescores <filename>` (`command.c`, + registered alongside `script`/`sd`) reads `setboard` lines from an + EPD file (the same convention `tests/ecm*.ep_` already use), + generates legal moves per position, and prints `(FEN, SAN move, SEE + value)` for every capture. Run against all three curated suites (747 + captures total: 51 + 351 + 345), once per binary from section 4 item + 5's same-commit asm-vs-`GETATTACKS_BITBOARD=1` A/B build -- output + **byte-identical, zero diff**, on all three. Confirms `_GetAttacksBB` + is correct not just at the attacker-list level (`TestGetAttacks`) + but all the way through `SEE()`/`_MinLegalPiece`'s exchange + simulation to the final score every move-ordering decision actually + uses. 5. **Full-suite behavioral check is mandatory here, not optional** -- this is the one place this migration is *riskier* than the Eval constant work: `GetAttacks` feeds move ordering and pruning decisions diff --git a/src/command.c b/src/command.c index a3361af..37686ed 100755 --- a/src/command.c +++ b/src/command.c @@ -1844,6 +1844,130 @@ COMMAND(SEECommand) } +COMMAND(SeeScoresCommand) +/** + +Routine description: + + board_representation/MIGRATION.md section 4 item 4: for every + position in <filename> (lines starting with "setboard ", the same + EPD convention the 'script' command's suite files use -- see + tests/ecm*.ep_), compute SEE() for every legal capture and print + (FEN, SAN move, SEE value) triples in generation order. Meant to + be run once per GetAttacksBB toggle state and diffed -- catches + any behavioral drift TestGetAttacks's attacker-*list* comparison + can't see, since that test only checks GetAttacks's raw output, + not what SEE()/_MinLegalPiece's exchange simulation does with it + downstream. + + Usage: + + seescores <required filename> + +Parameters: + + The COMMAND macro hides four arguments from the input parser: + + CHAR *szInput : the full line of input + ULONG argc : number of argument chunks + CHAR *argv[] : array of ptrs to each argument chunk + POSITION *pos : a POSITION pointer to operate on (unused -- + this command sets its own position per line of the file) + +Return value: + + void + +**/ +{ + FILE *pFile; + static CHAR szLine[SMALL_STRING_LEN_CHAR]; + SEARCHER_THREAD_CONTEXT *ctx; + CHAR *szFen; + CHAR *p; + MOVE mv; + ULONG u; + ULONG uPositions = 0; + ULONG uCaptures = 0; + + if (argc < 2) + { + Trace("Usage: seescores <required-filename>\n"); + return; + } + if (FALSE == SystemDoesFileExist(argv[1])) + { + Trace("Error (file doesn't exist): %s\n", argv[1]); + return; + } + pFile = fopen(argv[1], "rb"); + if (NULL == pFile) + { + Trace("Error (can't open file): %s\n", argv[1]); + return; + } + ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); + if (NULL == ctx) + { + fclose(pFile); + return; + } + + while (fgets(szLine, ARRAY_LENGTH(szLine), pFile)) + { + if (STRNCMPI(szLine, "setboard", 8)) + { + continue; + } + szFen = szLine + 8; + while (' ' == *szFen) + { + szFen++; + } + p = strchr(szFen, '\r'); + if (NULL != p) *p = '\0'; + p = strchr(szFen, '\n'); + if (NULL != p) *p = '\0'; + + if (FALSE == FenToPosition(&(ctx->sPosition), szFen)) + { + Trace("SEESCORES: skipping malformed FEN: %s\n", szFen); + continue; + } + uPositions++; + + mv.uMove = 0; + GenerateMoves(ctx, mv, + (InCheck(&(ctx->sPosition), ctx->sPosition.uToMove) ? + GENERATE_ESCAPES : GENERATE_ALL_MOVES)); + ASSERT(ctx->uPly == 0); + for (u = ctx->sMoveStack.uBegin[0]; + u < ctx->sMoveStack.uEnd[0]; + u++) + { + mv = ctx->sMoveStack.mvf[u].mv; + if (!mv.pCaptured) + { + continue; + } + if (MakeMove(ctx, mv)) + { + UnmakeMove(ctx, mv); + Trace("SEESCORES %s | %s | %d\n", + szFen, + MoveToSan(mv, &(ctx->sPosition)), + SEE(&(ctx->sPosition), mv)); + uCaptures++; + } + } + } + fclose(pFile); + SystemFreeMemory(ctx); + Trace("SEESCORES: %u position(s), %u capture(s) scored.\n", + uPositions, uCaptures); +} + + COMMAND(TestCommand) /** @@ -2404,11 +2528,17 @@ COMMAND_PARSER_ENTRY g_ParserTable[] = FALSE, "Run a script" }, { "sd", - SearchDepthCommand, + SearchDepthCommand, TRUE, FALSE, TRUE, "Set engine maximum search depth" }, + { "seescores", + SeeScoresCommand, + FALSE, + FALSE, + FALSE, + "Dump SEE() for every legal capture in a batch of EPD positions" }, { "set", SetCommand, TRUE, |
