3 Copyright (c) Scott Gasch
11 General purpose utility code to support the engine.
19 $Id: util.c 345 2007-12-02 22:56:42Z 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)
706 fprintf(g_pfLogfile, buf);
712 fprintf(stdout, buf);
718 Log(CHAR *szMessage, ...)
723 Trace a logging message to the logfile only
736 va_list ap; // Variable argument list
737 CHAR buf[MEDIUM_STRING_LEN_CHAR];
738 memset(buf, 0, sizeof(buf));
743 va_start(ap, szMessage);
744 COMPILER_VSNPRINTF(buf, MEDIUM_STRING_LEN_CHAR - 1, szMessage, ap);
750 if (NULL != g_pfLogfile)
752 fprintf(g_pfLogfile, buf);
758 Bug(CHAR *szMessage, ...)
763 Trace an error message to stderr and the logfile
776 va_list ap; // Variable argument list
777 CHAR buf[MEDIUM_STRING_LEN_CHAR];
779 memset(buf, 0, sizeof(buf));
784 va_start(ap, szMessage);
785 COMPILER_VSNPRINTF(buf, MEDIUM_STRING_LEN_CHAR - 1, szMessage, ap);
789 // Send it to logfile
791 if (NULL != g_pfLogfile)
793 fprintf(g_pfLogfile, buf);
800 fprintf(stderr, buf);
807 _assert(CHAR *szFile, ULONG uLine)
825 Bug("Assertion failure in %s, line %u.\n", szFile, uLine);
832 AcquireSpinLock(volatile ULONG *pSpinlock)
837 Atomically acquire a lock or spin trying.
841 ULONG *pSpinlock : a pointer to the lock
845 ULONG : the number of times we had to spin
850 while(0 != LockCompareExchange(pSpinlock, 1, 0))
854 ASSERT(*pSpinlock == 1);
859 TryAcquireSpinLock(volatile ULONG *pSpinlock)
861 return(0 != LockCompareExchange(pSpinlock, 1, 0));
865 ReleaseSpinLock(volatile ULONG *pSpinlock)
870 Release a lock so someone else can acquire it.
874 ULONG *pSpinlock : a pointer to the lock
883 ASSERT(*pSpinlock == 1);
884 u = LockCompareExchange(pSpinlock, 0, 1);
890 BackupFile(CHAR *szFile)
895 Backup a file to file.000. If file.000 already exists, back it up
898 Note: this function is recursive and can require quite a lot of
899 stack space. Also it is full of race conditions and should not be
900 used when some other code may be accessing the filesystem at the
905 CHAR *szFile : the file to backup
914 CHAR buf[SMALL_STRING_LEN_CHAR];
917 if (TRUE == SystemDoesFileExist(szFile))
919 if ((strlen(szFile) > 3) &&
920 (isdigit(szFile[0])) &&
921 (isdigit(szFile[1])) &&
922 (isdigit(szFile[2])))
924 u = (szFile[0] - '0') * 100;
925 u += (szFile[1] - '0') * 10;
926 u += szFile[2] - '0' + 1;
934 snprintf(buf, ARRAY_LENGTH(buf), "%03u%s", u, p);
935 if (TRUE == SystemDoesFileExist(buf))
939 return(SystemCopyFile(szFile, buf) && SystemDeleteFile(szFile));
946 Checksum(BYTE *p, ULONG uSize)
951 Compute the checksum of a buffer.
975 FinishPVTailFromHash(SEARCHER_THREAD_CONTEXT *ctx,
979 MOVE PV[MAX_PLY_PER_SEARCH];
981 ULONG uPly = ctx->uPly;
985 memcpy(&board, &(ctx->sPosition), sizeof(POSITION));
988 if (NULL == g_pHashTable) return;
991 mv = GetPonderMove(&ctx->sPosition);
992 if (mv.uMove == 0) break;
995 uLen = (ULONG)strlen(MoveToSan(mv, &ctx->sPosition)) + 1;
997 if (uLenRemain <= uLen) break;
998 strcat(buf, MoveToSan(PV[ctx->uPly], &ctx->sPosition));
1002 if (FALSE == MakeMove(ctx, mv)) break;
1009 while(ctx->uPly > uPly)
1011 UnmakeMove(ctx, PV[ctx->uPly - 1]);
1013 ASSERT(ctx->uPly == uPly);
1014 ASSERT(PositionsAreEquivalent(&board, &ctx->sPosition));
1020 WalkPV(SEARCHER_THREAD_CONTEXT *ctx)
1023 Routine description:
1025 Return a string buffer representing the principle variation.
1029 SEARCHER_THREAD_CONTEXT *ctx
1037 static CHAR buf[SMALL_STRING_LEN_CHAR];
1038 ULONG u = ctx->uPly;
1043 memcpy(&board, &(ctx->sPosition), sizeof(POSITION));
1047 while((mv.uMove = ctx->sPlyInfo[v].PV[u].uMove) != 0)
1049 if (mv.uMove == HASHMOVE.uMove)
1051 strncat(buf, "<TT> ", ARRAY_LENGTH(buf) - strlen(buf) - 1);
1052 FinishPVTailFromHash(ctx, buf, ARRAY_LENGTH(buf) - strlen(buf));
1053 ASSERT(ctx->sPlyInfo[v].PV[u+1].uMove == 0);
1056 else if (mv.uMove == RECOGNMOVE.uMove)
1058 strncat(buf, "<REC>", ARRAY_LENGTH(buf) - strlen(buf) - 1);
1059 ASSERT(ctx->sPlyInfo[v].PV[u+1].uMove == 0);
1062 else if (mv.uMove == DRAWMOVE.uMove)
1064 strncat(buf, "<=>", ARRAY_LENGTH(buf) - strlen(buf) - 1);
1065 ASSERT(ctx->sPlyInfo[v].PV[u+1].uMove == 0);
1070 strncat(buf, MoveToSan(mv, &ctx->sPosition),
1071 ARRAY_LENGTH(buf) - strlen(buf));
1072 strncat(buf, " ", ARRAY_LENGTH(buf) - strlen(buf) - 1);
1075 if (FALSE == MakeMove(ctx, mv)) break;
1078 if (strlen(buf) > (SMALL_STRING_LEN_CHAR - 10)) break;
1085 while(ctx->uPly > v)
1087 UnmakeMove(ctx, ctx->sPlyInfo[v].PV[u]);
1090 ASSERT(ctx->uPly == v);
1091 ASSERT(PositionsAreEquivalent(&board, &ctx->sPosition));
1097 UtilPanic(ULONG uPanicCode,
1099 void *arg1, void *arg2, void *arg3,
1100 char *szFile, ULONG uLine)
1102 Bug("Typhoon PANIC 0x%x (%p, %p, %p, %p)\n"
1103 "Location: %s at line %u\n\n",
1104 uPanicCode, pos, arg1, arg2, arg3, szFile, uLine);
1106 Bug("----------------------------------------------------------------\n");
1107 switch(uPanicCode) {
1108 case CANNOT_INITIALIZE_SPLIT:
1109 case GOT_ILLEGAL_MOVE_WHILE_PONDERING:
1110 case CANNOT_OFFICIALLY_MAKE_MOVE:
1112 DumpMove((ULONG)arg1);
1114 case INITIALIZATION_FAILURE:
1115 Bug("%s\n", (char *)arg1);
1121 fflush(g_pfLogfile);
1127 UtilPrintPV(SEARCHER_THREAD_CONTEXT *ctx,
1134 Routine description:
1136 Print a PV out to stdout and the log.
1140 SEARCHER_THREAD_CONTEXT *ctx,
1152 double dNow = SystemTimeStamp();
1154 ASSERT(ctx->uThreadNumber == 0);
1155 ASSERT(IS_VALID_SCORE(iAlpha));
1156 ASSERT(IS_VALID_SCORE(iBeta));
1157 ASSERT(iAlpha < iBeta);
1158 ASSERT(IS_VALID_SCORE(iScore));
1160 dNow -= g_MoveTimer.dStartTime;
1163 // Set the last PV in the context if we have a root PV or a root
1166 if ((iAlpha < iScore) && (iScore < iBeta))
1168 strncpy(ctx->szLastPV, WalkPV(ctx), SMALL_STRING_LEN_CHAR);
1170 else if (iScore > iBeta)
1172 strncpy(ctx->szLastPV, MoveToSan(mv, &(ctx->sPosition)),
1173 SMALL_STRING_LEN_CHAR);
1176 if ((TRUE == g_Options.fThinking) && (TRUE == g_Options.fShouldPost))
1179 // Maybe output the PV. Note: xboard gets confused by PV
1180 // lines that don't match its requirements exactly; if we are
1181 // running under xboard, match its expected format. Otherwise
1182 // be more helpful / verbose.
1184 if ((dNow > 0.35) || ((dNow > 0.15) && (g_Options.fVerbosePosting)))
1186 if (TRUE == g_Options.fRunningUnderXboard)
1188 if ((iAlpha < iScore) && (iScore < iBeta))
1190 Trace("%2u %6d %5u %-12"
1191 COMPILER_LONGLONG_UNSIGNED_FORMAT" %s\n",
1194 (ULONG)(dNow * 100),
1195 ctx->sCounters.tree.u64TotalNodeCount,
1198 else if (iScore <= iAlpha)
1200 Trace("%2u %6d %5u %-12"
1201 COMPILER_LONGLONG_UNSIGNED_FORMAT" FL --\n",
1204 (ULONG)(dNow * 100),
1205 ctx->sCounters.tree.u64TotalNodeCount);
1209 ASSERT(iScore >= iBeta);
1210 Trace("%2u %6d %5u %-12"
1211 COMPILER_LONGLONG_UNSIGNED_FORMAT" %s ++\n",
1214 (ULONG)(dNow * 100),
1215 ctx->sCounters.tree.u64TotalNodeCount,
1219 else // !running under xboard
1221 if ((iAlpha < iScore) && (iScore < iBeta))
1223 Trace("%2u %5s %-9s %-11"
1224 COMPILER_LONGLONG_UNSIGNED_FORMAT" %s\n",
1226 ScoreToString(iScore),
1228 ctx->sCounters.tree.u64TotalNodeCount,
1231 else if (iScore <= iAlpha)
1233 Trace("%2u- %5s %-9s %-11"
1234 COMPILER_LONGLONG_UNSIGNED_FORMAT" FL --\n",
1236 ScoreToString(iScore),
1238 ctx->sCounters.tree.u64TotalNodeCount);
1242 ASSERT(iScore >= iBeta);
1243 Trace("%2u+ %5s %-9s %-11"
1244 COMPILER_LONGLONG_UNSIGNED_FORMAT" %s ++\n",
1246 ScoreToString(iScore),
1248 ctx->sCounters.tree.u64TotalNodeCount,