summaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-13 11:12:35 -0700
committerScott Gasch <[email protected]>2026-10-13 11:12:35 -0700
commit7400001fc2d43a507b54281f9860c60177860fe5 (patch)
tree0ab8e3d1bd5fe54cc7f2883e38da67c3bfac36d8 /src/main.c
parent78881b8e6422b7980e5f71780ae4bed862b93e72 (diff)
Add 5D FH% joint table (improve axis) + beta-side static-null-move
table, wire up static null-move pruning; misc searchsup.c tuning; --cpus=4 fix; multi-pool match_play.py openings. data.c: g_uJointFHThousandths grows a 5th "improve" axis ([12][11][11][15] -> [12][11][11][16][11]); new g_uBetaFHThousandths[12][21][11][11] table added. chess.h/searchsup.c/ search.c/split.c: minimal consumer wiring for the improve axis (ImproveBucket(), extended _JointTablePredictedFHThousandths signature/callers). search.c: new static (score-only, no verification search) null-move cutoff ahead of the existing real null-move check -- when iEval > iBeta by enough that the new beta-side table predicts a real null-move search would fail high with >95% confidence, take the cutoff on the table's say-so instead of paying for the verification search. searchsup.c: EstimateHangingMaterialCp now gives half credit when the mover has an unrelated piece already flagged trapped at node entry; bumped the SEE>=0 check extension from 3/4 ply to a full ply when the opponent has significant material left; removed the "entering the endgame extension" bonus (recapture extension and its window check untouched). main.c: accept "--opt=value" in addition to "--opt value" on the command line (a bare "--cpus=4" used to silently fail to match and leave uNumProcessors at its default of 1); also fixes a latent off-by-one that could read one past the end of argv for a value-taking flag given as the last command-line argument. unix.c: brace-scoping cleanup around signal handler setup, no behavior change. eval_tune/match_play.py, test_vs_head.sh: sample opening positions from multiple PGN pools (twic_filtered.pgn + lichess*.pgn) instead of one, for more opening diversity in match-play testing. Brought out of a larger stashed changeset one piece at a time, each verified independently against a --cpus 1 / --cpus 4 ratio benchmark to rule out the MP scaling regression that changeset originally triggered (see SCOTT_IS_A_MORON.md) -- no regression reproduced in any of the pieces here. Playing-strength impact measured as neutral via self-play (170 games, llr=-0.04, score=0.500) despite ~1 ply deeper average search. The stashed changeset's EFP margin rewrite, LMP threshold rewrite, and a disabled null-move-skip experiment are deliberately excluded -- those need their own careful, separate commits and testing. The stash's eval.c retuning is abandoned (measured ~even/worse over a ~700-game overnight self-play run). Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c95
1 files changed, 67 insertions, 28 deletions
diff --git a/src/main.c b/src/main.c
index 89d41f4..3c6352a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -306,80 +306,119 @@ Return value:
i = 1;
while(i < argc)
{
+ // Accept both "--opt value" (two argv tokens) and "--opt=value"
+ // (one token, '=' separated) -- a user typing --cpus=4 used to
+ // silently fail STRCMPI's exact-name match against "--cpus",
+ // fall through to the "unknown argument" branch, and leave
+ // uNumProcessors at its default of 1 with no indication why.
+ // Split any "--name=value" token into pszArg/pszInlineVal here
+ // so every branch below can keep comparing/consuming exactly
+ // as before; only the value-source (same token vs. next argv
+ // slot) changes.
+ char szArgNameBuf[SMALL_STRING_LEN_CHAR];
+ char *pszArg = argv[i];
+ char *pszInlineVal = NULL;
+ char *pEquals = strchr(argv[i], '=');
+
+ if ((NULL != pEquals) && ('-' == argv[i][0]))
+ {
+ size_t uNameLen = (size_t)(pEquals - argv[i]);
+ if (uNameLen < sizeof(szArgNameBuf))
+ {
+ strncpy(szArgNameBuf, argv[i], uNameLen);
+ szArgNameBuf[uNameLen] = '\0';
+ pszArg = szArgNameBuf;
+ pszInlineVal = pEquals + 1;
+ }
+ }
+ // The "value" argument for a flag that takes one: either the
+ // inline "=value" just split off above, or the next argv
+ // token -- and whether that next token is even present. Must
+ // check argc > i+1, not argc > i -- the while loop's own
+ // condition already guarantees i < argc, so "argc > i" was
+ // always true and let the two-token branches below read
+ // argv[i+1] one past the end of argv when a value-taking flag
+ // was the last argument on the command line.
+ FLAG fHaveVal = (NULL != pszInlineVal) || (argc > i+1);
+ char *pszVal = (NULL != pszInlineVal) ? pszInlineVal :
+ ((argc > i+1) ? argv[i+1] : NULL);
+ // Only the two-token form consumes an extra argv slot.
+ FLAG fConsumedNextArg = (NULL == pszInlineVal);
+
#ifdef MP
- if ((!STRCMPI(argv[i], "--cpus")) && (argc > i))
+ if ((!STRCMPI(pszArg, "--cpus")) && fHaveVal)
{
- g_Options.uNumProcessors = (ULONG)atoi(argv[i+1]);
+ g_Options.uNumProcessors = (ULONG)atoi(pszVal);
if ((g_Options.uNumProcessors == 0) ||
(g_Options.uNumProcessors > 64))
{
g_Options.uNumProcessors = 2;
}
- i++;
+ if (fConsumedNextArg) i++;
}
else
#endif
- if ((!STRCMPI(argv[i], "--command")) && (argc > i))
+ if ((!STRCMPI(pszArg, "--command")) && fHaveVal)
{
- ULONG uLen = (ULONG)strlen(argv[i+1]);
+ ULONG uLen = (ULONG)strlen(pszVal);
g_szInitialCommand = (CHAR *)SystemAllocateMemory(uLen + 3);
- strcpy(g_szInitialCommand, argv[i+1]);
+ strcpy(g_szInitialCommand, pszVal);
strcat(g_szInitialCommand, "\r\n");
- i++;
+ if (fConsumedNextArg) i++;
}
- else if ((!STRCMPI(argv[i], "--hash")) && (argc > i))
+ else if ((!STRCMPI(pszArg, "--hash")) && fHaveVal)
{
g_Options.uNumHashTableEntries =
- _ParseHashOption(argv[i+1],
+ _ParseHashOption(pszVal,
sizeof(HASH_ENTRY));
- i++;
+ if (fConsumedNextArg) i++;
}
- else if ((!STRCMPI(argv[i], "--egtbpath")) && (argc > i))
+ else if ((!STRCMPI(pszArg, "--egtbpath")) && fHaveVal)
{
- if (!strcmp(argv[i+1], "-")) {
+ if (!strcmp(pszVal, "-")) {
g_Options.szEGTBPath[0] = '\0';
} else {
- strncpy(g_Options.szEGTBPath, argv[i+1], SMALL_STRING_LEN_CHAR);
+ strncpy(g_Options.szEGTBPath, pszVal, SMALL_STRING_LEN_CHAR);
}
- i++;
+ if (fConsumedNextArg) i++;
}
- else if (!STRCMPI(argv[i], "--book") && argc > i)
+ else if (!STRCMPI(pszArg, "--book") && fHaveVal)
{
- if (!strcmp(argv[i+1], "-")) {
+ if (!strcmp(pszVal, "-")) {
g_Options.szBookName[0] = '\0';
} else {
- strncpy(g_Options.szBookName, argv[i+1], SMALL_STRING_LEN_CHAR);
+ strncpy(g_Options.szBookName, pszVal, SMALL_STRING_LEN_CHAR);
}
- i++;
+ if (fConsumedNextArg) i++;
}
- else if (!STRCMPI(argv[i], "--batch"))
+ else if (!STRCMPI(pszArg, "--batch"))
{
g_Options.fNoInputThread = TRUE;
}
- else if ((!STRCMPI(argv[i], "--dnafile")) && (argc > i))
+ else if ((!STRCMPI(pszArg, "--dnafile")) && fHaveVal)
{
- if (!ReadEvalDNA(argv[i+1]))
+ if (!ReadEvalDNA(pszVal))
{
UtilPanic(INITIALIZATION_FAILURE,
(void *)"Bad DNA file",
- (void *)&(argv[i+1]),
+ (void *)&(pszVal),
(void *)"read",
NULL,
__FILE__, __LINE__);
}
else
{
- Trace("Loaded dna file \"%s\"\n", argv[i+1]);
+ Trace("Loaded dna file \"%s\"\n", pszVal);
}
- i++;
+ if (fConsumedNextArg) i++;
}
- else if ((!STRCMPI(argv[i], "--logfile")) && (argc > i))
+ else if ((!STRCMPI(pszArg, "--logfile")) && fHaveVal)
{
- strncpy(g_Options.szLogfile, argv[i+1],
+ strncpy(g_Options.szLogfile, pszVal,
SMALL_STRING_LEN_CHAR);
- i++;
+ if (fConsumedNextArg) i++;
}
- else if (!STRCMPI(argv[i], "--help")) {
+ else if (!STRCMPI(pszArg, "--help")) {
Trace("Usage: %s [--batch] [--command arg] [--logfile arg] [--egtbpath arg]\n"
" [--dnafile arg] [--cpus arg] [--hash arg]\n\n"
" --batch : operate the engine without an input thread\n"