From 550ea81a5a2ee2561c3feb91dc55686f4d3cb872 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Fri, 4 Sep 2026 09:55:15 -0700 Subject: Add seescores diagnostic command, verify zero SEE-value drift Board-representation migration section 4 item 4: seescores (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 Claude-Session: https://claude.ai/code/session_01Jntky4yGUTyQVaGCXms4F2 --- src/command.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) (limited to 'src/command.c') 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 (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 + +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 \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, -- cgit v1.3