diff options
| author | Scott Gasch <[email protected]> | 2026-10-13 08:59:29 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-13 08:59:29 -0700 |
| commit | f03bcac9ce3c6542310010bfbef2b126bb6ae861 (patch) | |
| tree | fb30befe5cdd08d126d38009a5a62ca3ef40e9f9 | |
| parent | 03545c4600df81b1a47dbc63fc606c065e857d8b (diff) | |
Minor changes; eval code was play tested as ~even.
| -rw-r--r-- | src/GNUmakefile | 10 | ||||
| -rw-r--r-- | src/book.c | 550 | ||||
| -rw-r--r-- | src/eval.c | 201 | ||||
| -rwxr-xr-x | src/eval_tune/match_play.py | 177 | ||||
| -rwxr-xr-x | src/eval_tune/test_vs_head.sh | 3 | ||||
| -rw-r--r-- | src/main.c | 57 | ||||
| -rw-r--r-- | src/unix.c | 16 |
7 files changed, 551 insertions, 463 deletions
diff --git a/src/GNUmakefile b/src/GNUmakefile index 5879d31..9831428 100644 --- a/src/GNUmakefile +++ b/src/GNUmakefile @@ -154,6 +154,16 @@ endif ifdef PERF_COUNTERS PROFILE += -DPERF_COUNTERS endif +# 2026-10-10: this flag was referenced (search.c's joint-table/marginal +# collection block, #ifdef FH_TABLE_COLLECTION) but never actually wired +# to a compiler define anywhere in this makefile -- every prior +# collection run must have been built some other way (or this gap +# predates the comment claiming it's "a live, in-progress collection +# run"). Requires PERF_COUNTERS=1 too (the block this gates is nested +# inside an outer #ifdef PERF_COUNTERS). +ifdef FH_TABLE_COLLECTION +PROFILE += -DFH_TABLE_COLLECTION +endif ifdef MOVE_ORDERING_EVIDENCE_COLLECTION PROFILE += -DMOVE_ORDERING_EVIDENCE_COLLECTION endif @@ -50,13 +50,13 @@ static BOOK_ENTRY *g_pMemBookHead = NULL; // ptr to 1st entry in membook // // Globals exported -// +// FLAG g_fTournamentMode = FALSE; // are we in tournament mode ULONG g_uBookProbeFailures = 0; // how many times have we missed ULONG g_uMemBookSize = 0x1000000; OPENING_NAME_MAPPING *g_pOpeningNames = NULL; // opening names file -static FLAG +static FLAG _VerifyBook(CHAR *szName) /** @@ -82,7 +82,7 @@ Return value: ULONG uLastMove; BOOK_ENTRY be; FLAG fMyRetVal = FALSE; - + fd = open(szName, O_RDONLY | O_BINARY); if (fd < 0) { @@ -129,7 +129,7 @@ Return value: goto end; } - // + // // Make sure the book is sorted by signature // if (be.u64Sig < u64LastSig) @@ -138,11 +138,11 @@ Return value: "(offset %u).\n", u, uPos); goto end; } - + // // If our sig is equal to the last entry's then the move here must // be greater than or equal to the last entry's move. - // + // else if (be.u64Sig == u64LastSig) { if (be.mvNext.uMove < uLastMove) @@ -152,12 +152,12 @@ Return value: goto end; } } - + u64LastSig = be.u64Sig; uLastMove = be.mvNext.uMove; } fMyRetVal = TRUE; - + end: if (-1 != fd) { @@ -186,7 +186,7 @@ Return value: **/ { ULONG u = 0; - + if (NULL == g_pOpeningNames) return(NULL); while(g_pOpeningNames[u].szString != NULL) { @@ -200,7 +200,7 @@ Return value: } -static void +static void _DumpUserOpeningList(void) /** @@ -223,8 +223,8 @@ Return value: **/ { - int fd = open(OPENING_LEARNING_FILENAME, - O_RDWR | O_CREAT | O_BINARY | O_RANDOM, + int fd = open(OPENING_LEARNING_FILENAME, + O_RDWR | O_CREAT | O_BINARY | O_RANDOM, _S_IREAD | _S_IWRITE); ULONG uNumRecords; ULONG u; @@ -234,20 +234,20 @@ Return value: // // If we failed to open it, abort. - // + // if (fd < 0) goto end; // // Stat it to see how many entries there are. - // - if (0 != stat(OPENING_LEARNING_FILENAME, &s)) + // + if (0 != stat(OPENING_LEARNING_FILENAME, &s)) { ASSERT(FALSE); - Trace("UpdateUserOpeningList: Corrupt %s file.\n", + Trace("UpdateUserOpeningList: Corrupt %s file.\n", OPENING_LEARNING_FILENAME); goto end; } - if ((s.st_size % sizeof(OPENING_LEARNING_ENTRY)) != 0) + if ((s.st_size % sizeof(OPENING_LEARNING_ENTRY)) != 0) { ASSERT(FALSE); Trace("UpdateUserOpeningList: Corrupt %s file.\n", @@ -256,13 +256,13 @@ Return value: } uNumRecords = s.st_size / sizeof(OPENING_LEARNING_ENTRY); Trace("Learned data on %u openings:\n\n", uNumRecords); - + // // Read each entry looking for a match. - // + // for (u = 0; u < uNumRecords; u++) { - if (sizeof(OPENING_LEARNING_ENTRY) != + if (sizeof(OPENING_LEARNING_ENTRY) != read(fd, &ole, sizeof(OPENING_LEARNING_ENTRY))) { Trace("UpdateUserOpeningList: Read error.\n"); @@ -276,7 +276,7 @@ Return value: p, ole.uWhiteWins, ole.uDraws, ole.uBlackWins); } } - + end: if (fd >= 0) { @@ -284,7 +284,7 @@ Return value: } } -FLAG +FLAG InitializeOpeningBook(void) /** @@ -303,7 +303,7 @@ Return value: return(TRUE); } -void +void ResetOpeningBook(void) /** @@ -322,7 +322,7 @@ Return value: g_uBookProbeFailures = 0; } -void +void CleanupOpeningBook(void) /** @@ -339,11 +339,11 @@ Return value: **/ { ULONG x = 0; - + // // If we have read an "opening names" list and created a dynamic memory // structure for it, free that now. - // + // if (NULL != g_pOpeningNames) { while (g_pOpeningNames[x].szString != NULL) @@ -356,10 +356,10 @@ Return value: SystemFreeMemory(g_pOpeningNames); g_pOpeningNames = NULL; } - + // // Cleanup membook if they quit in the middle of book building somehow - // + // if (NULL != g_pMemBook) { ASSERT(FALSE); @@ -368,7 +368,7 @@ Return value: } -static FLAG +static FLAG _BookOpen(void) /** @@ -386,17 +386,17 @@ Return value: { // // Need to have set the book name before calling this. - // + // if (!strlen(g_Options.szBookName)) { return(FALSE); } - + // // Open it. - // + // ASSERT(g_fdBook == -1); - g_fdBook = open(g_Options.szBookName, + g_fdBook = open(g_Options.szBookName, O_RDWR | O_CREAT | O_RANDOM | O_BINARY | _S_IREAD | _S_IWRITE); if (g_fdBook < 0) @@ -404,12 +404,12 @@ Return value: Bug("_BookOpen: Failed to open book \"%s\".\n", g_Options.szBookName); return(FALSE); } - + return((FLAG)(g_fdBook >= 0)); } -static void +static void _BookClose(void) /** @@ -433,7 +433,7 @@ Return value: } -static FLAG +static FLAG _BookSeek(ULONG n) /** @@ -465,7 +465,7 @@ Return value: { uPos = n * sizeof(BOOK_ENTRY); ASSERT(g_fdBook != -1); - + if (uPos != lseek(g_fdBook, uPos, SEEK_SET)) { Trace("_BookSeek: Cannot seek to byte offset %u\n", uPos); @@ -482,7 +482,7 @@ Return value: } #if 0 -static FLAG +static FLAG _BookWrite(BOOK_ENTRY *x) /** @@ -499,7 +499,7 @@ Return value: **/ { int iNum; - + // // If we are building the book, operate on memory // buffer... otherwise operate on the book file. @@ -511,11 +511,11 @@ Return value: else { ASSERT(g_fdBook != -1); - + iNum = write(g_fdBook, x, sizeof(BOOK_ENTRY)); if (sizeof(BOOK_ENTRY) != iNum) { - Trace("_BookWrite: I/O error on fd=%d, got %d and expected %d\n", + Trace("_BookWrite: I/O error on fd=%d, got %d and expected %d\n", g_fdBook, iNum, sizeof(BOOK_ENTRY)); ASSERT(FALSE); return(FALSE); @@ -525,7 +525,7 @@ Return value: } #endif -static FLAG +static FLAG _BookRead(BOOK_ENTRY *x) /** @@ -554,7 +554,7 @@ Return value: iNum = read(g_fdBook, x, sizeof(BOOK_ENTRY)); if (sizeof(BOOK_ENTRY) != iNum) { - Trace("_BookRead: I/O error on fd=%d, got %d and expected %d\n", + Trace("_BookRead: I/O error on fd=%d, got %d and expected %d\n", g_fdBook, iNum, sizeof(BOOK_ENTRY)); perror("_BookRead"); ASSERT(FALSE); @@ -565,7 +565,7 @@ Return value: } -static ULONG +static ULONG _BookCount(void) /** @@ -582,7 +582,7 @@ Return value: **/ { struct stat s; - + if (NULL != g_pMemBook) { return(g_uMemBookCount); @@ -593,7 +593,7 @@ Return value: { return(0); } - + if (0 != stat(g_Options.szBookName, &s)) { Trace("_BookCount: Stat failed.\n"); @@ -604,7 +604,7 @@ Return value: { if ((s.st_size % sizeof(BOOK_ENTRY)) != 0) { - Trace("_BookCount: Size is %u... not a mult of %u!\n", + Trace("_BookCount: Size is %u... not a mult of %u!\n", s.st_size, sizeof(BOOK_ENTRY)); return(0); } @@ -614,7 +614,7 @@ Return value: } #if 0 -static FLAG +static FLAG _BookAppend(BOOK_ENTRY *x) /** @@ -640,19 +640,19 @@ Return value: else { ASSERT(g_fdBook != -1); - + if (-1 == lseek(g_fdBook, 0, SEEK_END)) { ASSERT(FALSE); return(FALSE); } - return((FLAG)(sizeof(BOOK_ENTRY) == + return((FLAG)(sizeof(BOOK_ENTRY) == write(g_fdBook, x, sizeof(BOOK_ENTRY)))); } } #endif -static ULONG +static ULONG _BookFindSig(UINT64 u64Sig, ULONG *puLow, ULONG *puHigh) /** @@ -684,7 +684,7 @@ Return value: // if (0 == uTotalEntries) { - Trace("BookFindSig: Opening book \"%s\" is empty.\n", + Trace("BookFindSig: Opening book \"%s\" is empty.\n", g_Options.szBookName); goto end; } @@ -697,11 +697,11 @@ Return value: // // Perform a a binary search looking for an entry with a signature // that matches this position's. - // + // uLow = 0; uHigh = uTotalEntries - 1; - while ((uLow <= uHigh) && + while ((uLow <= uHigh) && (FALSE == fFound)) { uCurrent = (uLow + uHigh) / 2; @@ -718,7 +718,7 @@ Return value: uHigh = uCurrent - 1; } else - { + { ASSERT(u64Sig > entry.u64Sig); uLow = uCurrent + 1; } @@ -730,7 +730,7 @@ Return value: ASSERT(uCurrent >= 0); ASSERT(uCurrent < uTotalEntries); uRetVal = uCurrent; - + // // Ok, we matched a signature at current. Since there can be many // entries with the same checksum (many moves from the same board @@ -743,7 +743,7 @@ Return value: _BookSeek(uIndex); _BookRead(&entry); - if (entry.u64Sig != u64Sig) + if (entry.u64Sig != u64Sig) { uIndex++; #ifdef DEBUG @@ -766,7 +766,7 @@ Return value: { _BookSeek(uIndex); _BookRead(&entry); - if (entry.u64Sig != u64Sig) + if (entry.u64Sig != u64Sig) { uIndex--; #ifdef DEBUG @@ -793,19 +793,19 @@ static ULONG _BookComputeWeight(BOOK_ENTRY *e) Routine description: - + Given a book entry, compute its weight. The higher the more likely it will be chosen and played. Used in BookMove and BookEdit. The formula is: - + weight(mv) = winningness_coefficient * popularity_factor - + wins winningness_coefficient = --------------- (wins + losses) - + popularity_factor = wins + losses + draws - + Parameters: BOOK_ENTRY *e @@ -823,17 +823,17 @@ Return value: // // dWinning is 0.0 now. The only way it gets a value is if we've // seen this line win at least once. - // + // if (e->uWins > 0) { dWinning = e->uWins; dWinning /= (double)(e->uWins + e->uLosses); - + // // dWinning is now the ratio of wins to deterministic games // (games that were not draws). If this is less than 20% then // ignore the line (give the move a zero weight). - // + // if (dWinning < 0.20) return(0); } @@ -842,14 +842,14 @@ Return value: // computed and is non-zero. Compute the rest of the weight term // which is based on the popularity of the move (how many times it // has been played). - // + // dPopular = (double)(e->uWins + e->uLosses + e->uDraws); uRet = (ULONG)(dPopular * dWinning); return(uRet); } -static void +static void _QuickSortBook(ULONG uLeft, ULONG uRight) /** @@ -882,11 +882,11 @@ Return value: // u64Pivot = g_pMemBook[uLeft].u64Sig; uPivotSec = g_pMemBook[uLeft].mvNext.uMove; - + // - // Partition the space based on the pivot. Everything left of + // Partition the space based on the pivot. Everything left of // it is less, everything right is greater. - // + // uL = uLeft; uR = uRight; while (uL < uR) @@ -898,7 +898,7 @@ Return value: { uL++; } - + while (((g_pMemBook[uR].u64Sig > u64Pivot) || ((g_pMemBook[uR].u64Sig == u64Pivot) && (g_pMemBook[uR].mvNext.uMove > uPivotSec))) && @@ -906,7 +906,7 @@ Return value: { uR--; } - + if (uL < uR) { temp = g_pMemBook[uL]; @@ -915,14 +915,14 @@ Return value: } } uMid = uR; - + temp = g_pMemBook[uLeft]; g_pMemBook[uLeft] = g_pMemBook[uMid]; g_pMemBook[uMid] = temp; // // Recurse on the two halves - // + // if (uLeft != uMid) { _QuickSortBook(uLeft, uMid - 1); @@ -935,7 +935,7 @@ Return value: } -static void +static void _CompactMemBook(void) /** @@ -954,22 +954,22 @@ Return value: ULONG uCount = 0; ULONG i; ULONG uOccurances; - + Trace("Compacting the opening book... one moment.\n"); // // Lose the blank signatures to reduce the working set. // for (i = 0; - i < g_uMemBookSize; + i < g_uMemBookSize; i++) { - if (0 != g_pMemBook[i].u64Sig) + if (0 != g_pMemBook[i].u64Sig) { uOccurances = (g_pMemBook[i].uWins + g_pMemBook[i].uDraws + g_pMemBook[i].uLosses); - + if (0 != uOccurances) { memcpy(&(g_pMemBook[uCount]), @@ -988,7 +988,7 @@ Return value: } -static void +static void _StrainMemBook(ULONG uLimit) /** @@ -1009,8 +1009,8 @@ Return value: Trace("Straining out unpopular positions to compact buffer...\n"); - for (i = 0; - i < g_uMemBookSize; + for (i = 0; + i < g_uMemBookSize; i++) { if (0 != g_pMemBook[i].u64Sig) @@ -1021,22 +1021,22 @@ Return value: if (uOccurances <= uLimit) { - memset(&(g_pMemBook[i]), - 0, + memset(&(g_pMemBook[i]), + 0, sizeof(BOOK_ENTRY)); g_uMemBookCount--; } } } } - + //+---------------------------------------------------------------------------- // // Function: _BookHashFind // // Arguments: BOOK_ENTRY *p - pointer to a book entry -// +// // Returns: BOOK_ENTRY * - pointer to a membook location to store in // //+---------------------------------------------------------------------------- @@ -1074,7 +1074,7 @@ Return value: ASSERT(g_pMemBook != NULL); ASSERT(p->u64Sig); #endif - + while(1) { // @@ -1091,7 +1091,7 @@ Return value: { break; } - + // // Otherwise check the next book entry... if we go off the end // then loop around. @@ -1112,17 +1112,17 @@ Return value: -MOVE +MOVE BookMove(POSITION *pos, BITV bvFlags) /** Routine description: This is a bit confusing. - + The primary purpose of this function is, given a position pointer, to find and return a book move to play. - + This behavior can be achieved by calling with a good pos pointer and bvFlags containing BOOKMOVE_SELECT_MOVE. @@ -1132,7 +1132,7 @@ Routine description: If bvFlags contains BOOKMOVE_DUMP this function will trace a bunch of info about move weights. Hence, something like BookDump can be achieved via: - + (void)BookMove(pos, BOOKMOVE_DUMP); This is done so that the code to find all moves from a position @@ -1172,7 +1172,7 @@ Return value: memset(uMoveWeights, 0, sizeof(uMoveWeights)); #endif ctx = SystemAllocateMemory(sizeof(LIGHTWEIGHT_SEARCHER_CONTEXT)); - if (NULL == ctx) + if (NULL == ctx) { Trace("Out of memory.\n"); goto end; @@ -1183,7 +1183,7 @@ Return value: // indicates the opening book is currently in-memory and still // being built. // - if (g_pMemBook != NULL) + if (g_pMemBook != NULL) { Trace("Cannot probe the book while its still being built!\n"); ASSERT(FALSE); @@ -1206,13 +1206,13 @@ Return value: } ASSERT((uHigh - uLow) < 32); ASSERT(uLow <= uHigh); - + // // Consider each entry from iLow to iHigh and determine the chances // of playing it. // - for (uIndex = uLow, uMoveNum = 0; - uIndex <= uHigh; + for (uIndex = uLow, uMoveNum = 0; + uIndex <= uHigh; uIndex++, uMoveNum++) { _BookSeek(uIndex); @@ -1250,13 +1250,13 @@ Return value: UnmakeMove((SEARCHER_THREAD_CONTEXT *)ctx, entry.mvNext); continue; } - + u64Sig = (ctx->sPosition.u64PawnSig ^ ctx->sPosition.u64NonPawnSig); szNames[uMoveNum] = _BookLineToString(u64Sig); UnmakeMove((SEARCHER_THREAD_CONTEXT *)ctx, entry.mvNext); } - + // // If one is marked always, just return it. // @@ -1272,7 +1272,7 @@ Return value: // uMoveWeights[uMoveNum] = _BookComputeWeight(&entry); uTotalWeight += uMoveWeights[uMoveNum]; - + // // If we are in tournament mode we will play the top weighted // move; look for it now. @@ -1282,7 +1282,7 @@ Return value: uBestWeight = uMoveWeights[uMoveNum]; uBestIndex = uIndex; } - + } // next book move // @@ -1320,7 +1320,7 @@ Return value: if (bvFlags & BOOKMOVE_DUMP) { u64Sig = pos->u64PawnSig ^ pos->u64NonPawnSig; - Trace(" BookMove: List of moves in position 0x%" + Trace(" BookMove: List of moves in position 0x%" COMPILER_LONGLONG_HEX_FORMAT ":\n", u64Sig); for (x = 0; x < uMoveNum; x++) @@ -1328,18 +1328,18 @@ Return value: _BookSeek(uLow + x); _BookRead(&entry); ASSERT(entry.u64Sig == u64Sig); - + d = 0.0; if (uMoveWeights[x] != 0) { d = (double)uMoveWeights[x] / (double)uTotalWeight; d *= 100.0; } - Trace("%02u. %-4s [+%u =%u -%u] \"%s\" @ %5.3f\n", - x, + Trace("%02u. %-4s [+%u =%u -%u] \"%s\" @ %5.3f\n", + x, MoveToSan(entry.mvNext, pos), entry.uWins, entry.uDraws, entry.uLosses, - szNames[x], + szNames[x], d); } Trace(" Total weight was %u.\n\n", uTotalWeight); @@ -1354,8 +1354,8 @@ Return value: // Based on the weighted list and the random number we // selected pick a move. // - for (uIndex = 0, uCounter = 0; - uIndex < uMoveNum; + for (uIndex = 0, uCounter = 0; + uIndex < uMoveNum; uIndex++) { uCounter += uMoveWeights[uIndex]; @@ -1370,18 +1370,18 @@ Return value: } #ifdef DEBUG UtilPanic(SHOULD_NOT_GET_HERE, - NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, __FILE__, __LINE__); #endif end: g_Options.fShouldAnnounceOpening = fOldValue; _BookClose(); - + if (mvBook.uMove) { ASSERT(bvFlags & BOOKMOVE_SELECT_MOVE); - + // // We could be probing the book after a predicted ponder move (in // which case we will have forced fAnnounceOpening to FALSE because @@ -1393,8 +1393,8 @@ Return value: entry.uWins, entry.uDraws, entry.uLosses); } } - - if (NULL != ctx) + + if (NULL != ctx) { SystemFreeMemory(ctx); } @@ -1410,11 +1410,11 @@ Return value: // Synopsis: Giant hack to edit the book. // // Arguments: POSITION *pos -// +// // Returns: void // //+--------------------------------------------------------------------------- -void +void BookEdit(POSITION *pos) { int iTotalEntries = _BookCount(); @@ -1434,13 +1434,13 @@ BookEdit(POSITION *pos) FLAG fOldValue = g_Options.fAnnounceOpening; g_Options.fAnnounceOpening = FALSE; - + // // We cannot probe the opening book if membook is non-NULL (which // indicates the opening book is currently in-memory and still being // built. // - if (g_pMemBook != NULL) + if (g_pMemBook != NULL) { ASSERT(FALSE); goto end; @@ -1452,7 +1452,7 @@ BookEdit(POSITION *pos) iTotalEntries = _BookCount(); if (0 == iTotalEntries) { - Trace("BookDump: Opening book \"%s\" is empty.\n", + Trace("BookDump: Opening book \"%s\" is empty.\n", g_Options.szBookName); goto end; } @@ -1463,10 +1463,10 @@ BookEdit(POSITION *pos) Trace("BookDump: Could not open the book.\n"); goto end; } - + // // Open the learning recorder - // + // pfLearn = fopen(BOOK_EDITING_RECORD, "a+b"); if (NULL == pfLearn) { @@ -1479,25 +1479,25 @@ BookEdit(POSITION *pos) Trace("BookEdit: Position %I64x is not in the book.\n"); goto end; } - + iNumMoves = iHigh - iLow + 1; iCurrent = 0; do { printf("[H[J\n"); Trace("--EDITING OPENING BOOK--\n\n"); - Trace("There are %u positions in the opening book \"%s\".\n", + Trace("There are %u positions in the opening book \"%s\".\n", iTotalEntries, g_Options.szBookName); Trace("There are %u book moves in this position, %u to %u:\n", iNumMoves, iLow, iHigh); - + for (x = 0, iSum = 0; x < iNumMoves; x++) { _BookSeek(iLow + x); _BookRead(&entry); - + // // If the disabled flag is asserted, do not allow this // move to be played. If the disabled flag is not @@ -1514,14 +1514,14 @@ BookEdit(POSITION *pos) } iSum += iWeights[x]; } - + for (x = 0; x < iNumMoves; x++) { _BookSeek(iLow + x); _BookRead(&entry); - + fl = (float)iWeights[x]; if (iSum) { @@ -1537,8 +1537,8 @@ BookEdit(POSITION *pos) { Trace(" "); } - - Trace("%02u. %6u..%6u = %4s [+%6u =%6u -%6u] @ ~%6.3f%% | %s%s", + + Trace("%02u. %6u..%6u = %4s [+%6u =%6u -%6u] @ ~%6.3f%% | %s%s", x + 1, 0, iWeights[x], @@ -1549,7 +1549,7 @@ BookEdit(POSITION *pos) fl, (entry.bvFlags & FLAG_DISABLED) ? "NEV" : "", (entry.bvFlags & FLAG_ALWAYSPLAY) ? "ALW" : ""); - + if (x == iCurrent) { Trace("<<\n"); @@ -1559,7 +1559,7 @@ BookEdit(POSITION *pos) Trace("\n"); } } - + _BookSeek(iLow + iCurrent); _BookRead(&entry); Trace("Command (q, p, n, +, =, -, h, l, e, a, d, ?): "); @@ -1589,7 +1589,7 @@ BookEdit(POSITION *pos) _BookWrite(&entry); if (NULL != pfLearn) { - fprintf(pfLearn, "%I64x (%x) + %u // %s\n", + fprintf(pfLearn, "%I64x (%x) + %u // %s\n", entry.u64Sig, entry.mvNext.move, arg, @@ -1614,7 +1614,7 @@ BookEdit(POSITION *pos) entry.mvNext.move, arg, BoardToFEN(pos)); - } + } } SystemFreeMemory(pArg); break; @@ -1712,7 +1712,7 @@ BookEdit(POSITION *pos) end: g_Options.fAnnounceOpening = fOldValue; - + ASSERT(g_fdBook != -1); _BookClose(); @@ -1727,7 +1727,7 @@ BookEdit(POSITION *pos) -static FLAG +static FLAG _BookBuild(void) /** @@ -1763,10 +1763,10 @@ Return value: result.eResult != RESULT_DRAW) { return(FALSE); } - + // // If the membook has not yet been allocated, do it now. - // + // if (g_pMemBook == NULL) { ASSERT((g_uMemBookSize & (g_uMemBookSize - 1)) == 0); @@ -1788,7 +1788,7 @@ Return value: entry.u64Sig = q->u64PositionSigBeforeMove; entry.mvNext = q->mv; entry.u64NextSig = q->u64PositionSigAfterMove; - + // // Have we ever seen this position before? // @@ -1798,10 +1798,10 @@ Return value: // // Yes we have seen this signature-move before. Update the // win/draw/loss counter at this entry in the membook. - // + // ASSERT(pEntry->u64Sig == entry.u64Sig); ASSERT(pEntry->mvNext.uMove == entry.mvNext.uMove); - if (((result.eResult == RESULT_BLACK_WON) && + if (((result.eResult == RESULT_BLACK_WON) && (GET_COLOR(entry.mvNext.pMoved) == BLACK)) || ((result.eResult == RESULT_WHITE_WON) && (GET_COLOR(entry.mvNext.pMoved) == WHITE))) @@ -1820,21 +1820,21 @@ Return value: } } } - + // // Nope never seen this signature-move entry before, create a new // entry for it and add it to the membook. - // + // else { memset(pEntry, 0, sizeof(*pEntry)); pEntry->u64Sig = entry.u64Sig; pEntry->mvNext = entry.mvNext; pEntry->u64NextSig = entry.u64NextSig; - - if (((result.eResult == RESULT_BLACK_WON) && + + if (((result.eResult == RESULT_BLACK_WON) && (GET_COLOR(entry.mvNext.pMoved) == BLACK)) || - ((result.eResult == RESULT_WHITE_WON) && + ((result.eResult == RESULT_WHITE_WON) && (GET_COLOR(entry.mvNext.pMoved) == WHITE)) ) { pEntry->uWins = 1; @@ -1866,7 +1866,7 @@ Return value: { _StrainMemBook(n); n++; - + dFull = (double)g_uMemBookCount; dFull /= (double)g_uMemBookSize; dFull *= 100.0; @@ -1879,7 +1879,7 @@ Return value: } -static FLAG +static FLAG _BookToDisk(ULONG uStrainLimit) /** @@ -1932,15 +1932,15 @@ Return value: // // Create the temporary book on the disk. - // + // (void)unlink(szTempBook); if (SystemDoesFileExist(szTempBook)) { Trace("_BookToDisk: Can't create a temporary book!\n"); return(FALSE); } - - ifd = open(szTempBook, + + ifd = open(szTempBook, O_RDWR | O_CREAT | O_BINARY, _S_IREAD | _S_IWRITE); if (ifd < 0) @@ -1951,10 +1951,10 @@ Return value: #ifndef _MSC_VER fchmod(ifd, 0644); #endif - + // // Count the entries in the old book on disk. - // + // uOld_BookCount = 0; if ((TRUE == _VerifyBook(g_Options.szBookName)) && (TRUE == _BookOpen())) { @@ -1963,23 +1963,23 @@ Return value: uOld_BookCount = (s.st_size / sizeof(BOOK_ENTRY)); } } - + // // Merge the old book (on disk) with the new (in memory) in the // temp book. - // + // Trace("Merging book lines and writing to disk...\n"); do { pOldBook = pNewBook = NULL; - + // // Read the next old book entry. - // + // retry: if (uOldBook < uOld_BookCount) { - if ((int)(uOldBook * sizeof(BOOK_ENTRY)) != + if ((int)(uOldBook * sizeof(BOOK_ENTRY)) != lseek(g_fdBook, (uOldBook * sizeof(BOOK_ENTRY)), SEEK_SET)) { Trace("_BookToDisk: Cannot seek to byte offset %u in old " @@ -1989,7 +1989,7 @@ Return value: } if (sizeof(BOOK_ENTRY) != read(g_fdBook, - &sBe, + &sBe, sizeof(BOOK_ENTRY))) { Trace("_BookToDisk: Can't read error in old book, trying " @@ -1999,10 +1999,10 @@ Return value: } pOldBook = &sBe; } - + // // Get the next new book entry - // + // if (uNewBook < uNew_BookCount) { ASSERT(0 != g_pMemBook[uNewBook].u64Sig); @@ -2012,7 +2012,7 @@ Return value: // // Decide which to write into the merged (temporary) book we // are constructing next... - // + // if ((NULL == pNewBook) && (NULL == pOldBook)) { break; @@ -2057,15 +2057,15 @@ Return value: pNewBook->bvFlags |= pOldBook->bvFlags; uOldBook++; goto writenewbook; - } + } } } #ifdef DEBUG UtilPanic(SHOULD_NOT_GET_HERE, - NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, __FILE__, __LINE__); #endif - + writeoldbook: if (-1 == lseek(ifd, 0, SEEK_END)) { @@ -2078,7 +2078,7 @@ Return value: } uOldBook++; continue; - + writenewbook: if (-1 == lseek(ifd, 0, SEEK_END)) { @@ -2095,7 +2095,7 @@ Return value: } while(1); fMyRetVal = TRUE; - + end: close(ifd); SystemFreeMemory(g_pMemBook); @@ -2119,15 +2119,15 @@ Return value: // HACKHACK: put this in system, use CopyFile in win32 // #ifdef _MSC_VER - _snprintf(szCmd, 255, "move %s %s%c", szTempBook, + _snprintf(szCmd, 255, "move %s %s%c", szTempBook, g_Options.szBookName, 0); #else - snprintf(szCmd, 255, "/bin/mv %s %s%c", szTempBook, + snprintf(szCmd, 255, "/bin/mv %s %s%c", szTempBook, g_Options.szBookName, 0); #endif system(szCmd); } - + if (TRUE == fMyRetVal) { Trace("Book successfully built.\n"); @@ -2136,7 +2136,7 @@ Return value: } -static FLAG +static FLAG _LearnPgnOpenings(CHAR *szFilename, ULONG uStrainLimit) /** @@ -2164,22 +2164,22 @@ Return value: szFilename); return(FALSE); } - + if (0 != stat(szFilename, &s)) { Trace("LearnPgnOpenings: Can't read file \"%s\".\n", szFilename); return(FALSE); } - + pf = fopen(szFilename, "rb"); - if (NULL == pf) - { - Trace("LearnPgnOpenings: error reading file %s.\n", + if (NULL == pf) + { + Trace("LearnPgnOpenings: error reading file %s.\n", szFilename); return(FALSE); } - + CleanupHashSystem(); Trace("Stage 1: reading and parsing PGN\n"); @@ -2201,11 +2201,11 @@ Return value: SystemFreeMemory(p); } fclose(pf); - + _BookToDisk(uStrainLimit); - + InitializeHashSystem(); - + return(TRUE); } @@ -2214,8 +2214,8 @@ Return value: static void _UpdateUserOpeningList(INT iResult) { - int fd = open(OPENING_LEARNING_FILENAME, - O_RDWR | O_CREAT | O_BINARY | O_RANDOM, + int fd = open(OPENING_LEARNING_FILENAME, + O_RDWR | O_CREAT | O_BINARY | O_RANDOM, _S_IREAD | _S_IWRITE); ULONG uNumRecords; ULONG u; @@ -2225,20 +2225,20 @@ _UpdateUserOpeningList(INT iResult) // // If we failed to open it, abort. - // + // if (fd < 0) goto end; // // Stat it to see how many entries there are. - // - if (0 != stat(OPENING_LEARNING_FILENAME, &s)) + // + if (0 != stat(OPENING_LEARNING_FILENAME, &s)) { ASSERT(FALSE); - Trace("UpdateUserOpeningList: Corrupt %s file.\n", + Trace("UpdateUserOpeningList: Corrupt %s file.\n", OPENING_LEARNING_FILENAME); goto end; } - if ((s.st_size % sizeof(OPENING_LEARNING_ENTRY)) != 0) + if ((s.st_size % sizeof(OPENING_LEARNING_ENTRY)) != 0) { ASSERT(FALSE); Trace("UpdateUserOpeningList: Corrupt %s file.\n", @@ -2246,24 +2246,24 @@ _UpdateUserOpeningList(INT iResult) goto end; } uNumRecords = s.st_size / sizeof(OPENING_LEARNING_ENTRY); - + // // Read each entry looking for a match. - // + // for (u = 0; u < uNumRecords; u++) { - if (sizeof(OPENING_LEARNING_ENTRY) != + if (sizeof(OPENING_LEARNING_ENTRY) != read(fd, &ole, sizeof(OPENING_LEARNING_ENTRY))) { Trace("UpdateUserOpeningList: Read error.\n"); goto end; } - + if (ole.u64Sig == g_Options.u64OpeningSig) { // // Found it, so update and write back in place. - // + // switch (iResult) { case -1: @@ -2281,14 +2281,14 @@ _UpdateUserOpeningList(INT iResult) break; #endif } - + uPos = u * sizeof(OPENING_LEARNING_ENTRY); if (uPos != lseek(fd, uPos, SEEK_SET)) { Trace("UpdateUserOpeningList: Seek error.\n"); goto end; } - if (sizeof(OPENING_LEARNING_ENTRY) != + if (sizeof(OPENING_LEARNING_ENTRY) != write(fd, &ole, sizeof(OPENING_LEARNING_ENTRY))) { Trace("UpdateUserOpeningList: Write error.\n"); @@ -2296,10 +2296,10 @@ _UpdateUserOpeningList(INT iResult) goto end; } } - + // // We never found it, so add one at the end. - // + // memset(&ole, 0, sizeof(ole)); ole.u64Sig = g_Options.u64OpeningSig; switch(iResult) @@ -2319,19 +2319,19 @@ _UpdateUserOpeningList(INT iResult) break; #endif } - + if (-1 == lseek(fd, 0, SEEK_END)) { Trace("UpdateUserOpeningList: Seek error.\n"); goto end; } - - if (sizeof(OPENING_LEARNING_ENTRY) != + + if (sizeof(OPENING_LEARNING_ENTRY) != write(fd, &ole, sizeof(OPENING_LEARNING_ENTRY))) { Trace("UpdateUserOpeningList: Append error.\n"); } - + end: if (fd >= 0) { @@ -2350,11 +2350,11 @@ _UpdateUserOpeningList(INT iResult) // result of the game and the calibre of the opponent. // // Arguments: int iResult -// +// // Returns: void // //+---------------------------------------------------------------------------- -void +void BookLearn(INT iResult) { ULONG iIndex, x; @@ -2371,7 +2371,7 @@ BookLearn(INT iResult) } else if (g_Options.ePlayMode == I_PLAY_WHITE) { - Trace("BookLearn: I was playing white\n"); + Trace("BookLearn: I was playing white\n"); } if (iResult == -1) @@ -2387,7 +2387,7 @@ BookLearn(INT iResult) Trace("BookLearn: The game was a draw.\n"); } #endif - + // // Should we learn anything? // @@ -2421,7 +2421,7 @@ BookLearn(INT iResult) Trace("BookLearn: Loss by forfeit/abort, don't learn.\n"); goto end; } - + // // Not if we won but the opponent was weak. // @@ -2465,16 +2465,16 @@ BookLearn(INT iResult) // Yes, we have decided to learn. // Trace("BookLearn: Revising opening book line...\n"); - + // // 1. Save the learning on a user-friendly list... - // + // UpdateUserOpeningList(iResult); - + // // 2. Save the learning in the real opening book... - // - if ((0 == iTotalEntries) || + // + if ((0 == iTotalEntries) || (FALSE == _BookOpen())) { Trace("BookLearn: Could not open the book.\n"); @@ -2484,8 +2484,8 @@ BookLearn(INT iResult) // // Look at the moves in the move list. // - for (iIndex = 0; - iIndex < g_iMoveNum - 1; + for (iIndex = 0; + iIndex < g_iMoveNum - 1; iIndex++) { // @@ -2493,7 +2493,7 @@ BookLearn(INT iResult) // if (iIndex > 60) break; - Trace("BookLearn: Seeking %I64x with move %s (%x) in the book.\n", + Trace("BookLearn: Seeking %I64x with move %s (%x) in the book.\n", g_MoveList[iIndex].u64Sig, MoveToICS(g_MoveList[iIndex + 1].mv), g_MoveList[iIndex + 1].mv.move); @@ -2503,7 +2503,7 @@ BookLearn(INT iResult) // if (0 != _BookFindSig(g_MoveList[iIndex].u64Sig, &iLow, &iHigh)) { - Trace("BookLearn: Found a (P) : %I64x at %u.\n", + Trace("BookLearn: Found a (P) : %I64x at %u.\n", g_MoveList[iIndex].u64Sig, iCurrent); // @@ -2531,7 +2531,7 @@ BookLearn(INT iResult) // // Did the side making the move win in the end? // - if (((iResult == -1) && + if (((iResult == -1) && (IS_BLACK(g_MoveList[iIndex + 1].mv.data.pMoved))) || ((iResult == 1) && (IS_WHITE(g_MoveList[iIndex + 1].mv.data.pMoved)))) @@ -2589,13 +2589,13 @@ BookLearn(INT iResult) // Function: PreferPosition // // Synopsis: Given a position, love, hate or be neutral towards it. -// +// // If the position is to be loved, all other book entries // that lead to it will be flagged "always play". -// +// // If the position is to be hated, all other book entries // that lead to it will be flagged "never play". -// +// // A neutral setting will cause all other book entries that // lead to have their flags cleared. // @@ -2605,11 +2605,11 @@ BookLearn(INT iResult) // int i : -1 means hate // : 0 means neuter // : 1 means love -// +// // Returns: void // //+---------------------------------------------------------------------------- -void +void PreferPosition(POSITION *pos, FILE *fd, INT64 i64Targ, int i) { int iTotalEntries = _BookCount(); @@ -2618,15 +2618,15 @@ PreferPosition(POSITION *pos, FILE *fd, INT64 i64Targ, int i) static BOOK_ENTRY entry; ASSERT(pos->u64Sig == i64Targ); - - if (g_pMemBook != NULL) + + if (g_pMemBook != NULL) { ASSERT(FALSE); return; } iTotalEntries = _BookCount(); - + for (x = 0; x < iTotalEntries - 1; x++) @@ -2686,7 +2686,7 @@ PreferPosition(POSITION *pos, FILE *fd, INT64 i64Targ, int i) } } } - + printf("\n\n"); Trace("PreferPosition: Changed %u lines\n", iChanged); } @@ -2700,11 +2700,11 @@ PreferPosition(POSITION *pos, FILE *fd, INT64 i64Targ, int i) // positions. // // Arguments: char *szFile -// +// // Returns: FLAG // //+---------------------------------------------------------------------------- -FLAG +FLAG ImportEditing(char *szFile) { FILE *p = fopen(szFile, "r"); @@ -2719,39 +2719,39 @@ ImportEditing(char *szFile) int iIndex; BOOK_ENTRY entry; BOOK_ENTRY klobber; - + // // We cannot probe the opening book if membook is non-NULL (which // indicates the opening book is currently in-memory and still being // built. // - if (g_pMemBook != NULL) + if (g_pMemBook != NULL) { goto end; } - + // // Open the book. // iTotalEntries = _BookCount(); if (0 == iTotalEntries) { - Trace("BookMove: Opening book \"%s\" is empty.\n", + Trace("BookMove: Opening book \"%s\" is empty.\n", g_Options.szBookName); goto end; } - + if (FALSE == _BookOpen()) { Trace("BookMove: Could not open the book.\n"); goto end; } - + if (NULL != p) { while(fgets(szLine, 511, p)) { - if (_snscanf(szLine, 511, "%I64x (%x) %s", + if (_snscanf(szLine, 511, "%I64x (%x) %s", &u64Sig, &mv.move, &szOpcode) != 3) @@ -2763,27 +2763,27 @@ ImportEditing(char *szFile) Trace("Searching for %I64x move %x\n", u64Sig, mv.move); #endif - + if (_BookFindSig(u64Sig, &iLow, &iHigh)) { // // Walk forward looking for the right move. // memset(&entry, 0, sizeof(entry)); - for (iIndex = iLow; + for (iIndex = iLow; iIndex <= iHigh; iIndex++) { _BookSeek(iIndex); _BookRead(&entry); ASSERT(entry.u64Sig == u64Sig); - + if (entry.mvNext.move == mv.move) break; } // // Did we find the move? - // + // if (entry.mvNext.move != mv.move) { Trace("Move %x not present in book at position %I64x\n", @@ -2793,13 +2793,13 @@ ImportEditing(char *szFile) // // Yes. What should we do with it? - // + // if (!STRNCMP(szOpcode, "FLAGS", 5)) { - if (_snscanf(szLine, 511, "%I64x (%x) %s %x", + if (_snscanf(szLine, 511, "%I64x (%x) %s %x", &u64Sig, &mv.move, - &szOpcode, + &szOpcode, &iArg) == 4) { if (iArg >= 0) entry.bvFlags = iArg; @@ -2811,10 +2811,10 @@ ImportEditing(char *szFile) } else if (!STRNCMP(szOpcode, "+", 1)) { - if (_snscanf(szLine, 511, "%I64x (%x) %s %u", + if (_snscanf(szLine, 511, "%I64x (%x) %s %u", &u64Sig, &mv.move, - &szOpcode, + &szOpcode, &iArg) == 4) { if (iArg >= 0) entry.iWins = iArg; @@ -2826,10 +2826,10 @@ ImportEditing(char *szFile) } else if (!STRNCMP(szOpcode, "=", 1)) { - if (_snscanf(szLine, 511, "%I64x (%x) %s %u", + if (_snscanf(szLine, 511, "%I64x (%x) %s %u", &u64Sig, &mv.move, - &szOpcode, + &szOpcode, &iArg) == 4) { if (iArg >= 0) entry.iDraws = iArg; @@ -2841,10 +2841,10 @@ ImportEditing(char *szFile) } else if (!STRNCMP(szOpcode, "-", 1)) { - if (_snscanf(szLine, 511, "%I64x (%x) %s %u", + if (_snscanf(szLine, 511, "%I64x (%x) %s %u", &u64Sig, &mv.move, - &szOpcode, + &szOpcode, &iArg) == 4) { if (iArg >= 0) entry.iLosses = iArg; @@ -2854,18 +2854,18 @@ ImportEditing(char *szFile) Trace("Error\n"); } } - else + else { Trace("Error\n"); } - + // // Write it back to the book // _BookSeek(iIndex); _BookRead(&klobber); - if ((entry.u64Sig == + if ((entry.u64Sig == klobber.u64Sig) && (entry.mvNext.move == klobber.mvNext.move)) @@ -2884,8 +2884,8 @@ ImportEditing(char *szFile) #endif -static FLAG -_FindTerminalPositions(CHAR *szFilename, +static FLAG +_FindTerminalPositions(CHAR *szFilename, ULONG uPlyBeforeMate) /** @@ -2913,30 +2913,30 @@ Return value: FLAG fRet = FALSE; ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - if (NULL == ctx) + if (NULL == ctx) { Trace("Out of memory.\n"); goto end; } - + if (FALSE == SystemDoesFileExist(szFilename)) { Trace("FindTerminalPositions: file \"%s\" doesn't exist.\n", szFilename); goto end; } - + if (0 != stat(szFilename, &s)) { Trace("FindTerminalPositions: Can't read file \"%s\".\n", szFilename); goto end; } - + pf = fopen(szFilename, "rb"); - if (NULL == pf) - { - Trace("FindTerminalPositions: error reading file %s.\n", + if (NULL == pf) + { + Trace("FindTerminalPositions: error reading file %s.\n", szFilename); goto end; } @@ -2978,9 +2978,9 @@ Return value: } fclose(pf); fRet = TRUE; - + end: - if (NULL != ctx) + if (NULL != ctx) { SystemFreeMemory(ctx); } @@ -3009,7 +3009,7 @@ COMMAND(BookCommand) "\n"); return; } - + szOpcode = argv[1]; if (!STRNCMPI(szOpcode, "NAME", 4)) { @@ -3020,7 +3020,7 @@ COMMAND(BookCommand) { Trace("Usage: book name <required-filename>\n"); return; - } + } strncpy(g_Options.szBookName, argv[2], SMALL_STRING_LEN_CHAR); Trace("Opening book name set to \"%s\"\n", argv[2]); } @@ -3028,7 +3028,7 @@ COMMAND(BookCommand) { // // book dump : show book dump help - // + // if (argc == 2) { Trace("Available book dump commands:\n" @@ -3039,19 +3039,19 @@ COMMAND(BookCommand) return; } ASSERT(argc > 2); - + if (!STRNCMPI(argv[2], "LEARNING", 5)) { // // book dump learning : show book learning on stdout - // + // _DumpUserOpeningList(); } else { // // book dump moves : show book moves in this position - // + // BookMove(pos, BOOKMOVE_DUMP); } } @@ -3075,7 +3075,7 @@ COMMAND(BookCommand) { // // book tourn <t|f> : set tournament mode on or off - // + // if (tolower(*argv[2]) == 't') { g_fTournamentMode = TRUE; @@ -3089,16 +3089,16 @@ COMMAND(BookCommand) else { Trace("Usage: book tournamentmode [<true | false>]\n"); - } + } } } - + #if 0 else if (!STRNCMPI(argv[1], "EDIT", 4)) { // // book edit : edit book moves in this position - // + // BookEdit(pos); return(TRUE); } @@ -3113,33 +3113,33 @@ COMMAND(BookCommand) } _FindTerminalPositions(argv[2], 4); } - + else if (!STRNCMPI(argv[1], "OPENINGS", 8)) { // // book openings : read book opening names file - // + // if (argc == 2) { Trace("Usage: book openings <required-filename>\n"); return; } - + if (FALSE == SystemDoesFileExist(argv[2])) { Trace("Error (file doesn't exist): %s\n", argv[2]); return; } - + // Trace("%u opening lines named.\n", LearnOpeningNames(argv[2])); } - + else if (!STRNCMPI(argv[1], "IMPORT", 6)) { // // book import : create new book file from PGN -or- // apply editing / learning to a book file - // + // if (argc == 2) { Trace("Usage: book import <required-filename>\n"); @@ -123,7 +123,7 @@ static ULONG CASTLE_AND_DEVELOPMENT_SCALER[32] = // 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m // Q2R Q2Rm - 0, 1, 2, 3, 4, 5, 6, 7, + 1, 2, 3, 3, 4, 5, 6, 7, // na Q2R2m QR4m na Q2R3m na na full 7, 8, 8, 8, 8, 8, 8, 8, @@ -304,8 +304,8 @@ static SCORE PASSER_BY_RANK[2][9] = static SCORE CANDIDATE_PASSER_BY_RANK[2][9] = {// 0 1 2 3 4 5 6 7 8 - { +0, +0, +0, +48, +34, +22, +13, +9, +0 }, // black - { +0, +0, +9, +13, +22, +34, +48, +0, +0 } // white + { +0, +0, +0, +38, +26, +17, +10, +6, +0 }, // black + { +0, +0, +6, +10, +17, +26, +38, +0, +0 } // white }; // Note: x2 @@ -414,6 +414,21 @@ static SCORE UNDEVELOPED_MINORS_IN_OPENING[5] = // "Evaluation of Material Imbalance" static SCORE BISHOP_PAIR_BONUS = 30; +// Material-imbalance bonuses for a handful of "close looking but not +// actually equal" non-pawn shapes that _EvalBadTrades special-cases +// outright rather than running through its generic ahead/behind +// trade-encouragement logic (see _EvalBadTrades for why: that logic +// picks "ahead" by raw point value, which gets the direction or +// magnitude wrong for these specific shapes). All three are flat, +// position-independent nudges -- deliberately not scaled by pawn +// count/game phase the way the generic terms are, since the whole +// point is that these shapes are close enough that a big swing isn't +// warranted. DNA-tunable; starting values are hand judgment pending a +// tuning/match-play pass. +static SCORE QUEEN_VS_TWO_ROOKS_BONUS = 25; // favors the 2 rooks +static SCORE QUEEN_VS_THREE_MINORS_BONUS = 10; // favors the 3 minors +static SCORE QUEEN_VS_ROOK_PLUS_MINOR_BONUS = 20; // favors the queen + static SCORE STATIONARY_PAWN_ON_BISHOP_COLOR[128] = { +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0, @@ -749,7 +764,7 @@ static SCORE KING_TO_CENTER[128] = // the stay-home bonus now uses this same threshold, so "safety scored" // and "stay-home bonus applies" are exactly the same condition. #define DO_KING_SAFETY_THRESHOLD \ - (VALUE_ROOK + VALUE_BISHOP + VALUE_BISHOP + VALUE_KING) + (VALUE_ROOK + VALUE_ROOK + VALUE_BISHOP + VALUE_KING) static ULONG KING_COUNTER_BY_ATTACK_PATTERN[32] = { @@ -817,7 +832,7 @@ static SCORE KING_QUEEN_PROXIMITY_DANGER[7] = +0, -10, -18, -25, -32, -39, -55 }; -static SCORE KING_MISSING_ONE_CASTLE_OPTION = -23; +static SCORE KING_MISSING_ONE_CASTLE_OPTION = -25; // 2026-09-14: new term, not a retune of an existing one. // king_safety_data/HANDOFF.md's regression found `back_flight` (an @@ -959,6 +974,9 @@ static DNA_BASE_SIZE g_EvalDNA[] = { DNA_VAR(RACER_WINS_RACE), DNA_ARRAY(UNDEVELOPED_MINORS_IN_OPENING), DNA_VAR(BISHOP_PAIR_BONUS), + DNA_VAR(QUEEN_VS_TWO_ROOKS_BONUS), + DNA_VAR(QUEEN_VS_THREE_MINORS_BONUS), + DNA_VAR(QUEEN_VS_ROOK_PLUS_MINOR_BONUS), DNA_ARRAY(STATIONARY_PAWN_ON_BISHOP_COLOR), DNA_ARRAY(TRANSIENT_PAWN_ON_BISHOP_COLOR), DNA_ARRAY(BISHOP_MOBILITY_BY_SQUARES), @@ -1576,17 +1594,6 @@ Return value: pHash->iScore[uColor], PASSER_BY_RANK[uColor][RANK(c)], "passed pawn"); - - // However, don't give doubled passers such a big bonus. - if (IS_PAWN(pos->rgSquare[c - 16 * g_iAhead[uColor]].pPiece)) - { - EVAL_TERM(uColor, - PAWN, - c, - pHash->iScore[uColor], - -(PASSER_BY_RANK[uColor][RANK(c)] / 2), - "doubled passer"); - } return; } uSentries = FastCountBits(bb); @@ -3053,16 +3060,14 @@ Return value: // Out of action in KP-ish endgames, e.g. // 8/8/1p6/p6k/P3K3/8/1P6/8 w - - 0 11 -- how close this king is - // to the whole pawn mass. The *2 preserves the old formula's own - // scale (it used to be a differential, this is now a plain sum, - // but the per-pawn magnitude is unchanged). + // to the whole pawn mass. i = 0; bb = pos->bbPawns[WHITE] | pos->bbPawns[BLACK]; while (IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb))) { i -= (SCORE)DISTANCE(cSquare, cKing); } - iTotal += i * 2; + iTotal += i; pEntry->u64ActionKey = u64Key; pEntry->iActionBonus = iTotal; @@ -5379,13 +5384,11 @@ Return value: SCORE i; SCORE iKingScore = 0; - // // Come out. KING_TO_CENTER applies unconditionally (no longer // gated by uTotalNonPawnsBothSides<8 -- that was a different, // combined-both-sides unit than this whole split now uses, and // left a gap between "safety stops mattering" and "centralizing // is actually encouraged"; see _EvalKing's header comment). - // i = KING_TO_CENTER[c]; EVAL_TERM(uColor, KING, @@ -5395,15 +5398,7 @@ Return value: "centralize king"); // Sharper late-game guidance once the enemy is down to at most - // one non-king piece (lone king, K+minor, K+rook, even K+queen - // -- though a lone queen's own material value already exceeds - // DO_KING_SAFETY_THRESHOLD on its own, VALUE_QUEEN=975 > - // VALUE_ROOK+VALUE_BISHOP=800, so that specific case can never - // actually reach this function at all; the count check below is - // correct on its own terms regardless). Per-call/per-king - // (xColor's own material), not the old combined-both-sides - // uTotalNonPawnsBothSides==2 -- the two kings' own enemies can - // legitimately disagree now. + // one non-king piece (lone king, K+minor, K+rook, even K+queen. if (TotalNonPawnCount(pos, xColor) <= 2) { // Supporting-own-passer and out-of-action-in-KP-endgames, @@ -5422,7 +5417,7 @@ Return value: c, iKingScore, i, - "king activity (passer support + in/out of action)"); + "king/passer position"); } return iKingScore; } @@ -5856,7 +5851,7 @@ Return value: static void _ReEvalPassers(IN OUT POSITION *pos, - IN PAWN_HASH_ENTRY *pHash) + IN PAWN_HASH_ENTRY *pHash) /** Routine description: @@ -5877,15 +5872,13 @@ Return value: ULONG xColor; COOR c; COOR cSquare; + COOR cRank; BITBOARD bb; PIECE p; SCORE i; ULONG uNumPassers[2] = { 0, 0 }; int d1; - ASSERT(pHash->bbPasserLocations[WHITE] | - pHash->bbPasserLocations[BLACK]); - FOREACH_COLOR(uColor) { xColor = FLIP(uColor); @@ -5899,46 +5892,34 @@ Return value: while(IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bb))) { - // // Consider the control of the square in front of the passer - // + cRank = RANK(c); cSquare = c + d1; ASSERT(IS_ON_BOARD(cSquare)); u = _WhoControlsSquareFast(pos, cSquare); - if (u == xColor) - { - EVAL_TERM(uColor, - PAWN, - c, - pos->iScore[uColor], - -((PASSER_BY_RANK[uColor][RANK(c)] / 4) + - (PASSER_BY_RANK[uColor][RANK(c)] / 16)), - "enemy controls sq ahead"); - } - else if (u == (ULONG)-1) - { - p = pos->rgSquare[cSquare].pPiece; - if (!IS_EMPTY(p) && (OPPOSITE_COLORS(p, uColor))) - { - ASSERT(GET_COLOR(p) != uColor); - EVAL_TERM(uColor, - PAWN, - c, - pos->iScore[uColor], - -(PASSER_BY_RANK[uColor][RANK(c)] / 4), - "enemy occupies sq ahead"); - } - } -#ifdef DEBUG - else - { - ASSERT(u == uColor); - } -#endif + EVAL_TERM(uColor, + PAWN, + c, + pos->iScore[uColor], + (u == xColor) * + -((PASSER_BY_RANK[uColor][cRank] / 4)), + "enemy controls sq ahead"); + + // Square ahead is uncontrolled but occupied by xColor? + p = pos->rgSquare[cSquare].pPiece; + EVAL_TERM(uColor, + PAWN, + c, + pos->iScore[uColor], + ( + (u == (ULONG)-1) && + !IS_EMPTY(p) && + OPPOSITE_COLORS(p, uColor) + ) * + -(PASSER_BY_RANK[uColor][cRank] / 4), + "enemy occupies sq ahead"); - // // Consider distance from friend king / enemy king - // i = (8 - DISTANCE(c, KingPosition(pos, uColor))); if (((uColor == WHITE) && ((KingPosition(pos, BLACK) >> 4) <= (c >> 4))) || @@ -5964,26 +5945,6 @@ Return value: pos->iScore[uColor], i, "passer distance to kings"); - - // - // If a side is a piece down then the other side's passers - // are even more dangerous. - // - if (TotalNonPawnCount(pos, uColor) > - TotalNonPawnCount(pos, xColor)) - { - i = (PASSER_BY_RANK[uColor][RANK(c)] / 2); - EVAL_TERM(uColor, - PAWN, - c, - pos->iScore[uColor], - i, - "passer and piece up"); - } - - // - // TODO: For connected passers vs KR, consult the oracle - // } } } @@ -6070,6 +6031,64 @@ Return value: } // + // Q vs RR, Q vs MMM, and Q vs RM are three "close looking but not + // actually equal" non-pawn shapes that this routine's generic + // ahead/behind machinery below gets wrong -- it picks "ahead" by + // raw point value and hands that side a position/phase-scaled + // "keep trading" bonus (up to ~160 here), which is both sometimes + // the wrong side and wildly too large an opinion for shapes this + // close. Special-case all three to their own small flat + // (unscaled, pawn/phase-independent) bonus instead, then return + // before the generic logic ever runs. + // + // Q vs RR: rooks are a bit better (open files/ranks, strong in the + // endgame) -- small flat bonus to the rook side. Queen is uBehind + // here (975 < 2*500 makes the rooks uAhead by this routine's own + // value-based metric). + if ((1 == TotalNonPawnCount(pos, uBehind)) && + (1 == FastCountBits(pos->bbPieces[uBehind][QUEEN])) && + (2 == TotalNonPawnCount(pos, uAhead)) && + (2 == FastCountBits(pos->bbPieces[uAhead][ROOK]))) + { + EVAL_TERM(uAhead, 0, ILLEGAL_COOR, pos->iScore[uAhead], + QUEEN_VS_TWO_ROOKS_BONUS, "queen vs two rooks"); + return; + } + + // Q vs MMM: three minors need heavy coordination to use their + // nominal edge and are drawish in practice -- small flat bonus to + // the minors side. Queen is uAhead here (975 > 900 makes the + // queen uAhead by this routine's own value-based metric). + if ((1 == TotalNonPawnCount(pos, uAhead)) && + (1 == FastCountBits(pos->bbPieces[uAhead][QUEEN])) && + (3 == TotalNonPawnCount(pos, uBehind)) && + (0 == FastCountBits(pos->bbPieces[uBehind][ROOK])) && + (0 == FastCountBits(pos->bbPieces[uBehind][QUEEN]))) + { + EVAL_TERM(uBehind, 0, ILLEGAL_COOR, pos->iScore[uBehind], + QUEEN_VS_THREE_MINORS_BONUS, "queen vs three minors"); + return; + } + + // Q vs RM: queen's single-piece coordination (easier to combine + // with the king than a rook+minor pair trying to work together) + // and tactical resourcefulness outweighs the ~1 pawn point + // deficit (975 vs 800) slightly more often than not -- small flat + // bonus to the queen side. Queen is uAhead here (975 > 800 makes + // the queen uAhead by this routine's own value-based metric). + if ((1 == TotalNonPawnCount(pos, uAhead)) && + (1 == FastCountBits(pos->bbPieces[uAhead][QUEEN])) && + (2 == TotalNonPawnCount(pos, uBehind)) && + (1 == FastCountBits(pos->bbPieces[uBehind][ROOK])) && + (1 == (FastCountBits(pos->bbPieces[uBehind][KNIGHT]) + + FastCountBits(pos->bbPieces[uBehind][BISHOP])))) + { + EVAL_TERM(uAhead, 0, ILLEGAL_COOR, pos->iScore[uAhead], + QUEEN_VS_ROOK_PLUS_MINOR_BONUS, "queen vs rook+minor"); + return; + } + + // // Encourage the side that is ahead in piece material to continue // trading pieces and not to trade pawns. This also has the // effect of making the side that is behind in material want to @@ -6122,7 +6141,7 @@ Return value: 0, ILLEGAL_COOR, pos->iScore[uAhead], - (0 != uAheadPawnCount) ? (SCORE)(uAheadPawnCount * 2) : + (0 != uAheadPawnCount) ? (SCORE)(uAheadPawnCount * 4) : (TRUE == fAheadLoneMinorNoMate) ? -30 : 0, "don't trade pawns"); } diff --git a/src/eval_tune/match_play.py b/src/eval_tune/match_play.py index 4eecfd6..af7f011 100755 --- a/src/eval_tune/match_play.py +++ b/src/eval_tune/match_play.py @@ -64,12 +64,19 @@ _MOVE_RE = re.compile(r"^move (\S+)") # and against real engine output. A per-move stats block trails every # move, not just script-run suite summaries -- see EBF/first-move-beta # discussion in session history. +# Matches both the old report format ("Searched for 0.3 seconds, saw 123 +# nodes (45 qnodes) ( 969 nps).") and the new, more compact one ("Searched +# for 0.3s, saw 123 nodes (45 qnodes) (969knps)."): "seconds"/"s", and an +# optional "k" multiplier before "nps" (new format reports already-scaled +# thousands-of-nodes-per-second). _SEARCHED_RE = re.compile( - r"^Searched for\s+([\d.]+) seconds, saw (\d+) nodes " - r"\((\d+) qnodes\) \(\s*([\d.]+) nps\)\.") + r"^Searched for\s+([\d.]+)\s*(?:seconds|s), saw (\d+) nodes " + r"\((\d+) qnodes\) \(\s*([\d.]+)\s*(k?)nps\)\.") _DEPTH_RE = re.compile(r"^tellothers d(\d+),") +# Old: "First move beta cutoff rate was 93.681 percent." +# New: "First move beta cutoff rate was 93.681%." _FIRST_MOVE_BETA_RE = re.compile( - r"^First move beta cutoff rate was\s+([\d.]+) percent\.") + r"^First move beta cutoff rate was\s+([\d.]+)\s*(?:percent|%)\.") # 2026-09-18: per-iteration line UtilPrintPV (util.c) prints once per # completed depth during a move's search (both under-xboard "%2u %6d # %5u %-12llu %s" and human "%2u[+-]? %5s %-9s %-11llu %s" formats -- @@ -150,23 +157,77 @@ def _dbg(tag, msg): print(f"[{tag}] {msg}", file=sys.stderr, flush=True) -def sample_openings(pgn_path, n, min_ply=8, max_ply=20, seed=0, +def _sample_one_opening(fb, file_size, rng, min_ply, max_ply): + """One attempt at pulling a random opening position out of an + already-open pgn pool file: seek to a random byte offset, find the + next game's header block, parse just that one game, and walk a + random number of its plies. Returns a FEN, or None if this attempt + didn't land on a usable game (truncated tail, too-short game, etc.) + -- callers retry elsewhere on None rather than treating it as fatal.""" + offset = rng.randint(0, max(file_size - 1, 0)) + fb.seek(offset) + fb.readline() # discard partial line at the seek point + # scan forward to the start of the next game's header block + start = fb.tell() + line = fb.readline() + while line and not line.startswith(b"[Event "): + start = fb.tell() + line = fb.readline() + if not line: + return None # landed past the last game in the file; retry elsewhere + # read forward to the START of the game AFTER this one (or EOF) + fb.seek(start) + fb.readline() # consume this game's own "[Event " line + end = fb.tell() + line = fb.readline() + while line and not line.startswith(b"[Event "): + end = fb.tell() + line = fb.readline() + fb.seek(start) + raw = fb.read(end - start).decode("utf-8", errors="replace") + game = chess.pgn.read_game(io.StringIO(raw)) + if game is None: + return None + moves = list(game.mainline_moves()) + if len(moves) < min_ply: + return None + board = game.board() + ply = rng.randint(min_ply, min(max_ply, len(moves))) + for mv in moves[:ply]: + board.push(mv) + return board.fen() + + +def sample_openings(pgn_paths, n, min_ply=8, max_ply=20, seed=0, cache_dir="opening_cache"): - """Pick n random (roughly) opening positions out of a pgn pool by - seeking to random byte offsets and parsing just the one game found - there, rather than sequentially chess.pgn.read_game()-ing from the - start -- on a several-hundred-MB pool, replaying every game up to + """Pick n random (roughly) opening positions out of one or more pgn + pools by seeking to random byte offsets and parsing just the one game + found there, rather than sequentially chess.pgn.read_game()-ing from + the start -- on a several-hundred-MB pool, replaying every game up to the sample point (even while mostly skipping them) means real I/O and SAN-parsing cost that scales with how far into the file the first few accepted samples happen to land, which was observed taking minutes for as few as 2 requested openings. Cached like tune_eval_dna.py's position_cache, keyed on pgn identity + params, - since repeated cycle.sh runs against the same pool shouldn't pay - this more than once.""" - st = Path(pgn_path).stat() + since repeated cycle.sh runs against the same pool(s) shouldn't pay + this more than once. + + `pgn_paths` may name several pools at once (e.g. the TWIC game + archive alongside a curated opening-book PGN) to widen the set of + starting positions beyond whatever one file happens to contain -- + every match otherwise draws its "variety" from a single source, + which just means every match looks like that source's opening + repertoire. Each attempt picks a source file weighted by byte size + (a bigger pool has proportionally more distinct games to offer), so + pools aren't implicitly up- or down-weighted just by being listed + first or being much smaller than the others.""" + if isinstance(pgn_paths, (str, Path)): + pgn_paths = [pgn_paths] + stats = [Path(p).stat() for p in pgn_paths] + key_parts = [f"{p}|{st.st_mtime}|{st.st_size}" + for p, st in zip(pgn_paths, stats)] key = hashlib.sha256( - f"{pgn_path}|{st.st_mtime}|{st.st_size}|{n}|{min_ply}|{max_ply}|{seed}" - .encode() + ("||".join(key_parts) + f"||{n}|{min_ply}|{max_ply}|{seed}").encode() ).hexdigest()[:16] cache_path = Path(cache_dir) / f"openings_{key}.txt" if cache_path.exists(): @@ -176,49 +237,36 @@ def sample_openings(pgn_path, n, min_ply=8, max_ply=20, seed=0, return openings rng = random.Random(seed) - file_size = st.st_size + file_sizes = [st.st_size for st in stats] + total_size = sum(file_sizes) or 1 + # Precompute cumulative weights once for weighted source selection + # per attempt, rather than re-summing on every pick. + cum_weights = [] + running = 0 + for sz in file_sizes: + running += sz + cum_weights.append(running) + openings = [] attempts = 0 max_attempts = n * 50 + 200 # generous; each attempt is one seek+one game parse - with open(pgn_path, "rb") as fb: + file_handles = [open(p, "rb") for p in pgn_paths] + try: while len(openings) < n and attempts < max_attempts: attempts += 1 - offset = rng.randint(0, max(file_size - 1, 0)) - fb.seek(offset) - fb.readline() # discard partial line at the seek point - # scan forward to the start of the next game's header block - start = fb.tell() - line = fb.readline() - while line and not line.startswith(b"[Event "): - start = fb.tell() - line = fb.readline() - if not line: - continue # landed past the last game in the file; retry elsewhere - # read forward to the START of the game AFTER this one (or EOF) - fb.seek(start) - fb.readline() # consume this game's own "[Event " line - end = fb.tell() - line = fb.readline() - while line and not line.startswith(b"[Event "): - end = fb.tell() - line = fb.readline() - fb.seek(start) - raw = fb.read(end - start).decode("utf-8", errors="replace") - game = chess.pgn.read_game(io.StringIO(raw)) - if game is None: - continue - moves = list(game.mainline_moves()) - if len(moves) < min_ply: - continue - board = game.board() - ply = rng.randint(min_ply, min(max_ply, len(moves))) - for mv in moves[:ply]: - board.push(mv) - openings.append(board.fen()) + pick = rng.uniform(0, total_size) + idx = next(i for i, cw in enumerate(cum_weights) if pick <= cw) + fen = _sample_one_opening(file_handles[idx], file_sizes[idx], + rng, min_ply, max_ply) + if fen is not None: + openings.append(fen) + finally: + for fb in file_handles: + fb.close() if len(openings) < n: print(f"warning: only found {len(openings)} openings " - f"(wanted {n}) in {pgn_path}", file=sys.stderr) + f"(wanted {n}) across {pgn_paths}", file=sys.stderr) cache_path.parent.mkdir(parents=True, exist_ok=True) cache_path.write_text("\n".join(openings) + "\n" if openings else "") @@ -763,7 +811,16 @@ def main(): ap = argparse.ArgumentParser() ap.add_argument("head_engine", help="path to the baseline/head typhoon binary") ap.add_argument("candidate_engine", help="path to the candidate typhoon binary") - ap.add_argument("--pgn", required=True) + ap.add_argument("--pgn", required=True, nargs="+", + help="one or more pgn pools to sample starting " + "positions from (e.g. --pgn ../../pgn/" + "twic_filtered.pgn ../../pgn/self_play_games.pgn). " + "Each candidate opening's source file is picked " + "weighted by that file's byte size, so listing " + "several pools widens the set of starting " + "positions instead of implicitly favoring " + "whichever is listed first or is largest/" + "smallest.") ap.add_argument("--games", type=int, default=500, help="total games; rounded down to an even number " "since each opening is played as a pair " @@ -837,7 +894,27 @@ def main(): ap.add_argument("--log", default="match_history.log") ap.add_argument("--pgn-out", default="../self_play_games.pgn", help="every played game is appended here as PGN") + ap.add_argument("--opening-seed", type=int, default=None, metavar="N", + help="seed for opening-position sampling. Default: " + "a fresh random seed every run, so successive " + "invocations draw a DIFFERENT set of starting " + "positions from --pgn rather than silently " + "replaying whatever sample a previous run " + "already cached under opening_cache/ for these " + "exact (pgn, n, min_ply, max_ply) params -- that " + "caching exists to make a single run's sampling " + "cheap to redo (e.g. a crashed/resumed run), not " + "to pin one fixed opening set across unrelated " + "runs. Pass an explicit value to reproduce a " + "specific run's exact opening set.") args = ap.parse_args() + if args.opening_seed is None: + # SystemRandom, not random.Random/time-based: back-to-back runs + # (e.g. a shell loop launching several matches within the same + # second) must not collide on the same seed and silently resample + # the same "random" set. + args.opening_seed = random.SystemRandom().randrange(2**31) + print(f"OPENING_SEED={args.opening_seed}", file=sys.stderr) # --- resolve the per-side time controls once, loudly --------------- # Deliberately strict: a time-odds match that silently degrades into @@ -893,7 +970,7 @@ def main(): candidate_engine = str(Path(args.candidate_engine).resolve()) n_pairs = args.games // 2 - openings = sample_openings(args.pgn, n_pairs) + openings = sample_openings(args.pgn, n_pairs, seed=args.opening_seed) n_games = len(openings) * 2 jobs = [] diff --git a/src/eval_tune/test_vs_head.sh b/src/eval_tune/test_vs_head.sh index 6f37039..dbddf10 100755 --- a/src/eval_tune/test_vs_head.sh +++ b/src/eval_tune/test_vs_head.sh @@ -105,7 +105,7 @@ fi python3 ./match_play.py \ ${HEAD} \ ${CANDIDATE} \ - --pgn ../../pgn/twic_filtered.pgn \ + --pgn ../../pgn/twic_filtered.pgn ../../pgn/lichess*.pgn \ --games ${GAMES} \ --workers 18 \ --post \ @@ -113,5 +113,4 @@ python3 ./match_play.py \ ${SPRT_ARGS} \ --scratch /usr/local/tmp/typhoon_match_${LABEL} \ --log ${LABEL}.log \ - --pgn-out ../${LABEL}_games.pgn \ "$@" @@ -49,7 +49,7 @@ Return value: char *p; Trace("Typhoon %s (built on %s %s, commit %s):\n", VERSION, __DATE__, __TIME__, GIT_COMMIT); - Trace(" Copyright (C) 2000-2007, Scott Gasch ([email protected])\n"); + Trace(" Copyright (C) 2000-2026, Scott Gasch ([email protected])\n"); Trace(" " REVISION); Trace(" " COMPILER_STRING); Trace(" Make profile used: %s\n", PROFILE); @@ -72,13 +72,7 @@ Return value: Trace(" Evaluation timing\n"); #endif #ifdef PERF_COUNTERS - Trace(" Performance counters\n"); -#ifdef TEST_NULL - Trace(" Testing nullmove prediction algorithms\n"); -#endif -#endif -#ifdef DUMP_TREE - Trace(" Search tree dumpfile generation enabled\n"); + Trace(" Performance counters enabled\n"); #endif Trace(" Hash sizes: %u Mb (main), %u Mb / thread (pawn)\n", (g_uHashTableSizeEntries * sizeof(HASH_ENTRY)) / MB, @@ -150,12 +144,7 @@ Return value: return(FALSE); } - // If the logfile we want to write already exists, back it up. - if (TRUE == SystemDoesFileExist(g_Options.szLogfile)) { - VERIFY(BackupFile(g_Options.szLogfile)); - } - ASSERT(FALSE == SystemDoesFileExist(g_Options.szLogfile)); - + // If the logfile we want to write already exists, just append to it. g_pfLogfile = fopen(g_Options.szLogfile, "wb+"); if (NULL == g_pfLogfile) { Trace("WARNING: Can't open logfile %s for write (%s); " @@ -452,23 +441,19 @@ Return value: **/ { - { - // 2026-09-16 diagnostic: allow forcing a fixed srand seed via - // TYPHOON_SEED for reproducing a specific run bit-for-bit - // (there's no --seed command-line flag) -- e.g. re-running the - // same debug_smoke_test.sh repro file to check whether a - // failure is deterministic or a genuine SMP timing race. - // TEMPORARY: revert once the split.c investigation is done, - // this isn't meant to be a permanent CLI feature. - const char *pszForcedSeed = getenv("TYPHOON_SEED"); - unsigned int uSeed = pszForcedSeed ? - (unsigned int)strtoul(pszForcedSeed, NULL, 10) : - (unsigned int)time(0); - Trace("srand seed: %u\n", uSeed); - srand(uSeed); - } + // 2026-09-16 diagnostic: allow forcing a fixed srand seed via + // TYPHOON_SEED for reproducing a specific run bit-for-bit + // (there's no --seed command-line flag) -- e.g. re-running the + // same debug_smoke_test.sh repro file to check whether a + // failure is deterministic or a genuine SMP timing race. + // TEMPORARY: revert once the split.c investigation is done, + // this isn't meant to be a permanent CLI feature. + const char *pszForcedSeed = getenv("TYPHOON_SEED"); + unsigned int uSeed = pszForcedSeed ? + (unsigned int)strtoul(pszForcedSeed, NULL, 10) : + (unsigned int)time(0); + srand(uSeed); InitializeOptions(argc, argv); - InitializeTreeDump(); InitializeEGTB(); InitializeSigSystem(); InitializeInteriorNodeRecognizers(); @@ -488,6 +473,7 @@ Return value: InitializeHashSystem(); InitEval(); #ifdef MP + Trace(" ...Multi-threaded search\n"); InitializeParallelSearch(); #endif VERIFY(PreGameReset(TRUE)); @@ -692,13 +678,12 @@ void MainProgramLoop() { FLAG fPonder; while (!g_fExitProgram) { - // Prepare to search or ponder memcpy(&pos, GetRootPosition(), sizeof(POSITION)); fThink = fPonder = FALSE; result.eResult = RESULT_IN_PROGRESS; result.szDescription[0] = '\0'; - + // What should we do? switch(g_Options.ePlayMode) { case I_PLAY_WHITE: @@ -786,10 +771,10 @@ Return value: { g_uInputThreadHandle = InitInputSystemWithDedicatedThread(); } - + // Enter the main work loop code... - MainProgramLoop(); - + MainProgramLoop(); + // If we get here g_fExitProgram is set -- clean up. Trace("MAIN THREAD: thread terminating.\n"); if (FALSE == MainProgramCleanup()) @@ -797,5 +782,5 @@ Return value: Trace("main: Main program cleanup code failed, aborting.\n"); exit(-1); } - exit(0); + exit(0); } @@ -1125,14 +1125,12 @@ Return value: // doesn't get kicked out with EINTR by a signal that has nothing to // do with it. // - { - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = _AbortSearchOrTerminate; - sa.sa_flags = SA_RESTART; - sigemptyset(&sa.sa_mask); - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - } + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = _AbortSearchOrTerminate; + sa.sa_flags = SA_RESTART; + sigemptyset(&sa.sa_mask); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); return(TRUE); } |
