Update codebase to remove clang warnings (and a couple of legit errors
[typhoon.git] / src / save / main.c
1 /**
2
3 Copyright (c) Scott Gasch
4
5 Module Name:
6
7     main.c
8
9 Abstract:
10
11     Program entry point and setup code.
12
13 Author:
14
15     Scott Gasch ([email protected]) 8 Apr 2004
16
17 Revision History:
18
19     $Id: main.c 351 2008-06-28 04:21:53Z scott $
20
21 **/
22
23 #include "chess.h"
24
25 FILE *g_pfLogfile = NULL;
26 GAME_OPTIONS g_Options;
27 CHAR g_szInitialCommand[SMALL_STRING_LEN_CHAR];
28 ULONG g_uInputThreadHandle = (ULONG)-1;
29
30 void
31 Banner(void)
32 /**
33
34 Routine description:
35
36     Describe the compile/build options and program version.
37
38 Parameters:
39
40     void
41
42 Return value:
43
44     void
45
46 **/
47 {
48     char *p;
49
50     Trace("Typhoon %s (built on %s %s):\n", VERSION, __DATE__, __TIME__);
51     Trace("    Copyright (C) 2000-2007, Scott Gasch ([email protected])\n");
52     Trace("    " REVISION);
53     Trace("    " COMPILER_STRING);
54     Trace("    Make profile used: %s\n", PROFILE);
55 #ifdef MP
56     Trace("    Multiprocessor enabled; %u searcher thread%s\n",
57           g_Options.uNumProcessors, (g_Options.uNumProcessors > 1) ? "s" : "");
58 #else
59     Trace("    Single processor version\n");
60 #endif
61 #ifdef DEBUG
62     Trace("    DEBUG build\n");
63 #endif
64 #ifdef TEST
65     Trace("    Testcode compiled in\n");
66 #endif
67 #ifdef EVAL_DUMP
68     Trace("    Evaluation terms\n");
69 #endif
70 #ifdef EVAL_TIME
71     Trace("    Evaluation timing\n");
72 #endif
73 #ifdef PERF_COUNTERS
74     Trace("    Performance counters\n");
75 #ifdef TEST_NULL
76     Trace("    Testing nullmove prediction algorithms\n");
77 #endif
78 #endif
79 #ifdef DUMP_TREE
80     Trace("    Search tree dumpfile generation enabled\n");
81 #endif
82     Trace("    Hash sizes: %u Mb (main), %u Mb / thread (pawn), %u Mb / thread (eval)\n", 
83           (g_uHashTableSizeEntries * sizeof(HASH_ENTRY)) / MB,
84           PAWN_HASH_TABLE_SIZE * sizeof(PAWN_HASH_ENTRY) / MB,
85           EVAL_HASH_TABLE_SIZE * sizeof(EVAL_HASH_ENTRY) / MB);
86     Trace("    QCheckPlies: %u\n", QPLIES_OF_NON_CAPTURE_CHECKS);
87     Trace("    FutilityBase: %u\n", FUTILITY_BASE_MARGIN);
88     p = ExportEvalDNA();
89     Trace("    Logging Eval DNA.\n");
90     Log(p);
91     free(p);
92 #ifdef DO_IID
93     Trace("    IID R-factor: %u ply\n", IID_R_FACTOR / ONE_PLY);
94 #endif
95 }
96
97
98 void
99 EndLogging(void)
100 /**
101
102 Routine description:
103
104     Close the main logfile.
105
106 Parameters:
107
108     void
109
110 Return value:
111
112     void
113
114 **/
115 {
116     if (NULL != g_pfLogfile)
117     {
118         fflush(g_pfLogfile);
119         fflush(stdout);
120         fclose(g_pfLogfile);
121         g_pfLogfile = NULL;
122     }
123 }
124
125
126 FLAG
127 BeginLogging(void)
128 /**
129
130 Routine description:
131
132     Open the main logfile.
133
134 Parameters:
135
136     void
137
138 Return value:
139
140     FLAG
141
142 **/
143 {
144     if (NULL != g_pfLogfile)
145     {
146         EndLogging();
147     }
148     ASSERT(g_pfLogfile == NULL);
149     ASSERT(strlen(g_Options.szLogfile));
150
151     //
152     // If the logfile we want to write already exists, back it up.
153     //
154     if (TRUE == SystemDoesFileExist(g_Options.szLogfile))
155     {
156         VERIFY(BackupFile(g_Options.szLogfile));
157     }
158     ASSERT(FALSE == SystemDoesFileExist(g_Options.szLogfile));
159
160     g_pfLogfile = fopen(g_Options.szLogfile, "wb+");
161     if (NULL == g_pfLogfile)
162     {
163         UtilPanic(INITIALIZATION_FAILURE,
164                   (void *)"Can't open logfile", 
165                   (void *)&(g_Options.szLogfile), 
166                   (void *)"write", 
167                   NULL,
168                   __FILE__, __LINE__);
169     }
170     return(TRUE);
171 }
172
173
174 FLAG
175 PreGameReset(FLAG fResetBoard)
176 /**
177
178 Routine description:
179
180     Reset internal state between games.
181
182 Parameters:
183
184     void
185
186 Return value:
187
188     FLAG
189
190 **/
191 {
192     if (TRUE == fResetBoard)
193     {
194         SetRootToInitialPosition();
195     }
196     SetMyName();
197     ClearDynamicMoveOrdering();
198     ClearHashTable();
199     ResetOpeningBook();
200     return(TRUE);
201 }
202
203
204 static ULONG
205 _ParseHashOption(char *p,
206                  ULONG uEntrySize)
207 {
208     ULONG u, v;
209     CHAR c;
210
211     if ((!STRCMPI(p, "none")) || (!STRCMPI(p, "no"))) return(0);
212     if (2 == sscanf(p, "%u%1c", &u, &c))
213     {
214         v = u;
215         switch(c)
216         {
217             case 'K':
218             case 'k':
219                 u *= 1024;
220                 break;
221             case 'M':
222             case 'm':
223                 u *= 1024 * 1024;
224                 break;
225             case 'G':
226             case 'g':
227                 u *= 1024 * 1024 * 1024;
228                 break;
229             default:
230                 Trace("Error (unrecognized size): \"%s\"\n", p);
231                 break;
232         }
233         if (u < v)
234         {
235             Trace("Error (size too large): \"%s\"\n", p);
236             u = v;
237         }
238         u /= uEntrySize;
239     }
240     else
241     {
242         u = (ULONG)atoi(p);
243     }
244
245     while(!IS_A_POWER_OF_2(u))
246     {
247         u &= (u - 1);
248     }
249     return(u);
250 }
251
252
253
254 void
255 InitializeOptions(int argc, char *argv[])
256 /**
257
258 Routine description:
259
260     Set global options to their initial state.  This code also is
261     responsible for parsing the commandline in order to override the
262     default state of global options.
263
264 Parameters:
265
266     int argc,
267     char *argv[]
268
269 Return value:
270
271     void
272
273 **/
274 {
275     int i;
276
277     //
278     // Defaults
279     //
280     memset(&g_Options, 0, sizeof(g_Options));
281     g_szInitialCommand[0] = '\0';
282     g_Options.uMyClock = g_Options.uOpponentsClock = 600;
283     g_Options.fGameIsRated = FALSE;
284     g_Options.fOpponentIsComputer = FALSE;
285     g_Options.uSecPerMove = 0;
286     g_Options.uMaxDepth = MAX_PLY_PER_SEARCH - 1;
287     g_Options.fShouldPonder = TRUE;
288     g_Options.fPondering = g_Options.fThinking = FALSE;
289     g_Options.mvPonder.uMove = 0;
290     g_Options.fShouldPost = TRUE;
291     g_Options.fForceDrawWorthZero = FALSE;
292     g_Options.uMyIncrement = 0;
293     g_Options.uMovesPerTimePeriod = 0;
294     g_Options.szAnalyzeProgressReport[0] = '\0';
295     g_Options.fShouldAnnounceOpening = TRUE;
296     g_Options.eClock = CLOCK_NONE;
297     g_Options.eGameType = GAME_UNKNOWN;
298     g_Options.ePlayMode = FORCE_MODE;
299     g_Options.fNoInputThread = FALSE;
300     g_Options.fVerbosePosting = TRUE;
301     strcpy(g_Options.szEGTBPath, "/egtb/three;/etc/four;/egtb/five");
302     strcpy(g_Options.szLogfile, "typhoon.log");
303     strcpy(g_Options.szBookName, "book.bin");
304     g_Options.uNumHashTableEntries = 0x10000;
305     g_Options.uNumProcessors = 1;
306     g_Options.fStatusLine = TRUE;
307     g_Options.fShouldResign = FALSE;
308     g_Options.u64MaxNodeCount = 0ULL;
309
310     i = 1;
311     while(i < argc)
312     {
313 #ifdef MP
314         if ((!STRCMPI(argv[i], "--cpus")) && (argc > i))
315         {
316             g_Options.uNumProcessors = (ULONG)atoi(argv[i+1]);
317             if ((g_Options.uNumProcessors == 0) ||
318                 (g_Options.uNumProcessors > 64))
319             {
320                 g_Options.uNumProcessors = 2;
321             }
322             i++;
323         }
324         else
325 #endif
326         if ((!STRCMPI(argv[i], "--command")) && (argc > i))
327         {
328             strncpy(g_szInitialCommand,
329                     argv[i+1],
330                     SMALL_STRING_LEN_CHAR - 2);
331             strcat(g_szInitialCommand, "\r\n");
332             i++;
333         }
334         else if ((!STRCMPI(argv[i], "--hash")) && (argc > i))
335         {
336             g_Options.uNumHashTableEntries =
337                 _ParseHashOption(argv[i+1],
338                                  sizeof(HASH_ENTRY));
339             i++;
340         }
341         else if ((!STRCMPI(argv[i], "--egtbpath")) && (argc > i))
342         {
343             g_Options.szEGTBPath[0] = '\0';
344             strncpy(g_Options.szEGTBPath, argv[i+1], SMALL_STRING_LEN_CHAR);
345             i++;
346         }
347         else if (!STRCMPI(argv[i], "--batch"))
348         {
349             g_Options.fNoInputThread = TRUE;
350         }
351         else if ((!STRCMPI(argv[i], "--dnafile")) && (argc > i)) 
352         {
353             if (!ReadEvalDNA(argv[i+1])) 
354             {
355                 UtilPanic(INITIALIZATION_FAILURE,
356                           (void *)"Bad DNA file",
357                           (void *)&(argv[i+1]),
358                           (void *)"read",
359                           NULL,
360                           __FILE__, __LINE__);
361             }
362             i++;
363         }
364         else if ((!STRCMPI(argv[i], "--logfile")) && (argc > i)) 
365         {
366             strncpy(g_Options.szLogfile, argv[i+1],
367                     SMALL_STRING_LEN_CHAR);
368             i++;
369         }
370         else if (!STRCMPI(argv[i], "--help")) {
371             Trace("Usage: %s [--batch] [--command arg] [--logfile arg] [--egtbpath arg]\n"
372                   "                [--dnafile arg] [--cpus arg] [--hash arg]\n\n"
373                   "    --batch    : operate the engine without an input thread\n"
374                   "    --command  : specify initial command(s) (requires arg)\n"
375                   "    --cpus     : indicate the number of cpus to use (requires arg, MP build)\n"
376                   "    --hash     : indicate desired hash size (requires arg)\n"
377                   "    --egtbpath : supplies the egtb path to engine (requires arg)\n"
378                   "    --logfile  : indicate desired output logfile name (requires arg)\n"
379                   "    --dnafile  : indicate desired eval profile input (requres arg)\n\n"
380                   "Example: %s --hash 256m --cpus 2\n"
381                   "         %s --logfile test.out --batch --command \"test\" --dnafile test.in\n", argv[0], argv[0], argv[0]);
382             exit(-1);
383         }
384         else
385         {
386             Trace("Error (unknown argument): \"%s\"; try --help\n", argv[i]);
387         }
388
389         //
390         // TODO: parse other commandline options and override above
391         //
392
393         i++;
394     }
395 }
396
397
398 void
399 CleanupOptions(void)
400 /**
401
402 Routine description:
403
404     Cleanup options...
405
406 Parameters:
407
408     void
409
410 Return value:
411
412     void
413
414 **/
415 {
416     NOTHING;
417 }
418
419
420 FLAG
421 MainProgramInitialization(int argc, char *argv[])
422 /**
423
424 Routine description:
425
426     Perform main program initialization.  This includes stuff like:
427     precomputing some data structures, resetting the global options,
428     parsing the commandline to override said options, and calling out
429     to other modules to tell them to initialize themselves.
430
431 Parameters:
432
433     int argc,
434     char *argv[]
435
436 Return value:
437
438     FLAG
439
440 **/
441 {
442     srand((unsigned int)time(0));
443     InitializeOptions(argc, argv);
444     InitializeTreeDump();
445     InitializeEGTB();
446     InitializeSigSystem();
447     InitializeInteriorNodeRecognizers();
448     InitializeSearchDepthArray();
449     InitializeWhiteSquaresTable();
450     InitializeVectorDeltaTable();
451     InitializeSwapTable();
452     InitializeDistanceTable();
453     InitializeOpeningBook();
454     InitializeDynamicMoveOrdering();
455     InitializeHashSystem();
456     InitializePositionHashSystem();
457 #ifdef MP
458     InitializeParallelSearch();
459 #endif
460     VERIFY(PreGameReset(TRUE));
461     return(BeginLogging());
462 }
463
464
465 FLAG
466 MainProgramCleanup(void)
467 /**
468
469 Routine description:
470
471     Perform main program cleanup.  Tear down the stuff we set up in
472     MainProgramInitialization.
473
474 Parameters:
475
476     void
477
478 Return value:
479
480     FLAG
481
482 **/
483 {
484     CleanupOpeningBook();
485     CleanupEGTB();
486     CleanupDynamicMoveOrdering();
487 #ifdef MP
488     CleanupParallelSearch();
489 #endif
490     CleanupPositionHashSystem();
491     CleanupHashSystem();
492     CleanupTreeDump();
493     CleanupOptions();
494     EndLogging();
495     return(TRUE);
496 }
497
498
499 #ifdef TEST
500 FLAG
501 RunStartupProgramTest(void)
502 /**
503
504 Routine description:
505
506     If we built this version with -DTEST then this code is the start
507     of the self-test that runs at program startup time.
508
509 Parameters:
510
511     void
512
513 Return value:
514
515     FLAG
516
517 **/
518 {
519     ULONG u, x, y;
520
521 #ifdef _X86_
522     Trace("Testing CPP macros...\n");
523     for (u = 0; u < 1000000; u++)
524     {
525         x = rand() * rand();
526         y = rand() * rand();
527         x = x & ~(0x80000000);
528         y = y & ~(0x80000000);
529
530         if (MAX(x, y) != MAXU(x, y))
531         {
532             UtilPanic(TESTCASE_FAILURE,
533                       NULL, "max maxu", NULL, NULL, __FILE__, __LINE__);
534         }
535
536         if (MAXU(x, y) != MAXU(y, x))
537         {
538             UtilPanic(TESTCASE_FAILURE,
539                       NULL, "maxu assoc", NULL, NULL, __FILE__, __LINE__);
540         }
541
542         if (MIN(x, y) != MINU(x, y))
543         {
544             UtilPanic(TESTCASE_FAILURE,
545                       NULL, "min minu", NULL, NULL, __FILE__, __LINE__);
546         }
547
548         if (MINU(x, y) != MINU(y, x))
549         {
550             UtilPanic(TESTCASE_FAILURE,
551                       NULL, "minu assoc", NULL, NULL, __FILE__, __LINE__);
552         }
553
554         if (abs(x - y) != ABS_DIFF(x, y))
555         {
556             UtilPanic(TESTCASE_FAILURE,
557                       NULL, "abs_diff", NULL, NULL, __FILE__, __LINE__);
558         }
559
560         if (ABS_DIFF(x, y) != ABS_DIFF(y, x))
561         {
562             UtilPanic(TESTCASE_FAILURE,
563                       NULL, "abs_diff assoc", NULL, NULL, __FILE__, __LINE__);
564         }
565     }
566 #endif
567
568     TestDraw();
569 #ifdef EVAL_DUMP
570     TestEval();
571 #endif
572     TestBitboards();
573     TestSan();
574     TestIcs();
575     TestGetAttacks();
576     TestMoveGenerator();
577     TestLegalMoveGenerator();
578     TestFenCode();
579     TestLiftPlaceSlidePiece();
580     TestExposesCheck();
581     TestIsAttacked();
582     TestMakeUnmakeMove();
583     TestSearch();
584
585     return(TRUE);
586 }
587 #endif // TEST
588
589
590
591 void DeclareTerminalStates(GAME_RESULT result) {
592     switch(result.eResult) {
593     case RESULT_IN_PROGRESS:
594         return;
595     case RESULT_BLACK_WON:
596         Trace("0-1 {%s}\n", result.szDescription);
597         SetGameResultAndDescription(result.eResult, result.szDescription);
598         DumpPgn();
599         g_Options.ePlayMode = FORCE_MODE;
600         break;
601     case RESULT_WHITE_WON:
602         Trace("1-0 {%s}\n", result.szDescription);
603         SetGameResultAndDescription(result.eResult, result.szDescription);
604         DumpPgn();
605         g_Options.ePlayMode = FORCE_MODE;
606         break;
607     case RESULT_DRAW:
608         Trace("1/2-1/2 {%s}\n", result.szDescription);
609         Trace("tellics draw\n");
610         SetGameResultAndDescription(result.eResult, result.szDescription);
611         DumpPgn();
612         g_Options.ePlayMode = FORCE_MODE;
613         break;
614     case RESULT_ABANDONED:
615     case RESULT_UNKNOWN:
616         ASSERT(FALSE);
617         SetGameResultAndDescription(result.eResult, result.szDescription);
618         DumpPgn();
619         g_Options.ePlayMode = FORCE_MODE;
620         break;
621     }
622 }
623
624
625 // Main work loop
626 void MainProgramLoop() {
627     POSITION pos;
628     GAME_RESULT result;
629     FLAG fThink;
630     FLAG fPonder;
631
632     while (!g_fExitProgram) {
633         
634         // Prepare to search or ponder
635         memcpy(&pos, GetRootPosition(), sizeof(POSITION));
636         fThink = fPonder = FALSE;
637         result.eResult = RESULT_IN_PROGRESS;
638         result.szDescription[0] = '\0';
639         
640         // What should we do?
641         switch(g_Options.ePlayMode) {
642         case I_PLAY_WHITE:
643             Trace("I play white.\n");
644             fThink = (pos.uToMove == WHITE);
645             fPonder = (pos.uToMove == BLACK);
646             break;
647         case I_PLAY_BLACK:
648             Trace("I play black.\n");
649             fThink = (pos.uToMove == BLACK);
650             fPonder = (pos.uToMove == WHITE);
651             break;
652         case FORCE_MODE:
653         default:
654             Trace("Force mode\n");
655             fThink = fPonder = FALSE;
656             break;
657         }
658
659         if (fThink) {
660             Trace("Think...\n");
661             result = Think(&pos);
662             if ((NumberOfPendingInputEvents() != 0) &&
663                 (FALSE == g_fExitProgram))
664             {
665                 MakeStatusLine();
666                 ParseUserInput(FALSE);
667             }
668             DeclareTerminalStates(result);
669         } else if (fPonder) {
670             Trace("Ponder...\n");
671             if (g_Options.fShouldPonder) {
672                 result = Ponder(&pos);
673             }
674             if (FALSE == g_fExitProgram)
675             {
676                 MakeStatusLine();
677                 ParseUserInput(FALSE);
678             }
679             DeclareTerminalStates(result);
680         } else {
681             Trace("Force mode, consume input and continue\n");
682             MakeStatusLine();
683             ParseUserInput(FALSE);
684         }
685     }
686 }
687
688
689 int CDECL
690 main(int argc, char *argv[])
691 /**
692
693 Routine description:
694
695     Chess engine entry point and main loop.
696
697 Parameters:
698
699     int argc,
700     char *argv[]
701
702 Return value:
703
704     int CDECL
705
706 **/
707 {
708     // Initial setup work...
709     Trace("Setting up, please wait...\n");
710     if (FALSE == SystemDependentInitialization())
711     {
712         Trace("main: Operating system dependent init code failed, "
713               "aborting.\n");
714         exit(-1);
715     }
716     if (FALSE == MainProgramInitialization(argc, argv))
717     {
718         Trace("main: Main program init code failed, aborting.\n");
719         exit(-1);
720     }
721     Banner();
722 #ifdef TEST
723     (void)RunStartupProgramTest();
724 #endif
725     if (TRUE == g_Options.fNoInputThread)
726     {
727         InitInputSystemInBatchMode();
728     }
729     else
730     {
731         g_uInputThreadHandle = InitInputSystemWithDedicatedThread();
732     }
733     
734     // Enter the main work loop code...
735     MainProgramLoop();  
736     
737     // If we get here g_fExitProgram is set -- clean up.
738     Trace("MAIN THREAD: thread terminating.\n");
739     if (FALSE == MainProgramCleanup())
740     {
741         Trace("main: Main program cleanup code failed, aborting.\n");
742         exit(-1);
743     }
744     exit(0); 
745 }