summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-13 09:07:23 -0700
committerScott Gasch <[email protected]>2026-10-13 09:07:30 -0700
commit78881b8e6422b7980e5f71780ae4bed862b93e72 (patch)
treea433fdce69fd27359679ad91e0ebedd52e2506cd
parentf03bcac9ce3c6542310010bfbef2b126bb6ae861 (diff)
Revert "Minor changes; eval code was play tested as ~even."
This reverts commit f03bcac9ce3c6542310010bfbef2b126bb6ae861. Co-Authored-By: Claude Sonnet 5 <[email protected]>
-rw-r--r--src/GNUmakefile10
-rw-r--r--src/book.c550
-rw-r--r--src/eval.c201
-rwxr-xr-xsrc/eval_tune/match_play.py177
-rwxr-xr-xsrc/eval_tune/test_vs_head.sh3
-rw-r--r--src/main.c57
-rw-r--r--src/unix.c16
7 files changed, 463 insertions, 551 deletions
diff --git a/src/GNUmakefile b/src/GNUmakefile
index 9831428..5879d31 100644
--- a/src/GNUmakefile
+++ b/src/GNUmakefile
@@ -154,16 +154,6 @@ 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
diff --git a/src/book.c b/src/book.c
index 83385bf..4a39790 100644
--- a/src/book.c
+++ b/src/book.c
@@ -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("\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");
diff --git a/src/eval.c b/src/eval.c
index c747a8e..a909953 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -123,7 +123,7 @@ static ULONG CASTLE_AND_DEVELOPMENT_SCALER[32] =
// 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m
// Q2R Q2Rm
- 1, 2, 3, 3, 4, 5, 6, 7,
+ 0, 1, 2, 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, +38, +26, +17, +10, +6, +0 }, // black
- { +0, +0, +6, +10, +17, +26, +38, +0, +0 } // white
+ { +0, +0, +0, +48, +34, +22, +13, +9, +0 }, // black
+ { +0, +0, +9, +13, +22, +34, +48, +0, +0 } // white
};
// Note: x2
@@ -414,21 +414,6 @@ 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,
@@ -764,7 +749,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_ROOK + VALUE_BISHOP + VALUE_KING)
+ (VALUE_ROOK + VALUE_BISHOP + VALUE_BISHOP + VALUE_KING)
static ULONG KING_COUNTER_BY_ATTACK_PATTERN[32] =
{
@@ -832,7 +817,7 @@ static SCORE KING_QUEEN_PROXIMITY_DANGER[7] =
+0, -10, -18, -25, -32, -39, -55
};
-static SCORE KING_MISSING_ONE_CASTLE_OPTION = -25;
+static SCORE KING_MISSING_ONE_CASTLE_OPTION = -23;
// 2026-09-14: new term, not a retune of an existing one.
// king_safety_data/HANDOFF.md's regression found `back_flight` (an
@@ -974,9 +959,6 @@ 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),
@@ -1594,6 +1576,17 @@ 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);
@@ -3060,14 +3053,16 @@ 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.
+ // 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).
i = 0;
bb = pos->bbPawns[WHITE] | pos->bbPawns[BLACK];
while (IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
{
i -= (SCORE)DISTANCE(cSquare, cKing);
}
- iTotal += i;
+ iTotal += i * 2;
pEntry->u64ActionKey = u64Key;
pEntry->iActionBonus = iTotal;
@@ -5384,11 +5379,13 @@ 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,
@@ -5398,7 +5395,15 @@ 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.
+ // 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.
if (TotalNonPawnCount(pos, xColor) <= 2)
{
// Supporting-own-passer and out-of-action-in-KP-endgames,
@@ -5417,7 +5422,7 @@ Return value:
c,
iKingScore,
i,
- "king/passer position");
+ "king activity (passer support + in/out of action)");
}
return iKingScore;
}
@@ -5851,7 +5856,7 @@ Return value:
static void
_ReEvalPassers(IN OUT POSITION *pos,
- IN PAWN_HASH_ENTRY *pHash)
+ IN PAWN_HASH_ENTRY *pHash)
/**
Routine description:
@@ -5872,13 +5877,15 @@ 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);
@@ -5892,34 +5899,46 @@ 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);
- 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");
+ 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
+ //
// Consider distance from friend king / enemy king
+ //
i = (8 - DISTANCE(c, KingPosition(pos, uColor)));
if (((uColor == WHITE) &&
((KingPosition(pos, BLACK) >> 4) <= (c >> 4))) ||
@@ -5945,6 +5964,26 @@ 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
+ //
}
}
}
@@ -6031,64 +6070,6 @@ 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
@@ -6141,7 +6122,7 @@ Return value:
0,
ILLEGAL_COOR,
pos->iScore[uAhead],
- (0 != uAheadPawnCount) ? (SCORE)(uAheadPawnCount * 4) :
+ (0 != uAheadPawnCount) ? (SCORE)(uAheadPawnCount * 2) :
(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 af7f011..4eecfd6 100755
--- a/src/eval_tune/match_play.py
+++ b/src/eval_tune/match_play.py
@@ -64,19 +64,12 @@ _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.]+)\s*(?:seconds|s), saw (\d+) nodes "
- r"\((\d+) qnodes\) \(\s*([\d.]+)\s*(k?)nps\)\.")
+ r"^Searched for\s+([\d.]+) seconds, saw (\d+) nodes "
+ r"\((\d+) qnodes\) \(\s*([\d.]+) 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.]+)\s*(?:percent|%)\.")
+ r"^First move beta cutoff rate was\s+([\d.]+) 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 --
@@ -157,77 +150,23 @@ def _dbg(tag, msg):
print(f"[{tag}] {msg}", file=sys.stderr, flush=True)
-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,
+def sample_openings(pgn_path, n, min_ply=8, max_ply=20, seed=0,
cache_dir="opening_cache"):
- """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
+ """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
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(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)]
+ since repeated cycle.sh runs against the same pool shouldn't pay
+ this more than once."""
+ st = Path(pgn_path).stat()
key = hashlib.sha256(
- ("||".join(key_parts) + f"||{n}|{min_ply}|{max_ply}|{seed}").encode()
+ f"{pgn_path}|{st.st_mtime}|{st.st_size}|{n}|{min_ply}|{max_ply}|{seed}"
+ .encode()
).hexdigest()[:16]
cache_path = Path(cache_dir) / f"openings_{key}.txt"
if cache_path.exists():
@@ -237,36 +176,49 @@ def sample_openings(pgn_paths, n, min_ply=8, max_ply=20, seed=0,
return openings
rng = random.Random(seed)
- 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)
-
+ file_size = st.st_size
openings = []
attempts = 0
max_attempts = n * 50 + 200 # generous; each attempt is one seek+one game parse
- file_handles = [open(p, "rb") for p in pgn_paths]
- try:
+ with open(pgn_path, "rb") as fb:
while len(openings) < n and attempts < max_attempts:
attempts += 1
- 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()
+ 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())
if len(openings) < n:
print(f"warning: only found {len(openings)} openings "
- f"(wanted {n}) across {pgn_paths}", file=sys.stderr)
+ f"(wanted {n}) in {pgn_path}", file=sys.stderr)
cache_path.parent.mkdir(parents=True, exist_ok=True)
cache_path.write_text("\n".join(openings) + "\n" if openings else "")
@@ -811,16 +763,7 @@ 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, 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("--pgn", required=True)
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 "
@@ -894,27 +837,7 @@ 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
@@ -970,7 +893,7 @@ def main():
candidate_engine = str(Path(args.candidate_engine).resolve())
n_pairs = args.games // 2
- openings = sample_openings(args.pgn, n_pairs, seed=args.opening_seed)
+ openings = sample_openings(args.pgn, n_pairs)
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 dbddf10..6f37039 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/lichess*.pgn \
+ --pgn ../../pgn/twic_filtered.pgn \
--games ${GAMES} \
--workers 18 \
--post \
@@ -113,4 +113,5 @@ python3 ./match_play.py \
${SPRT_ARGS} \
--scratch /usr/local/tmp/typhoon_match_${LABEL} \
--log ${LABEL}.log \
+ --pgn-out ../${LABEL}_games.pgn \
"$@"
diff --git a/src/main.c b/src/main.c
index bde1801..89d41f4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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-2026, Scott Gasch ([email protected])\n");
+ Trace(" Copyright (C) 2000-2007, Scott Gasch ([email protected])\n");
Trace(" " REVISION);
Trace(" " COMPILER_STRING);
Trace(" Make profile used: %s\n", PROFILE);
@@ -72,7 +72,13 @@ Return value:
Trace(" Evaluation timing\n");
#endif
#ifdef PERF_COUNTERS
- Trace(" Performance counters enabled\n");
+ 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");
#endif
Trace(" Hash sizes: %u Mb (main), %u Mb / thread (pawn)\n",
(g_uHashTableSizeEntries * sizeof(HASH_ENTRY)) / MB,
@@ -144,7 +150,12 @@ Return value:
return(FALSE);
}
- // If the logfile we want to write already exists, just append to it.
+ // 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));
+
g_pfLogfile = fopen(g_Options.szLogfile, "wb+");
if (NULL == g_pfLogfile) {
Trace("WARNING: Can't open logfile %s for write (%s); "
@@ -441,19 +452,23 @@ 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);
- 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);
+ Trace("srand seed: %u\n", uSeed);
+ srand(uSeed);
+ }
InitializeOptions(argc, argv);
+ InitializeTreeDump();
InitializeEGTB();
InitializeSigSystem();
InitializeInteriorNodeRecognizers();
@@ -473,7 +488,6 @@ Return value:
InitializeHashSystem();
InitEval();
#ifdef MP
- Trace(" ...Multi-threaded search\n");
InitializeParallelSearch();
#endif
VERIFY(PreGameReset(TRUE));
@@ -678,12 +692,13 @@ 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:
@@ -771,10 +786,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())
@@ -782,5 +797,5 @@ Return value:
Trace("main: Main program cleanup code failed, aborting.\n");
exit(-1);
}
- exit(0);
+ exit(0);
}
diff --git a/src/unix.c b/src/unix.c
index 298473e..fdfce5f 100644
--- a/src/unix.c
+++ b/src/unix.c
@@ -1125,12 +1125,14 @@ 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);
}