Update codebase to remove clang warnings (and a couple of legit errors
[typhoon.git] / src / gamelist.c
1 /**
2
3 Copyright (c) Scott Gasch
4
5 Module Name:
6
7     gamelist.c
8
9 Abstract:
10
11     Maintain a list of game moves.  This list is used to take back
12     official moves.  It's also used to produce PGN at the end of a
13     game and to detect draws by repetition.  This code also maintains
14     the official "current position" of the game in progress.
15
16 Author:
17
18     Scott Gasch ([email protected]) 14 May 2004
19
20 Revision History:
21
22     $Id: gamelist.c 355 2008-07-01 15:46:43Z scott $
23
24 **/
25
26 #include "chess.h"
27
28 GAME_DATA g_GameData;
29 ULONG g_uMoveNumber[2] = { 1, 1 };
30 ULONG g_uToMove;
31 ALIGN64 POSITION g_RootPosition;
32
33 #ifdef DEBUG
34 ULONG g_uAllocs = 0;
35 #endif
36
37 #define MOVE_POOL_SIZE (256)
38 GAME_MOVE g_MovePool[MOVE_POOL_SIZE];
39
40 static GAME_MOVE *
41 _GetMoveFromPool(void)
42 /**
43
44 Routine description:
45
46     Fast GAME_MOVE allocator that uses a preallocated pool.
47
48 Parameters:
49
50     void
51
52 Return value:
53
54     static GAME_MOVE *
55
56 **/
57 {
58     ULONG x = (rand() % (MOVE_POOL_SIZE - 15));
59     ULONG y;
60
61     for (y = x; y < x + 15; y++)
62     {
63         if (g_MovePool[y].fInUse == FALSE)
64         {
65             LockIncrement(&(g_MovePool[y].fInUse));
66             return(&(g_MovePool[y]));
67         }
68     }
69 #ifdef DEBUG
70     g_uAllocs++;
71 #endif
72     return(SystemAllocateMemory(sizeof(GAME_MOVE)));
73 }
74
75 static void 
76 _ReturnMoveToPool(GAME_MOVE *p)
77 /**
78
79 Routine description:
80
81     Return a GAME_MOVE to the pool.
82
83 Parameters:
84
85     GAME_MOVE *p
86
87 Return value:
88
89     static void
90
91 **/
92 {
93     ULONG x = (ULONG)((BYTE *)p - (BYTE *)&g_MovePool);
94     
95     x /= sizeof(GAME_MOVE);
96     if (x < MOVE_POOL_SIZE)
97     {
98         g_MovePool[x].fInUse = FALSE;
99     }
100     else
101     {
102 #ifdef DEBUG
103         g_uAllocs--;
104 #endif
105         SystemFreeMemory(p);
106     }
107 }
108
109 static void 
110 _FreeIfNotNull(void *p)
111 /**
112
113 Routine description:
114
115     Free a pointer if it's not null.
116
117 Parameters:
118
119     void *p
120
121 Return value:
122
123     static void
124
125 **/
126 {
127     if (NULL != p)
128     {
129         SystemFreeMemory(p);
130 #ifdef DEBUG
131         g_uAllocs--;
132 #endif
133     }
134 }
135
136 static void 
137 _FreeGameMove(GAME_MOVE *p)
138 /**
139
140 Routine description:
141
142     Free memory allocated to store a game move.
143
144 Parameters:
145
146     GAME_MOVE *p
147
148 Return value:
149
150     static void
151
152 **/
153 {
154     ASSERT(NULL != p);
155     
156     _FreeIfNotNull(p->szComment);
157     _FreeIfNotNull(p->szDecoration);
158     _FreeIfNotNull(p->szMoveInSan);
159     _FreeIfNotNull(p->szMoveInIcs);
160     _FreeIfNotNull(p->szUndoPositionFen);
161     _ReturnMoveToPool(p);
162 }
163
164 static void 
165 _FreeGameList(void)
166 /**
167
168 Routine description:
169
170     Free the whole game list.
171
172 Parameters:
173
174     void
175
176 Return value:
177
178     static void
179
180 **/
181 {
182     DLIST_ENTRY *p;
183     GAME_MOVE *q;
184
185     if (NULL != g_GameData.sMoveList.pFlink)
186     {
187         while(FALSE == IsListEmpty(&(g_GameData.sMoveList)))
188         {
189             p = RemoveHeadList(&(g_GameData.sMoveList));
190             q = CONTAINING_STRUCT(p, GAME_MOVE, links);
191             ASSERT(NULL != q);
192             _FreeGameMove(q);
193         }
194     }
195     _FreeIfNotNull(g_GameData.sHeader.szGameDescription);
196     _FreeIfNotNull(g_GameData.sHeader.szLocation);
197     _FreeIfNotNull(g_GameData.sHeader.sPlayer[WHITE].szName);
198     _FreeIfNotNull(g_GameData.sHeader.sPlayer[WHITE].szDescription);
199     _FreeIfNotNull(g_GameData.sHeader.sPlayer[BLACK].szName);
200     _FreeIfNotNull(g_GameData.sHeader.sPlayer[BLACK].szDescription);
201     _FreeIfNotNull(g_GameData.sHeader.szInitialFen);
202     memset(&g_GameData, 0, sizeof(g_GameData));
203 }
204
205 void
206 ResetGameList(void)
207 /**
208
209 Routine description:
210
211     Reset the game list (between games etc...)
212
213 Parameters:
214
215     void
216
217 Return value:
218
219     void
220
221 **/
222 {
223     _FreeGameList();
224     ASSERT(g_uAllocs == 0);
225     memset(g_MovePool, 0, sizeof(g_MovePool));
226     g_uMoveNumber[BLACK] = g_uMoveNumber[WHITE] = 1;
227     g_uToMove = WHITE;
228     InitializeListHead(&(g_GameData.sMoveList));
229     g_GameData.sHeader.result.eResult = RESULT_IN_PROGRESS;
230 }
231
232 FLAG 
233 SetRootPosition(CHAR *szFen)
234 /**
235
236 Routine description:
237
238     Set the root position.
239
240 Parameters:
241
242     CHAR *szFen
243
244 Return value:
245
246     FLAG
247
248 **/
249 {
250     if (TRUE == FenToPosition(&g_RootPosition, szFen))
251     {
252         ResetGameList();
253         g_uToMove = g_RootPosition.uToMove;
254         TellGamelistThatIPlayColor(FLIP(g_RootPosition.uToMove));
255         if (0 != strcmp(szFen, STARTING_POSITION_IN_FEN))
256         {
257             g_GameData.sHeader.szInitialFen = STRDUP(szFen);
258 #ifdef DEBUG
259             if (g_GameData.sHeader.szInitialFen)
260             {
261                 g_uAllocs++;
262             }
263 #endif
264         }
265         if (g_Options.fShouldPost)
266         {
267             DumpPosition(&g_RootPosition);
268         }
269         return(TRUE);
270     }
271     return(FALSE);
272 }
273
274 POSITION *
275 GetRootPosition(void)
276 /**
277
278 Routine description:
279
280     Get the root position.
281
282 Parameters:
283
284     void
285
286 Return value:
287
288     POSITION
289
290 **/
291 {
292 #ifdef DEBUG
293     static POSITION p;
294
295     memcpy(&p, &g_RootPosition, sizeof(POSITION));
296     
297     return(&p);
298 #else
299     return(&g_RootPosition);
300 #endif
301 }
302
303 void 
304 SetGameResultAndDescription(GAME_RESULT result)
305 /**
306
307 Routine description:
308
309     Set the game result and a description (both for PGN).
310
311 Parameters:
312
313     GAME_RESULT result
314     
315 Return value:
316
317     void
318
319 **/
320 {
321     g_GameData.sHeader.result = result;
322 }
323
324 GAME_RESULT GetGameResult(void)
325 /**
326
327 Routine description:
328
329     Get the game result.
330
331 Parameters:
332
333     void
334
335 Return value:
336
337     INT
338
339 **/
340 {
341     return g_GameData.sHeader.result;
342 }
343
344
345 GAME_MOVE *
346 GetNthOfficialMoveRecord(ULONG n)
347 /**
348
349 Routine description:
350
351    Get the official move record for move N.
352
353 Parameters:
354
355     ULONG n
356
357 Return value:
358
359     GAME_MOVE
360
361 **/
362 {
363     DLIST_ENTRY *p = g_GameData.sMoveList.pFlink;
364     GAME_MOVE *q;
365     
366     while((p != &(g_GameData.sMoveList)) &&
367           (n != 0))
368     {
369         p = p->pFlink;
370         n--;
371     }
372     
373     if (n == 0)
374     {
375         q = CONTAINING_STRUCT(p, GAME_MOVE, links);
376         return(q);
377     }
378     return(NULL);
379 }
380
381
382 FLAG 
383 DoesSigAppearInOfficialGameList(UINT64 u64Sig)
384 /**
385
386 Routine description:
387
388     Does sig X appear in the official move list?
389
390 Parameters:
391
392     UINT64 u64Sig
393
394 Return value:
395
396     FLAG
397
398 **/
399 {
400     DLIST_ENTRY *p = g_GameData.sMoveList.pBlink;
401     GAME_MOVE *q;
402
403     while(p != &(g_GameData.sMoveList))
404     {
405         q = CONTAINING_STRUCT(p, GAME_MOVE, links);
406         if (q->u64PositionSigAfterMove == u64Sig)
407         {
408             return(TRUE);
409         }
410         if ((q->mv.pCaptured) || (IS_PAWN(q->mv.pMoved)))
411         {
412             break;
413         }
414         p = p->pBlink;
415     }
416     return(FALSE);
417 }
418
419 ULONG CountOccurrancesOfSigInOfficialGameList(UINT64 u64Sig)
420 /**
421
422 Routine description:
423
424     How many times does sig X appear in the official move list?
425
426 Parameters:
427
428     UINT64 u64Sig
429
430 Return value:
431
432     ULONG
433
434 **/
435 {
436     DLIST_ENTRY *p = g_GameData.sMoveList.pBlink;
437     GAME_MOVE *q;
438     ULONG uCount = 0;
439
440     while(p != &(g_GameData.sMoveList))
441     {
442         q = CONTAINING_STRUCT(p, GAME_MOVE, links);
443         if (q->u64PositionSigAfterMove == u64Sig)
444         {
445             uCount++;
446         }
447         if ((q->mv.pCaptured) || (IS_PAWN(q->mv.pMoved)))
448         {
449             break;
450         }
451         p = p->pBlink;
452     }
453     return(uCount);
454 }
455
456
457 FLAG 
458 IsLegalDrawByRepetition(void)
459 /**
460
461 Routine description:
462
463     Have we just drawn the game by repetition?
464
465 Parameters:
466
467     void
468
469 Return value:
470
471     FLAG
472
473 **/
474 {
475     POSITION *pos = GetRootPosition();
476     UINT64 u64Sig = (pos->u64PawnSig ^ pos->u64NonPawnSig);
477
478     if (3 == CountOccurrancesOfSigInOfficialGameList(u64Sig))
479     {
480         return(TRUE);
481     }
482     return(FALSE);
483 }
484
485
486 void 
487 DumpGameList(void)
488 /**
489
490 Routine description:
491
492     Dump the move list to stdout.
493
494 Parameters:
495
496     void
497
498 Return value:
499
500     void
501
502 **/
503 {
504     GAME_MOVE *q;
505     ULONG u = 0;
506
507     Trace("\twhite\tblack\n"
508           "\t-----\t-----\n");
509     q = GetNthOfficialMoveRecord(u);
510     while((q != NULL) && (q->mv.uMove != 0))
511     {
512         if (GET_COLOR(q->mv.pMoved) == WHITE)
513         {
514             Trace("%2u.\t%s\t", q->uNumber, q->szMoveInSan);
515         }
516         else
517         {
518             Trace("%s\n", q->szMoveInSan);
519         }
520         u++;
521         q = GetNthOfficialMoveRecord(u);
522     }
523     Trace("\n");
524 }
525
526 void 
527 DumpPgn(void)
528 /**
529
530 Routine description:
531
532     Dump the PGN of the current game to stdout.
533
534 Parameters:
535
536     void
537
538 Return value:
539
540     void
541
542 **/
543 {
544     DLIST_ENTRY *p = g_GameData.sMoveList.pFlink;
545     GAME_MOVE *q;
546     ULONG u = 0;
547
548     Trace("[Event \"Computer Chess Game\"]\n");
549     if (g_GameData.sHeader.szLocation != NULL)
550     {
551         Trace("[Site \"%s\"]\n", g_GameData.sHeader.szLocation);
552     }
553     Trace("[Date \"%s\"]\n", SystemGetDateString());
554     Trace("[Round 0]\n");
555     Trace("[White \"%s\"]\n", g_GameData.sHeader.sPlayer[WHITE].szName);
556     Trace("[Black \"%s\"]\n", g_GameData.sHeader.sPlayer[BLACK].szName);
557     switch(g_GameData.sHeader.result.eResult)
558     {
559         case RESULT_BLACK_WON:
560             Trace("[Result \"0-1\"]\n");
561             break;
562         case RESULT_WHITE_WON:
563             Trace("[Result \"1-0\"]\n");
564             break;
565         case RESULT_DRAW:
566             Trace("[Result \"1/2-1/2\"]\n");
567             break;
568         default:
569             Trace("[Result \"*\"]\n");
570             break;
571     }
572     if (g_GameData.sHeader.sPlayer[WHITE].uRating != 0)
573     {
574         Trace("[WhiteElo \"%u\"]\n", g_GameData.sHeader.sPlayer[WHITE].uRating);
575     }
576     if (g_GameData.sHeader.sPlayer[BLACK].uRating != 0)
577     {
578         Trace("[BlackElo \"%u\"]\n", g_GameData.sHeader.sPlayer[BLACK].uRating);
579     }
580     if (NULL != g_GameData.sHeader.szInitialFen)
581     {
582         Trace("[InitialFEN \"%s\"]\n", g_GameData.sHeader.szInitialFen);
583     }
584     // [ECO: XX]
585     Trace("[Time \"%s\"]\n", SystemGetTimeString());
586     /*
587 9.6.1: Tag: TimeControl
588
589 This uses a list of one or more time control fields.  Each field contains a
590 descriptor for each time control period; if more than one descriptor is present
591 then they are separated by the colon character (":").  The descriptors appear
592 in the order in which they are used in the game.  The last field appearing is
593 considered to be implicitly repeated for further control periods as needed.
594
595 There are six kinds of TimeControl fields.
596
597 The first kind is a single question mark ("?") which means that the time
598 control mode is unknown.  When used, it is usually the only descriptor present.
599
600 The second kind is a single hyphen ("-") which means that there was no time
601 control mode in use.  When used, it is usually the only descriptor present.
602
603 The third Time control field kind is formed as two positive integers separated
604 by a solidus ("/") character.  The first integer is the number of moves in the
605 period and the second is the number of seconds in the period.  Thus, a time
606 control period of 40 moves in 2 1/2 hours would be represented as "40/9000".
607
608 The fourth TimeControl field kind is used for a "sudden death" control period.
609 It should only be used for the last descriptor in a TimeControl tag value.  It
610 is sometimes the only descriptor present.  The format consists of a single
611 integer that gives the number of seconds in the period.  Thus, a blitz game
612 would be represented with a TimeControl tag value of "300".
613
614 The fifth TimeControl field kind is used for an "incremental" control period.
615 It should only be used for the last descriptor in a TimeControl tag value and
616 is usually the only descriptor in the value.  The format consists of two
617 positive integers separated by a plus sign ("+") character.  The first integer
618 gives the minimum number of seconds allocated for the period and the second
619 integer gives the number of extra seconds added after each move is made.  So,
620 an incremental time control of 90 minutes plus one extra minute per move would
621 be given by "4500+60" in the TimeControl tag value.
622
623 The sixth TimeControl field kind is used for a "sandclock" or "hourglass"
624 control period.  It should only be used for the last descriptor in a
625 TimeControl tag value and is usually the only descriptor in the value.  The
626 format consists of an asterisk ("*") immediately followed by a positive
627 integer.  The integer gives the total number of seconds in the sandclock
628 period.  The time control is implemented as if a sandclock were set at the
629 start of the period with an equal amount of sand in each of the two chambers
630 and the players invert the sandclock after each move with a time forfeit
631 indicated by an empty upper chamber.  Electronic implementation of a physical
632 sandclock may be used.  An example sandclock specification for a common three
633 minute egg timer sandclock would have a tag value of "*180".
634
635 Additional TimeControl field kinds will be defined as necessary.
636     */
637     
638     Trace("\n");
639
640     while(p != &(g_GameData.sMoveList))
641     {
642         q = CONTAINING_STRUCT(p, GAME_MOVE, links);
643         if (GET_COLOR(q->mv.pMoved) == WHITE)
644         {
645             Trace("%2u. %s ", q->uNumber, q->szMoveInSan);
646         }
647         else
648         {
649             Trace("%s ", q->szMoveInSan);
650         }
651         u++;
652         if (u > 10)
653         {
654             Trace("\n");
655             u = 0;
656         }
657         p = p->pFlink;
658     }
659     switch(g_GameData.sHeader.result.eResult)
660     {
661         case RESULT_BLACK_WON:
662             Trace("0-1\n");
663             break;
664         case RESULT_WHITE_WON:
665             Trace("1-0\n");
666             break;
667         case RESULT_DRAW:
668             Trace("1/2-1/2\n");
669             break;
670         default:
671             Trace("*\n");
672             break;
673     }
674     Trace("\n");
675     if (strlen(g_GameData.sHeader.result.szDescription)) {
676         Trace("%s\n", g_GameData.sHeader.result.szDescription);
677     }
678 }
679
680 FLAG 
681 OfficiallyTakebackMove(void)
682 /**
683
684 Routine description:
685
686     Take back a move (i.e. unmake it and delete it from the move
687     list).
688
689 Parameters:
690
691     void
692
693 Return value:
694
695     FLAG
696
697 **/
698 {
699     DLIST_ENTRY *p;
700     GAME_MOVE *q;
701     POSITION *pos = &g_RootPosition;
702
703     if (FALSE == IsListEmpty(&(g_GameData.sMoveList)))
704     {
705         p = RemoveTailList(&(g_GameData.sMoveList));
706         ASSERT(p);
707         q = CONTAINING_STRUCT(p, GAME_MOVE, links);
708         if (FALSE == FenToPosition(pos, q->szUndoPositionFen))
709         {
710             UtilPanic(INCONSISTENT_STATE,
711                       pos,
712                       q->szUndoPositionFen,
713                       NULL,
714                       NULL,
715                       __FILE__, __LINE__);
716         }
717         _FreeGameMove(q);
718         VerifyPositionConsistency(pos, FALSE);
719         
720         ASSERT(g_uMoveNumber[g_uToMove] > 0);
721         g_uMoveNumber[g_uToMove] -= 1;
722         g_uToMove = FLIP(g_uToMove);
723         return(TRUE);
724     }
725     return(FALSE);
726 }
727
728 FLAG 
729 OfficiallyMakeMove(MOVE mv, 
730                    SCORE iMoveScore,
731                    FLAG fFast)
732 /**
733
734 Routine description:
735
736     Officially make a move (i.e. add it to the game list)
737
738 Parameters:
739
740     MOVE mv,
741     SCORE iMoveScore
742
743 Return value:
744
745     FLAG
746
747 **/
748 {
749     static SEARCHER_THREAD_CONTEXT ctx;
750     FLAG fRet = FALSE;
751     GAME_MOVE *q;
752     POSITION *pos = &g_RootPosition;
753     UINT64 u64SigBefore = pos->u64PawnSig ^ pos->u64NonPawnSig;
754     
755     ReInitializeSearcherContext(pos, &ctx);
756     ASSERT(mv.uMove);
757     ASSERT(GET_COLOR(mv.pMoved) == g_uToMove);
758     if (FALSE == MakeUserMove(&ctx, mv))
759     {
760         goto end;
761     }
762     
763     q = _GetMoveFromPool();
764     if (NULL == q)
765     {
766         goto end;
767     }
768     q->uNumber = g_uMoveNumber[g_uToMove];
769     ASSERT(q->uNumber > 0);
770     g_uMoveNumber[g_uToMove] += 1;
771     ASSERT(g_uMoveNumber[g_uToMove] != 0);
772     q->mv.uMove = mv.uMove;
773     q->szComment = NULL;
774     q->szDecoration = NULL;
775     q->iMoveScore = iMoveScore;
776
777     q->szMoveInSan = STRDUP(MoveToSan(mv, pos));
778 #ifdef DEBUG
779     if (q->szMoveInSan)
780     {
781         g_uAllocs++;
782     }
783 #endif
784     q->szMoveInIcs = STRDUP(MoveToIcs(mv));
785 #ifdef DEBUG
786     if (q->szMoveInIcs)
787     {
788         g_uAllocs++;
789     }
790 #endif
791     q->szUndoPositionFen = PositionToFen(pos);
792 #ifdef DEBUG
793     g_uAllocs++;
794 #endif
795     InsertTailList(&(g_GameData.sMoveList), &(q->links));
796     q->u64PositionSigAfterMove = (ctx.sPosition.u64NonPawnSig ^ 
797                                   ctx.sPosition.u64PawnSig);
798     q->u64PositionSigBeforeMove = u64SigBefore;
799     g_uToMove = FLIP(g_uToMove);
800
801     //
802     // Update the root position
803     //
804     memcpy(pos, &(ctx.sPosition), sizeof(POSITION));
805     VerifyPositionConsistency(&(ctx.sPosition), FALSE);
806     fRet = TRUE;
807     
808  end:
809     return(fRet);
810 }
811
812 ULONG 
813 GetMyColor(void)
814 /**
815
816 Routine description:
817
818     What color am I playing?
819
820 Parameters:
821
822     void
823
824 Return value:
825
826     ULONG
827
828 **/
829 {
830     switch(g_Options.ePlayMode)
831     {
832         case I_PLAY_WHITE:
833             return(WHITE);
834         case I_PLAY_BLACK:
835         default:
836             return(BLACK);
837     }
838 }
839
840 ULONG 
841 GetOpponentsColor(void)
842 /**
843
844 Routine description:
845
846     What color is the opponent playing?
847
848 Parameters:
849
850     void
851
852 Return value:
853
854     ULONG
855
856 **/
857 {
858     return(FLIP(GetMyColor()));
859 }
860
861 void 
862 SetOpponentsName(CHAR *sz)
863 /**
864
865 Routine description:
866
867     Set my opponent's name for the PGN record.  Hello, my name is
868     Inigo Montoya.  You killed my father.  Prepare to die.
869
870 Parameters:
871
872     CHAR *sz
873
874 Return value:
875
876     void
877
878 **/
879 {
880     _FreeIfNotNull(g_GameData.sHeader.sPlayer[GetOpponentsColor()].szName);
881     g_GameData.sHeader.sPlayer[GetOpponentsColor()].szName = STRDUP(sz);
882 #ifdef DEBUG
883     if (g_GameData.sHeader.sPlayer[GetOpponentsColor()].szName)
884     {
885         g_uAllocs++;
886     }
887 #endif
888 }
889
890 void SetMyName(void)
891 /**
892
893 Routine description:
894
895     Set my name for the PGN record.  Some people call me... Tim?
896
897 Parameters:
898
899     void
900
901 Return value:
902
903     void
904
905 **/
906 {
907     ULONG u = GetMyColor();
908     
909     _FreeIfNotNull(g_GameData.sHeader.sPlayer[u].szName);
910     g_GameData.sHeader.sPlayer[u].szName = STRDUP("typhoon");
911 #ifdef DEBUG
912     if (g_GameData.sHeader.sPlayer[u].szName)
913     {
914         g_uAllocs++;
915     }
916 #endif
917     _FreeIfNotNull(g_GameData.sHeader.sPlayer[u].szDescription);
918     g_GameData.sHeader.sPlayer[u].szDescription =
919         STRDUP("Ver: " VERSION " Build Time: " __TIME__ " " __DATE__);
920 #ifdef DEBUG
921     if (g_GameData.sHeader.sPlayer[u].szDescription)
922     {
923         g_uAllocs++;
924     }
925 #endif
926     g_GameData.sHeader.sPlayer[u].fIsComputer = TRUE;
927 }
928
929 void 
930 SetMyRating(ULONG u)
931 /**
932
933 Routine description:
934
935     Set my rating for the PGN record.
936
937 Parameters:
938
939     ULONG u
940
941 Return value:
942
943     void
944
945 **/
946 {
947     g_GameData.sHeader.sPlayer[GetMyColor()].uRating = u;
948 }
949
950 void 
951 SetOpponentsRating(ULONG u)
952 /**
953
954 Routine description:
955
956     Set the opponent's rating for the PGN record.
957
958 Parameters:
959
960     ULONG u
961
962 Return value:
963
964     void
965
966 **/
967 {
968     g_GameData.sHeader.sPlayer[GetOpponentsColor()].uRating = u;
969 }
970
971 void 
972 TellGamelistThatIPlayColor(ULONG u)
973 /**
974
975 Routine description:
976
977     The gamelist needs to know what color the computer is playing.
978     This routine sets it.
979
980 Parameters:
981
982     ULONG u
983
984 Return value:
985
986     void
987
988 **/
989 {
990     ULONG uOldColor = GetMyColor();
991     GAME_PLAYER x;
992     
993     ASSERT(IS_VALID_COLOR(u));
994     ASSERT(IS_VALID_COLOR(uOldColor));
995
996     if (u != uOldColor)
997     {
998         x = g_GameData.sHeader.sPlayer[uOldColor];
999         g_GameData.sHeader.sPlayer[uOldColor] =
1000             g_GameData.sHeader.sPlayer[u];
1001         g_GameData.sHeader.sPlayer[u] = x;
1002     }
1003 }
1004
1005 ULONG 
1006 GetMoveNumber(ULONG uColor)
1007 /**
1008
1009 Routine description:
1010
1011     What move number is it?
1012
1013 Parameters:
1014
1015     void
1016
1017 Return value:
1018
1019     ULONG
1020
1021 **/
1022 {
1023     return(g_uMoveNumber[uColor]);
1024 }
1025
1026 void
1027 MakeStatusLine(void)
1028 /**
1029
1030 Routine description:
1031
1032 Parameters:
1033
1034     void
1035
1036 Return value:
1037
1038     void
1039
1040 **/
1041 {
1042     char buf[SMALL_STRING_LEN_CHAR];
1043     ASSERT(IS_VALID_COLOR(g_uToMove));
1044
1045     if (g_Options.fStatusLine) 
1046     {
1047         strcpy(buf, "_\0");
1048         if (!g_Options.fRunningUnderXboard) 
1049         {
1050             if ((TRUE == g_Options.fPondering) &&
1051                 (g_Options.mvPonder.uMove != 0))
1052             {
1053                 snprintf(buf, SMALL_STRING_LEN_CHAR - 1,
1054                          "_[pondering %s] ", 
1055                          MoveToSan(g_Options.mvPonder, &g_RootPosition));
1056             }
1057             snprintf(buf, SMALL_STRING_LEN_CHAR - strlen(buf),
1058                      "%s%s(move %u)_", 
1059                      buf, 
1060                      (g_uToMove == WHITE) ? "white" : "black",
1061                      g_uMoveNumber[g_uToMove]);
1062             Trace("%s\n", buf);
1063         }
1064     }
1065 }
1066
1067 static void 
1068 _ParsePgnHeaderLine(CHAR *p, CHAR **ppVar, CHAR **ppVal)
1069 /**
1070
1071 Routine description:
1072
1073 Parameters:
1074
1075     CHAR *p,
1076     CHAR **ppVar,
1077     CHAR **ppVal
1078
1079 Return value:
1080
1081     static void
1082
1083 **/
1084 {
1085     *ppVar = NULL;
1086     *ppVal = NULL;
1087     if (*p != '[') return;
1088     
1089     p++;
1090     *ppVar = p;
1091     while(*p && !isspace(*p)) p++;
1092
1093     p++;
1094     *ppVal = p;
1095     while(*p && *p != ']') p++;
1096 }
1097
1098 static void 
1099 _ParsePgnHeaderTag(CHAR *p)
1100 /**
1101
1102 Routine description:
1103
1104     Parse PGN headers and extract people's names, ratings, game
1105     result, etc...
1106
1107 Parameters:
1108
1109     CHAR *p
1110
1111 Return value:
1112
1113     static void
1114
1115 **/
1116 {
1117     CHAR *pVar, *pVal;
1118     
1119     _ParsePgnHeaderLine(p, &pVar, &pVal);
1120     if ((NULL == pVar) || (NULL == pVal))
1121     {
1122         return;
1123     }
1124     
1125     if (!STRNCMPI(pVar, "BLACK ", 6))
1126     {
1127         p = pVal;
1128         while(*p && *p != ']') p++;
1129         if (*p)
1130         {
1131             *p = '\0';
1132             _FreeIfNotNull(g_GameData.sHeader.sPlayer[BLACK].szName);
1133             g_GameData.sHeader.sPlayer[BLACK].szName = STRDUP(pVal);
1134 #ifdef DEBUG
1135             if (g_GameData.sHeader.sPlayer[BLACK].szName)
1136             {
1137                 g_uAllocs++;
1138             }
1139 #endif
1140             *p = ']';
1141             p++;
1142         }
1143     }
1144     else if (!STRNCMPI(pVar, "WHITE ", 6))
1145     {
1146         p = pVal;
1147         while(*p && *p != ']') p++;
1148         if (*p)
1149         {
1150             *p = '\0';
1151             _FreeIfNotNull(g_GameData.sHeader.sPlayer[WHITE].szName);
1152             g_GameData.sHeader.sPlayer[WHITE].szName = STRDUP(pVal);
1153 #ifdef DEBUG
1154             if (g_GameData.sHeader.sPlayer[WHITE].szName)
1155             {
1156                 g_uAllocs++;
1157             }
1158 #endif
1159             *p = ']';
1160             p++;
1161         }
1162     }
1163     else if (!STRNCMPI(pVar, "RESULT", 6))
1164     {
1165         p = pVal;
1166         if (*p == '"') p++;
1167         if (!STRNCMPI(p, "1-0", 3))
1168         {
1169             GAME_RESULT res;
1170             res.eResult = RESULT_WHITE_WON;
1171             strcpy(res.szDescription, "PGN game");
1172             SetGameResultAndDescription(res);
1173         }
1174         else if (!STRNCMPI(p, "0-1", 3))
1175         {
1176             GAME_RESULT res;
1177             res.eResult = RESULT_BLACK_WON;
1178             strcpy(res.szDescription, "PGN game");
1179             SetGameResultAndDescription(res);
1180         }
1181         else if (!STRNCMPI(p, "1/2", 3))
1182         {
1183             GAME_RESULT res;
1184             res.eResult = RESULT_DRAW;
1185             strcpy(res.szDescription, "PGN game");
1186             SetGameResultAndDescription(res);
1187         }
1188         while(*p && *p != ']') p++;
1189         if (*p) p++;
1190     }
1191     //
1192     // TODO: add others
1193     //
1194 }    
1195
1196 FLAG 
1197 LoadPgn(CHAR *szPgn)
1198 /**
1199
1200 Routine description:
1201
1202     Load a PGN blob and create a move list from it.
1203
1204 Parameters:
1205
1206     CHAR *szPgn
1207
1208 Return value:
1209
1210     FLAG
1211
1212 **/
1213 {
1214     POSITION *pos;
1215     CHAR szMove[16];
1216     MOVE mv;
1217     CHAR *p = szPgn;
1218     ULONG uMoveCount = 0;
1219     ULONG x;
1220     FLAG fRet = FALSE;
1221     FLAG fOldPost = g_Options.fShouldPost;
1222     
1223     g_Options.fShouldPost = FALSE;
1224     ResetGameList();
1225     SetRootToInitialPosition();
1226     pos = GetRootPosition();
1227     
1228     //
1229     // Get rid of newlines, non-space spaces and .'s
1230     // 
1231     while(*p)
1232     {
1233         if (isspace(*p)) *p = ' ';
1234         if (*p == '.') *p = ' ';
1235         p++;
1236     }
1237     
1238     p = szPgn;
1239     while(*p)
1240     {
1241         while(*p && isspace(*p)) p++;
1242         while(*p && (isdigit(*p) || (*p == '.'))) p++;
1243         if (*p == '[')
1244         {
1245             _ParsePgnHeaderTag(p);
1246             while(*p && *p != ']') p++;
1247             if (*p) p++;           
1248         }
1249         else if (*p == '{')
1250         {
1251             while(*p && *p != '}') p++;
1252             if (*p) p++;
1253         }
1254         else if (isalpha(*p))
1255         {
1256             //
1257             // Copy the thing we think is a move.
1258             //
1259             memset(szMove, 0, sizeof(szMove));
1260             x = 0;
1261             while(*p && (!isspace(*p)))
1262             {
1263                 if (x < (ARRAY_LENGTH(szMove) - 1)) 
1264                 {
1265                     szMove[x] = *p;
1266                 }
1267                 x++;
1268                 p++;
1269             }
1270             
1271             switch (LooksLikeMove(szMove))
1272             {
1273                 case MOVE_SAN:
1274                     mv = ParseMoveSan(szMove, pos);
1275                     break;
1276                 case MOVE_ICS:
1277                     mv = ParseMoveIcs(szMove, pos);
1278                     break;
1279                 default:
1280                     mv.uMove = 0;
1281                     break;
1282             }
1283                 
1284             //if (FALSE == SanityCheckMove(pos, mv))
1285             // {
1286             //    Trace("Error in PGN(?)  Bad chunk \"%s\"\n", szMove);
1287             //    goto end;
1288             // }
1289
1290             if (FALSE == OfficiallyMakeMove(mv, 0, TRUE))
1291             {
1292                 Trace("Error in PGN(?)  Can't play move \"%s\"\n", szMove);
1293                 goto end;
1294             }
1295             pos = GetRootPosition();
1296             uMoveCount++;
1297         }
1298         else
1299         {
1300             while(*p && !isspace(*p)) p++;
1301         }
1302     }
1303     fRet = TRUE;
1304  end:
1305     g_Options.fShouldPost = fOldPost;
1306     return(fRet);
1307 }
1308
1309
1310 char *
1311 CleanupString(char *pIn)
1312 /**
1313
1314 Routine description:
1315
1316     Ckeans up a PGN tag containing part of the opening line
1317     name... gets rid of the quotes around the name and the newline.
1318
1319 Parameters:
1320
1321     char *pIn
1322
1323 Return value:
1324
1325     char
1326
1327 **/
1328 {
1329     static char buf[256];
1330     char *p = pIn;
1331     char *q = buf;
1332
1333     //
1334     // Skip leading whitespace in input
1335     //
1336     buf[0] = '\0';
1337     while(isspace(*p)) p++;
1338     if (*p == '\0') return(buf);
1339
1340     do
1341     {
1342         //
1343         // Copy A-Z, 0-9, space, -, ., (, or )
1344         // 
1345         // Ignore others
1346         // 
1347         if ((*p == ' ') || 
1348             (isalpha(*p)) || 
1349             (isdigit(*p)) ||
1350             (*p == '-') ||
1351             (*p == '.') ||
1352             (*p == ',') ||
1353             (*p == '(') ||
1354             (*p == ')'))
1355         {
1356             *q = *p;
1357             q++;
1358             if ((q - buf) > (sizeof(buf) - 1)) break;
1359         }
1360         p++;
1361     }
1362     while(*p);
1363
1364     *q = '\0';
1365     buf[255] = 0;
1366     return(buf);
1367 }