3 Copyright (c) Scott Gasch
11 General purpose utility code to support the engine.
19 $Id: util.c 355 2008-07-01 15:46:43Z scott $
25 extern ULONG g_uIterateDepth;
26 extern ULONG g_uFullWidthDepth;
28 #define STATE_UNKNOWN (0)
29 #define STATE_HEADER (1)
30 #define STATE_MOVELIST (2)
33 ReadNextGameFromPgnFile(FILE *pf)
38 Reads the next game in a PGN file and returns a pointer to it.
42 FILE *pf : open file pointer
46 CHAR * : NULL on error, else caller must free via SystemFreeMemory.
50 CHAR buf[MEDIUM_STRING_LEN_CHAR];
52 long lStartOfGame = -1;
56 ULONG uState = STATE_UNKNOWN;
61 if (fgets(buf, ARRAY_LENGTH(buf), pf) <= 0)
63 //Trace("fgets failed, errno=%u.\n", errno);
68 // Skip leading whitespace on this line...
71 while(*p && isspace(*p)) p++;
76 // We just read a blank line
84 uState = STATE_MOVELIST;
89 uBytes = lEndOfGame - lStartOfGame;
95 p = SystemAllocateMemory(uBytes + 1);
97 fseek(pf, lStartOfGame, SEEK_SET);
98 if (1 != fread(p, uBytes, 1, pf))
110 // We saw a non-blank line
121 uState = STATE_HEADER;
122 lStartOfGame = lBefore;
132 COMMAND(LearnPsqtFromPgn)
148 ULONG uPSQT[12][128];
162 Trace("Error (missing filename)\n");
165 szFilename = argv[1];
167 if (FALSE == SystemDoesFileExist(szFilename))
169 Trace("LearnPsqtFromPgn: file \"%s\" doesn't exist.\n",
174 if (0 != stat(szFilename, &s))
176 Trace("Stat failed.\n");
180 pf = fopen(szFilename, "rb");
183 Trace("LearnPsqtFromPgn: error reading file \"%s\".\n",
188 memset(uPSQT, 0, sizeof(uPSQT));
191 uHeap = GetHeapMemoryUsage();
193 while((pPGN = ReadNextGameFromPgnFile(pf)) != NULL)
195 d = (double)ftell(pf);
196 d /= (double)s.st_size;
198 printf("(%5.2f%% done)\r", d);
200 if (TRUE == LoadPgn(pPGN))
202 p = g_GameData.sMoveList.pFlink;
203 while(p != &(g_GameData.sMoveList))
205 q = CONTAINING_STRUCT(p, GAME_MOVE, links);
206 if ((q->uNumber > 10) &&
210 uPSQT[q->mv.pMoved - 2][q->mv.cTo] += 1;
215 SystemFreeMemory(pPGN);
218 ASSERT(uHeap == GetHeapMemoryUsage());
224 for (x = 0; x < 12; x++)
226 for (y = 0; y < 128; y++)
235 Trace("ULONG g_PSQT[12][128] = {\n");
236 for (x = 0; x < 12; x++)
238 Trace("// Piece 0x%x (%s)\n", x + 2, PieceAbbrev((PIECE)(x + 2)));
241 for (y = 0; y < 128; y++)
243 if (y % 16 == 0) Trace("\n");
244 d = (double)uPSQT[x][y];
247 Trace("%4u,", (ULONG)d);
255 COMMAND(GeneratePositionAndBestMoveSuite)
275 SEARCHER_THREAD_CONTEXT *ctx = NULL;
276 FLAG fPost = g_Options.fShouldPost;
283 Trace("Error (missing filename)\n");
286 szFilename = argv[1];
288 if (FALSE == SystemDoesFileExist(szFilename))
290 Trace("LearnPsqtFromPgn: file \"%s\" doesn't exist.\n",
295 if (0 != stat(szFilename, &s))
297 Trace("Stat failed.\n");
301 pf = fopen(szFilename, "rb");
304 Trace("LearnPsqtFromPgn: error reading file \"%s\".\n",
309 ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT));
312 Trace("Out of memory.\n");
316 g_Options.fShouldPost = FALSE;
317 while((pPGN = ReadNextGameFromPgnFile(pf)) != NULL)
319 d = (double)ftell(pf);
320 d /= (double)s.st_size;
322 printf("(%5.2f%% done)\r", d);
324 if (TRUE == LoadPgn(pPGN))
326 p = g_GameData.sMoveList.pFlink;
327 while(p != &(g_GameData.sMoveList))
329 q = CONTAINING_STRUCT(p, GAME_MOVE, links);
330 if ((q->uNumber > 20) && (q->uNumber < 60))
332 if (FenToPosition(&board, q->szUndoPositionFen))
334 InitializeSearcherContext(&board, ctx);
335 g_MoveTimer.bvFlags = 0;
336 g_Options.fPondering = FALSE;
337 g_Options.fThinking = TRUE;
338 g_Options.fSuccessfulPonder = FALSE;
340 MaintainDynamicMoveOrdering();
343 g_MoveTimer.uNodeCheckMask = 0x1000 - 1;
344 g_MoveTimer.dStartTime = SystemTimeStamp();
345 g_MoveTimer.bvFlags = 0;
346 g_MoveTimer.dSoftTimeLimit =
347 g_MoveTimer.dStartTime + 3.0;
348 g_MoveTimer.dHardTimeLimit =
349 g_MoveTimer.dStartTime + 3.0;
350 g_Options.uMaxDepth = MAX_DEPTH_PER_SEARCH - 1;
353 // TODO: Set draw value
355 #if (PERF_COUNTERS && MP)
356 ClearHelperThreadIdleness();
358 GAME_RESULT result = Iterate(ctx);
359 if (result.eResult == RESULT_IN_PROGRESS) {
360 ASSERT(SanityCheckMove(&board,
361 ctx->sPlyInfo[0].PV[0]));
362 Trace("setboard %s\n", q->szUndoPositionFen);
363 Trace("gmmove %s\n", q->szMoveInSan);
364 Trace("solution %s\n",
365 MoveToSan(ctx->sPlyInfo[0].PV[0], &board));
372 SystemFreeMemory(pPGN);
383 SystemFreeMemory(ctx);
386 g_Options.fShouldPost = fPost;
391 FindChunk(char *sz, ULONG uTargetChunk)
396 A support routine used by FenToPosition. Used to parse apart a
397 string and return the Nth chunk of text (delimited by whitespace)
413 if (0 == uTargetChunk) return(p);
418 // Skip whitespace between chunks
420 while(isspace(*p) && *p) p++;
421 if (!*p) return(NULL);
424 if (u == uTargetChunk) return(p);
429 while(!isspace(*p) && &p) p++;
430 if (!*p) return(NULL);
435 UtilPanic(SHOULD_NOT_GET_HERE,
436 NULL, NULL, NULL, NULL,
445 ScoreToString(SCORE iScore)
450 Format a chess score as a string that looks like:
460 Note: NOT thread safe.
472 static char szBuf[10];
475 if (abs(iScore) < +NMATE)
477 snprintf(szBuf, ARRAY_LENGTH(szBuf),
479 (iScore < 0) ? '-' : '+',
487 uDist = +INFINITY - iScore;
491 snprintf(szBuf, ARRAY_LENGTH(szBuf), "+MATE%u", uDist);
495 snprintf(szBuf, ARRAY_LENGTH(szBuf), "+MATE");
500 uDist = +INFINITY + iScore;
504 snprintf(szBuf, ARRAY_LENGTH(szBuf), "-MATE%u", uDist);
508 snprintf(szBuf, ARRAY_LENGTH(szBuf), "-MATE");
522 Take a chessboard coordinate and convert it into a string. Note:
539 buf[0] = 'a' + FILE(c);
540 buf[1] = '0' + RANK(c);
553 ColorToString(ULONG u)
558 Convert a color into a string ("WHITE" or "BLACK"). Callers
559 should not mess with the contents of the buffer returned!
571 static CHAR *szColor[2] =
576 ASSERT(IS_VALID_COLOR(u));
582 TimeToString(double d)
587 Convert a time value into a string. e.g.
589 00:00:01.021 // seconds with ms
590 00:15:21.998 // minutes+seconds
591 01:59:23.232 // hours+minutes+sec
592 27:08:11:103 // we don't do days
594 Note: NOT thread safe. The caller should not mess with the
607 ULONG uTotalSecs = (ULONG)d;
608 double dFract = d - (double)uTotalSecs;
615 uFract = (int)dFract;
616 uHours = uTotalSecs / 60 / 60;
617 uTotalSecs -= uHours * 60 * 60;
618 uMinutes = uTotalSecs / 60;
619 uTotalSecs -= uMinutes * 60;
620 ASSERT(uTotalSecs < 60);
624 snprintf(buf, ARRAY_LENGTH(buf),
625 "%2u:%02u:%02u.%03u",
626 uHours, uMinutes, uTotalSecs, uFract);
630 snprintf(buf, ARRAY_LENGTH(buf),
632 uMinutes, uTotalSecs, uFract);
639 BufferIsZeroed(BYTE *p, ULONG u)
644 Verify that a buffer is zeroed out.
648 BYTE *p : buffer start
649 ULONG u : buffer length
671 Trace(CHAR *szMessage, ...)
676 Trace a logging message on stdout and to the logfile
689 va_list ap; // Variable argument list
690 CHAR buf[MEDIUM_STRING_LEN_CHAR];
692 memset(buf, 0, sizeof(buf));
697 va_start(ap, szMessage);
698 COMPILER_VSNPRINTF(buf, MEDIUM_STRING_LEN_CHAR - 1, szMessage, ap);
704 if (NULL != g_pfLogfile) {
705 fprintf(g_pfLogfile, buf);
711 fprintf(stdout, buf);
717 Log(CHAR *szMessage, ...)
722 Trace a logging message to the logfile only
735 va_list ap; // Variable argument list
736 CHAR buf[MEDIUM_STRING_LEN_CHAR];
737 memset(buf, 0, sizeof(buf));
742 va_start(ap, szMessage);
743 COMPILER_VSNPRINTF(buf, MEDIUM_STRING_LEN_CHAR - 1, szMessage, ap);
749 if (NULL != g_pfLogfile) {
750 fprintf(g_pfLogfile, buf);
757 Bug(CHAR *szMessage, ...)
762 Trace an error message to stderr and the logfile
775 va_list ap; // Variable argument list
776 CHAR buf[MEDIUM_STRING_LEN_CHAR];
778 memset(buf, 0, sizeof(buf));
783 va_start(ap, szMessage);
784 COMPILER_VSNPRINTF(buf, MEDIUM_STRING_LEN_CHAR - 1, szMessage, ap);
788 // Send it to logfile
790 if (NULL != g_pfLogfile) {
791 fprintf(g_pfLogfile, buf);
798 fprintf(stderr, buf);
805 _assert(CHAR *szFile, ULONG uLine)
823 Bug("Assertion failure in %s, line %u.\n", szFile, uLine);
830 AcquireSpinLock(volatile ULONG *pSpinlock)
835 Atomically acquire a lock or spin trying.
839 ULONG *pSpinlock : a pointer to the lock
843 ULONG : the number of times we had to spin
848 while(0 != LockCompareExchange(pSpinlock, 1, 0))
852 ASSERT(*pSpinlock == 1);
857 TryAcquireSpinLock(volatile ULONG *pSpinlock)
859 return(0 != LockCompareExchange(pSpinlock, 1, 0));
863 ReleaseSpinLock(volatile ULONG *pSpinlock)
868 Release a lock so someone else can acquire it.
872 ULONG *pSpinlock : a pointer to the lock
881 ASSERT(*pSpinlock == 1);
882 u = LockCompareExchange(pSpinlock, 0, 1);
888 BackupFile(CHAR *szFile)
893 Backup a file to file.000. If file.000 already exists, back it up
896 Note: this function is recursive and can require quite a lot of
897 stack space. Also it is full of race conditions and should not be
898 used when some other code may be accessing the filesystem at the
903 CHAR *szFile : the file to backup
912 CHAR buf[SMALL_STRING_LEN_CHAR];
915 if (TRUE == SystemDoesFileExist(szFile))
917 if ((strlen(szFile) > 3) &&
918 (isdigit(szFile[0])) &&
919 (isdigit(szFile[1])) &&
920 (isdigit(szFile[2])))
922 u = (szFile[0] - '0') * 100;
923 u += (szFile[1] - '0') * 10;
924 u += szFile[2] - '0' + 1;
932 snprintf(buf, ARRAY_LENGTH(buf), "%03u%s", u, p);
933 if (TRUE == SystemDoesFileExist(buf))
937 return(SystemCopyFile(szFile, buf) && SystemDeleteFile(szFile));
944 Checksum(BYTE *p, ULONG uSize)
949 Compute the checksum of a buffer.
973 FinishPVTailFromHash(SEARCHER_THREAD_CONTEXT *ctx,
977 MOVE PV[MAX_PLY_PER_SEARCH];
979 ULONG uPly = ctx->uPly;
983 memcpy(&board, &(ctx->sPosition), sizeof(POSITION));
986 if (NULL == g_pHashTable) return;
989 mv = GetPonderMove(&ctx->sPosition);
990 if (mv.uMove == 0) break;
993 uLen = (ULONG)strlen(MoveToSan(mv, &ctx->sPosition)) + 1;
995 if (uLenRemain <= uLen) break;
996 strcat(buf, MoveToSan(PV[ctx->uPly], &ctx->sPosition));
1000 if (FALSE == MakeMove(ctx, mv)) break;
1007 while(ctx->uPly > uPly)
1009 UnmakeMove(ctx, PV[ctx->uPly - 1]);
1011 ASSERT(ctx->uPly == uPly);
1012 ASSERT(PositionsAreEquivalent(&board, &ctx->sPosition));
1018 WalkPV(SEARCHER_THREAD_CONTEXT *ctx)
1021 Routine description:
1023 Return a string buffer representing the principle variation.
1027 SEARCHER_THREAD_CONTEXT *ctx
1035 static CHAR buf[SMALL_STRING_LEN_CHAR];
1036 ULONG u = ctx->uPly;
1041 memcpy(&board, &(ctx->sPosition), sizeof(POSITION));
1045 while((mv.uMove = ctx->sPlyInfo[v].PV[u].uMove) != 0)
1047 if (mv.uMove == HASHMOVE.uMove)
1049 strncat(buf, "<TT> ", ARRAY_LENGTH(buf) - strlen(buf) - 1);
1050 FinishPVTailFromHash(ctx, buf, ARRAY_LENGTH(buf) - strlen(buf));
1051 ASSERT(ctx->sPlyInfo[v].PV[u+1].uMove == 0);
1054 else if (mv.uMove == RECOGNMOVE.uMove)
1056 strncat(buf, "<REC>", ARRAY_LENGTH(buf) - strlen(buf) - 1);
1057 ASSERT(ctx->sPlyInfo[v].PV[u+1].uMove == 0);
1060 else if (mv.uMove == DRAWMOVE.uMove)
1062 strncat(buf, "<=>", ARRAY_LENGTH(buf) - strlen(buf) - 1);
1063 ASSERT(ctx->sPlyInfo[v].PV[u+1].uMove == 0);
1068 strncat(buf, MoveToSan(mv, &ctx->sPosition),
1069 ARRAY_LENGTH(buf) - strlen(buf));
1070 strncat(buf, " ", ARRAY_LENGTH(buf) - strlen(buf) - 1);
1073 if (FALSE == MakeMove(ctx, mv)) break;
1076 if (strlen(buf) > (SMALL_STRING_LEN_CHAR - 10)) break;
1083 while(ctx->uPly > v)
1085 UnmakeMove(ctx, ctx->sPlyInfo[v].PV[u]);
1088 ASSERT(ctx->uPly == v);
1089 ASSERT(PositionsAreEquivalent(&board, &ctx->sPosition));
1095 UtilPanic(ULONG uPanicCode,
1097 void *arg1, void *arg2, void *arg3,
1098 char *szFile, ULONG uLine)
1100 Bug("Typhoon PANIC 0x%x (%p, %p, %p, %p)\n"
1101 "Location: %s at line %u\n\n",
1102 uPanicCode, pos, arg1, arg2, arg3, szFile, uLine);
1104 Bug("----------------------------------------------------------------\n");
1105 switch(uPanicCode) {
1106 case CANNOT_INITIALIZE_SPLIT:
1107 case GOT_ILLEGAL_MOVE_WHILE_PONDERING:
1108 case CANNOT_OFFICIALLY_MAKE_MOVE:
1110 DumpMove((ULONG)arg1);
1112 case INITIALIZATION_FAILURE:
1113 Bug("%s\n", (char *)arg1);
1119 fflush(g_pfLogfile);
1125 UtilPrintPV(SEARCHER_THREAD_CONTEXT *ctx,
1132 Routine description:
1134 Print a PV out to stdout and the log.
1138 SEARCHER_THREAD_CONTEXT *ctx,
1150 double dNow = SystemTimeStamp();
1152 ASSERT(ctx->uThreadNumber == 0);
1153 ASSERT(IS_VALID_SCORE(iAlpha));
1154 ASSERT(IS_VALID_SCORE(iBeta));
1155 ASSERT(iAlpha < iBeta);
1156 ASSERT(IS_VALID_SCORE(iScore));
1158 dNow -= g_MoveTimer.dStartTime;
1161 // Set the last PV in the context if we have a root PV or a root
1164 if ((iAlpha < iScore) && (iScore < iBeta))
1166 strncpy(ctx->szLastPV, WalkPV(ctx), SMALL_STRING_LEN_CHAR);
1168 else if (iScore > iBeta)
1170 strncpy(ctx->szLastPV, MoveToSan(mv, &(ctx->sPosition)),
1171 SMALL_STRING_LEN_CHAR);
1174 if ((TRUE == g_Options.fThinking) && (TRUE == g_Options.fShouldPost))
1177 // Maybe output the PV. Note: xboard gets confused by PV
1178 // lines that don't match its requirements exactly; if we are
1179 // running under xboard, match its expected format. Otherwise
1180 // be more helpful / verbose.
1182 if ((dNow > 0.35) || ((dNow > 0.15) && (g_Options.fVerbosePosting)))
1184 if (TRUE == g_Options.fRunningUnderXboard)
1186 if ((iAlpha < iScore) && (iScore < iBeta))
1188 Trace("%2u %6d %5u %-12"
1189 COMPILER_LONGLONG_UNSIGNED_FORMAT" %s\n",
1192 (ULONG)(dNow * 100),
1193 ctx->sCounters.tree.u64TotalNodeCount,
1196 else if (iScore <= iAlpha)
1198 Trace("%2u %6d %5u %-12"
1199 COMPILER_LONGLONG_UNSIGNED_FORMAT" FL --\n",
1202 (ULONG)(dNow * 100),
1203 ctx->sCounters.tree.u64TotalNodeCount);
1207 ASSERT(iScore >= iBeta);
1208 Trace("%2u %6d %5u %-12"
1209 COMPILER_LONGLONG_UNSIGNED_FORMAT" %s ++\n",
1212 (ULONG)(dNow * 100),
1213 ctx->sCounters.tree.u64TotalNodeCount,
1217 else // !running under xboard
1219 if ((iAlpha < iScore) && (iScore < iBeta))
1221 Trace("%2u %5s %-9s %-11"
1222 COMPILER_LONGLONG_UNSIGNED_FORMAT" %s\n",
1224 ScoreToString(iScore),
1226 ctx->sCounters.tree.u64TotalNodeCount,
1229 else if (iScore <= iAlpha)
1231 Trace("%2u- %5s %-9s %-11"
1232 COMPILER_LONGLONG_UNSIGNED_FORMAT" FL --\n",
1234 ScoreToString(iScore),
1236 ctx->sCounters.tree.u64TotalNodeCount);
1240 ASSERT(iScore >= iBeta);
1241 Trace("%2u+ %5s %-9s %-11"
1242 COMPILER_LONGLONG_UNSIGNED_FORMAT" %s ++\n",
1244 ScoreToString(iScore),
1246 ctx->sCounters.tree.u64TotalNodeCount,