Update codebase to remove clang warnings (and a couple of legit errors
[typhoon.git] / src / board.c
1 /**
2
3 Copyright (c) Scott Gasch
4
5 Module Name:
6
7     board.c
8
9 Abstract:
10
11     Chess-board related functions.
12
13 Author:
14
15     Scott Gasch ([email protected]) 15 Apr 2004
16
17 Revision History:
18
19     $Id: board.c 345 2007-12-02 22:56:42Z scott $
20
21 **/
22
23 #include "chess.h"
24
25 void
26 SetRootToInitialPosition(void)
27 /**
28
29 Routine description:
30
31     Sets the root position to the initial position.
32
33 Parameters:
34
35     none
36
37 Return value:
38
39     void
40
41 **/
42 {
43 #ifdef DEBUG
44     POSITION *pos;
45 #endif
46
47     (void)SetRootPosition(STARTING_POSITION_IN_FEN);
48 #ifdef DEBUG
49     pos = GetRootPosition();
50
51     //
52     // Sanity check the sig system.  Note: if this changes then any
53     // opening books need to be reconstructed.
54     //
55     ASSERT(pos->u64NonPawnSig == 0xd46f3e84d897a443ULL);
56     ASSERT(pos->u64PawnSig == 0xa9341a54d7352d3cULL);
57
58     ASSERT_ASM_ASSUMPTIONS;
59 #endif
60 }
61
62
63 CHAR *
64 DrawTextBoardFromPosition(POSITION *pos)
65 /**
66
67 Routine description:
68
69     Draw a text-based representation of the board configuration in
70     POSITION pos and returned it as a string.  Note: this function is
71     _NOT_ thread safe.
72
73 Parameters:
74
75     POSITION *pos : position to draw
76
77 Return value:
78
79     static char * : my drawing
80
81 **/
82 {
83     static char buf[MEDIUM_STRING_LEN_CHAR];
84     COOR f, r;
85
86     memset(buf, 0, sizeof(buf));
87     strcpy(buf, "    0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07\n");
88     strcat(buf, "   +---------------------------------------+\n");
89     for (r = 8; r >= 1; r--)
90     {
91         sprintf(buf, "%s %u |", buf, r);
92
93         for (f = A; f <= H; f++)
94         {
95             if (IS_WHITE_SQUARE_FR(f, r))
96             {
97                 if (IS_EMPTY(pos->rgSquare[FILE_RANK_TO_COOR(f,r)].pPiece))
98                 {
99                     sprintf(buf, "%s%s", buf, "    |");
100                 }
101                 else
102                 {
103                     sprintf(buf, "%s %2s |", buf,
104                         PieceAbbrev(pos->rgSquare[FILE_RANK_TO_COOR(f,r)].pPiece));
105                 }
106             }
107             else
108             {
109                 if (IS_EMPTY(pos->rgSquare[FILE_RANK_TO_COOR(f,r)].pPiece))
110                 {
111                     sprintf(buf, "%s%s", buf, "::::|");
112                 }
113                 else
114                 {
115                     sprintf(buf, "%s:%2s:|", buf,
116                         PieceAbbrev(pos->rgSquare[FILE_RANK_TO_COOR(f,r)].pPiece));
117                 }
118             }
119         }
120         sprintf(buf, "%s 0x%x0\n", buf, (8 - r));
121     }
122     strcat(buf, "   +---------------------------------------+\n");
123     strcat(buf, "     A    B    C    D    E    F    G    H\n\n");
124     return(buf);
125 }
126
127
128 FLAG
129 VerifyPositionConsistency(POSITION *pos,
130                           FLAG fContinue)
131 /**
132
133 Routine description:
134
135     Verifies the internal consistency of a POSITION structure.  Looks
136     for defects like bad piece lists, bad piece counts, bad material
137     counts, etc...
138
139 Parameters:
140
141     POSITION *pos : the position to verify
142
143 Return value:
144
145     FLAG : TRUE if consistent, FALSE otherwise
146
147 **/
148 {
149     static char *szExplainations[] = {
150         "Pawn signature mismatch",
151         "Main signature mismatch",
152         "Invalid enpassant square",
153         "Material balance for white != -Material balance for black",
154         "White castled already and can still castle",
155         "Black castled already and can still castle",
156         "Pawn list points to non-pawn square on board",
157         "Square pointed to by pawn list doesn't point back",
158         "Piece list points to non-piece square on board",
159         "Square pointed to by the non-piece list doesn't point back",
160         "Non-king piece in 0th position of list",
161         "Invalid piece sitting in non-pawn list entry",
162         "Counted pawn material != expected material sum",
163         "Counted piece material != expected material sum",
164         "Total army material != expected material sum",
165         "More white square bishops than total bishop count",
166         "Strange bits flipped in board square",
167         "More pawns on board than accounted for in pawn material",
168         "More pieces on board than accounted for in piece material",
169         "Extra pieces on board that are not accounted for",
170         "Fifty move counter is too high",
171     };
172     ULONG u, v;
173     COOR c;
174     ULONG uPawnMaterial[2] = {0, 0};
175     ULONG uPawnCount[2] = {0, 0};
176     ULONG uNonPawnMaterial[2] = {0, 0};
177     ULONG uNonPawnCount[2][7];
178     ULONG uSigmaNonPawnCount[2] = {0, 0};
179     ULONG uWhiteSqBishopCount[2] = {0, 0};
180     UINT64 u64Computed;
181     PIECE p;
182     FLAG fRet = FALSE;
183     ULONG uReason = (ULONG)-1;
184
185     memset(uNonPawnCount, 0, sizeof(uNonPawnCount));
186     u64Computed = ComputeSig(pos);
187     if (pos->u64NonPawnSig != u64Computed)
188     {
189         uReason = 0;
190         goto end;
191     }
192
193     u64Computed = ComputePawnSig(pos);
194     if (pos->u64PawnSig != u64Computed)
195     {
196         uReason = 1;
197         goto end;
198     }
199
200     if (!VALID_EP_SQUARE(pos->cEpSquare))
201     {
202         uReason = 2;
203         goto end;
204     }
205
206     if (pos->iMaterialBalance[WHITE] != -pos->iMaterialBalance[BLACK])
207     {
208         uReason = 3;
209         goto end;
210     }
211
212     if (pos->iMaterialBalance[WHITE] !=
213         ((SCORE)(pos->uPawnMaterial[WHITE] + pos->uNonPawnMaterial[WHITE]) -
214          (SCORE)(pos->uPawnMaterial[BLACK] + pos->uNonPawnMaterial[BLACK])))
215     {
216         uReason = 3;
217         goto end;
218     }
219
220     FOREACH_COLOR(u)
221     {
222         //
223         // If a side has already castled it shouldn't have legal castling
224         // options.
225         //
226         if (TRUE == pos->fCastled[u])
227         {
228             if (u == WHITE)
229             {
230                 if (pos->bvCastleInfo & WHITE_CAN_CASTLE)
231                 {
232                     uReason = 4;
233                     goto end;
234                 }
235             }
236             else
237             {
238                 ASSERT(u == BLACK);
239                 if (pos->bvCastleInfo & BLACK_CAN_CASTLE)
240                 {
241                     uReason = 5;
242                     goto end;
243                 }
244             }
245         }
246
247         //
248         // Make sure the pawns are where they should be
249         //
250         for (v = 0; v < pos->uPawnCount[u]; v++)
251         {
252             c = pos->cPawns[u][v];
253             if (IS_ON_BOARD(c))
254             {
255                 if (pos->rgSquare[c].pPiece != ((PAWN << 1) | u))
256                 {
257                     uReason = 6;
258                     goto end;
259                 }
260
261                 if (pos->rgSquare[c].uIndex != v)
262                 {
263                     uReason = 7;
264                     goto end;
265                 }
266                 uPawnMaterial[u] += VALUE_PAWN;
267                 uPawnCount[u]++;
268             }
269         }
270
271         //
272         // Make sure the non-pawns are where they should be
273         //
274         for (v = 0; v < 16; v++)
275         {
276             c = pos->cNonPawns[u][v];
277             if (v < pos->uNonPawnCount[u][0])
278             {
279                 if (!IS_ON_BOARD(c))
280                 {
281                     uReason = 8;
282                     goto end;
283                 }
284                 else
285                 {
286                     p = pos->rgSquare[c].pPiece;
287                     if (pos->rgSquare[c].uIndex != v)
288                     {
289                         uReason = 9;
290                         goto end;
291                     }
292
293                     //
294                     // 0th spot must be a king
295                     //
296                     if ((v == 0) && (!IS_KING(p)))
297                     {
298                         uReason = 10;
299                         goto end;
300                     }
301
302                     if ((!IS_VALID_PIECE(p)) || (PIECE_COLOR(p) != u))
303                     {
304                         uReason = 11;
305                         goto end;
306                     }
307                     uNonPawnCount[u][PIECE_TYPE(p)]++;
308                     uNonPawnMaterial[u] += PIECE_VALUE(p);
309                     if ((IS_BISHOP(p)) &&
310                         (IS_WHITE_SQUARE_COOR(c)))
311                     {
312                         uWhiteSqBishopCount[u]++;
313                     }
314                 }
315             }
316             else
317             {
318                 //
319                 // The first off-board is the last, the list must not
320                 // have gaps in it followed by good piece data again.
321                 //
322                 break;
323             }
324         }
325     }
326
327     FOREACH_COLOR(u)
328     {
329         //
330         // Make sure the materials and counts match up with what we
331         // got when walking the piece lists
332         //
333         if ((pos->uPawnMaterial[u] != uPawnMaterial[u]) ||
334             (pos->uPawnCount[u] != uPawnCount[u]) ||
335             (pos->uPawnMaterial[u] != pos->uPawnCount[u] * VALUE_PAWN))
336         {
337             uReason = 12;
338             goto end;
339         }
340
341         for (v = KNIGHT; v <= KING; v++)
342         {
343             if (pos->uNonPawnCount[u][v] != uNonPawnCount[u][v])
344             {
345                 uReason = 13;
346                 goto end;
347             }
348             uSigmaNonPawnCount[u] += pos->uNonPawnCount[u][v];
349         }
350
351         //
352         // Note: the 0th spot in the array is the sum of all non pawns
353         //
354         if (uSigmaNonPawnCount[u] != pos->uNonPawnCount[u][0])
355         {
356             uReason = 14;
357             goto end;
358         }
359
360         //
361         // You can't have more white-square bishops than you have
362         // bishops.
363         //
364         if (pos->uWhiteSqBishopCount[u] > pos->uNonPawnCount[u][BISHOP])
365         {
366             uReason = 15;
367             goto end;
368         }
369     }
370
371     //
372     // Now walk the actual board and reduce the material counts we got
373     // by walking the piece lists.  If everything is ok then the
374     // material counts will end up at 0.  If there are pieces on the
375     // board that are not on a list or pieces in a list that are not
376     // on the board this will catch it.
377     //
378     FOREACH_SQUARE(c)
379     {
380         if (IS_ON_BOARD(c))
381         {
382             p = pos->rgSquare[c].pPiece;
383
384             if (EMPTY != p)
385             {
386                 //
387                 // Make sure none of these have strange bits flipped
388                 //
389                 if (p & 0xFFFFFFF0)
390                 {
391                     uReason = 16;
392                     goto end;
393                 }
394
395                 u = PIECE_COLOR(p);
396                 if (IS_PAWN(p))
397                 {
398                     uPawnMaterial[u] -= VALUE_PAWN;
399                     if (pos->rgSquare[c].uIndex > pos->uPawnCount[u])
400                     {
401                         uReason = 17;
402                         goto end;
403                     }
404                 }
405                 else
406                 {
407                     uNonPawnMaterial[u] -= PIECE_VALUE(p);
408                     if (pos->rgSquare[c].uIndex > pos->uNonPawnCount[u][0])
409                     {
410                         uReason = 18;
411                         goto end;
412                     }
413                 }
414             }
415         }
416     }
417
418     if ((uPawnMaterial[WHITE] + uPawnMaterial[BLACK] +
419          uNonPawnMaterial[WHITE] + uNonPawnMaterial[BLACK]) != 0)
420     {
421         uReason = 19;
422         goto end;
423     }
424
425 //    if (pos->uFifty > 101)
426 //    {
427 //        uReason = 20;
428 //        goto end;
429 //    }
430
431     //
432     // Looks Ok
433     //
434     fRet = TRUE;
435
436  end:
437     if (FALSE == fRet)
438     {
439         if (FALSE == fContinue)
440         {
441             UtilPanic(INCONSISTENT_POSITION,
442                       pos, szExplainations[uReason], NULL, NULL,
443                       __FILE__, __LINE__);
444             ASSERT(FALSE);
445         }
446     }
447     return(fRet);
448 }
449
450
451
452 FLAG
453 PositionsAreEquivalent(POSITION *p1,
454                        POSITION *p2)
455 /**
456
457 Routine description:
458
459     Compare two positions and decide whether they are the same
460     position.  Note: this code is meant to be accessed from test code
461     or from non-speed critical codepaths.  Comparing their signatures
462     should be sufficient to determine whether they are the same in
463     production code.
464
465 Parameters:
466
467     POSITION *p1 : first position to compare
468     POSITION *p2 : second position to compare
469
470 Return value:
471
472     FLAG : TRUE if they are the same position, FALSE otherwise
473
474 **/
475 {
476     COOR c;
477     ULONG u;
478
479     if ((p1->u64NonPawnSig != p2->u64NonPawnSig) ||
480         (p1->u64PawnSig != p2->u64PawnSig) ||
481         (p1->uToMove != p2->uToMove) ||
482         (p1->uFifty != p2->uFifty) ||
483         (p1->bvCastleInfo != p2->bvCastleInfo) ||
484         (p1->cEpSquare != p2->cEpSquare))
485     {
486         return(FALSE);
487     }
488
489     FOREACH_COLOR(u)
490     {
491         if ((p1->fCastled[u] != p2->fCastled[u]) ||
492             (p1->uWhiteSqBishopCount[u] != p2->uWhiteSqBishopCount[u]))
493         {
494             return(FALSE);
495         }
496     }
497
498     FOREACH_SQUARE(c)
499     {
500         if (!IS_ON_BOARD(c)) continue;
501
502         if (p1->rgSquare[c].pPiece != p2->rgSquare[c].pPiece)
503         {
504             return(FALSE);
505         }
506     }
507
508     VerifyPositionConsistency(p1, FALSE);
509     VerifyPositionConsistency(p2, FALSE);
510     return(TRUE);
511 }
512
513
514 char *
515 CastleInfoString(BITV bv)
516 /**
517
518 Routine description:
519
520     Generate and returns a small string describing the possible
521     castling choices in the bitvector passed in for use in a friandly
522     display or in a FEN string representing a position.  This function
523     is _NOT_ thread safe.
524
525 Parameters:
526
527     BITV bv : the castling bitvector
528
529 Return value:
530
531     char * : the string, either '-' signifying none possible or some
532              combination of:
533
534                  K = white kingside possible
535                  Q = white queenside possibe
536                  k = black kingside possible
537                  q = black queenside possible
538
539 **/
540 {
541     static char buf[5];
542     char *p = buf;
543
544     memset(buf, 0, sizeof(buf));
545     ASSERT((bv & ~CASTLE_ALL_POSSIBLE) == 0);
546
547     if (bv == 0)
548     {
549         *p++ = '-';
550     }
551     else
552     {
553         if (bv & CASTLE_WHITE_SHORT)
554         {
555             *p++ = 'K';
556         }
557
558         if (bv & CASTLE_WHITE_LONG)
559         {
560             *p++ = 'Q';
561         }
562
563         if (bv & CASTLE_BLACK_SHORT)
564         {
565             *p++ = 'k';
566         }
567
568         if (bv & CASTLE_BLACK_LONG)
569         {
570             *p++ = 'q';
571         }
572     }
573     return(buf);
574 }
575
576
577 void
578 DumpPosition(POSITION *pos)
579 /**
580
581 Routine description:
582
583     Dump an ASCII representation of POSITION pos on stdout.
584
585 Parameters:
586
587     POSITION *pos
588
589 Return value:
590
591     void
592
593 **/
594 {
595     char *p;
596 #ifdef DEBUG
597     ULONG u;
598     if (!VerifyPositionConsistency(pos, TRUE))
599     {
600         Bug("===========================================================================\n"
601             " WARNING: The following position seems to be inconsistent.  I'll do my best\n"
602             " to render it but this may not be such a good idea...\n"
603             "===========================================================================\n\n");
604     }
605 #endif
606     p = PositionToFen(pos);
607     if (g_Options.fRunningUnderXboard)
608     {
609         Trace("; PositionToFen(pos): %s\n", p);
610         goto end;
611     }
612     else
613     {
614         Trace("FEN: %s\n\n", p);
615     }
616
617 #ifdef DEBUG
618     Trace("--------------------------------------------------------------------------------\n"
619           "PositionToFen(pos): %s\n\n"
620
621           "POSITION (@%p):\n"
622           "  +0x%03x u64NonPawnSig             : %"
623               COMPILER_LONGLONG_HEX_FORMAT "\n"
624           "  +0x%03x u64PawnSig                : %"
625               COMPILER_LONGLONG_HEX_FORMAT "\n"
626           "         u64NonPawnSig ^ u64PawnSig: %"
627               COMPILER_LONGLONG_HEX_FORMAT "\n"
628           "  +0x%03x pSquare[128]              : \n\n"
629           "%s"
630           "  +0x%03x uToMove                   : 0x%02x (%s)\n"
631           "  +0x%03x uFifty                    : 0x%02x (%u moves)\n"
632           "  +0x%03x fCastled[2]               : W:0x%02x         B:0x%02x\n"
633           "  +0x%03x bvCastleInfo              : 0x%02x (%s)\n"
634           "  +0x%03x cEpSquare                 : 0x%02x (%s)\n"
635           "  +0x%03x cPawns[2][8]              : \n",
636           p,
637           pos,
638           OFFSET_OF(u64PawnSig, POSITION), pos->u64PawnSig,
639           OFFSET_OF(u64NonPawnSig, POSITION), pos->u64NonPawnSig,
640           pos->u64NonPawnSig ^ pos->u64PawnSig,
641           OFFSET_OF(rgSquare, POSITION), DrawTextBoardFromPosition(pos),
642           OFFSET_OF(uToMove, POSITION), pos->uToMove, ColorToString(pos->uToMove),
643           OFFSET_OF(uFifty, POSITION), pos->uFifty, pos->uFifty,
644           OFFSET_OF(fCastled, POSITION), pos->fCastled[WHITE], pos->fCastled[BLACK],
645           OFFSET_OF(bvCastleInfo, POSITION), pos->bvCastleInfo, CastleInfoString(pos->bvCastleInfo),
646           OFFSET_OF(cEpSquare, POSITION), pos->cEpSquare, (IS_ON_BOARD(pos->cEpSquare) ? CoorToString(pos->cEpSquare) : "none"),
647           OFFSET_OF(cPawns, POSITION));
648
649     Trace("    W: ");
650     for (u = 0; u < pos->uPawnCount[WHITE]; u++)
651     {
652         Trace("0x%02x[%s] ",
653               pos->cPawns[WHITE][u],
654               CoorToString(pos->cPawns[WHITE][u]));
655         ASSERT(IS_ON_BOARD(pos->cPawns[WHITE][u]));
656     }
657
658     Trace("\n"
659           "    B: ");
660     for (u = 0; u < pos->uPawnCount[BLACK]; u++)
661     {
662         Trace("0x%02x[%s] ",
663               pos->cPawns[BLACK][u],
664               CoorToString(pos->cPawns[BLACK][u]));
665         ASSERT(IS_ON_BOARD(pos->cPawns[BLACK][u]));
666     }
667
668     Trace("\n"
669           "  +0x%03x uPawnMaterial[2]          : W:0x%02x (%u, pawn=%u)\n"
670           "                                   : B:0x%02x (%u, pawn=%u)\n"
671           "  +0x%03x uPawnCount[2]             : W:%1u pawns      B:%1u pawns\n",
672           OFFSET_OF(uPawnMaterial, POSITION),
673           pos->uPawnMaterial[WHITE], pos->uPawnMaterial[WHITE], VALUE_PAWN,
674           pos->uPawnMaterial[BLACK], pos->uPawnMaterial[BLACK], VALUE_PAWN,
675           OFFSET_OF(uPawnCount, POSITION),
676           pos->uPawnCount[WHITE], pos->uPawnCount[BLACK]);
677
678     Trace("  +0x%03x cNonPawns[2][16]          : \n"
679           "    W: ", OFFSET_OF(cNonPawns, POSITION));
680     for (u = 0; u < pos->uNonPawnCount[WHITE][0]; u++)
681     {
682         Trace("0x%02x[%s] ",
683               pos->cNonPawns[WHITE][u],
684               CoorToString(pos->cNonPawns[WHITE][u]));
685         ASSERT(IS_ON_BOARD(pos->cNonPawns[WHITE][u]));
686     }
687     Trace("\n"
688           "    B: ");
689     for (u = 0; u < pos->uNonPawnCount[BLACK][0]; u++)
690     {
691         Trace("0x%02x[%s] ",
692               pos->cNonPawns[BLACK][u],
693               CoorToString(pos->cNonPawns[BLACK][u]));
694         ASSERT(IS_ON_BOARD(pos->cNonPawns[BLACK][u]));
695     }
696     Trace("\n"
697           "  +0x%03x uNonPawnMaterial[2]       : W: 0x%02x (%u, pawn=%u)\n"
698           "                                   : B: 0x%02x (%u, pawn=%u)\n"
699           "  +0x%03x uNonPawnCount[2][7]       : WHITE           BLACK\n"
700           "                            KNIGHT : %u              %u\n"
701           "                            BISHOP : %u              %u\n"
702           "               WHITE SQUARE BISHOP : %u              %u\n"
703           "                              ROOK : %u              %u\n"
704           "                             QUEEN : %u              %u\n"
705           "                               SUM : %u              %u\n",
706           OFFSET_OF(uNonPawnMaterial, POSITION),
707           pos->uNonPawnMaterial[WHITE], pos->uNonPawnMaterial[WHITE],
708           VALUE_PAWN,
709           pos->uNonPawnMaterial[BLACK], pos->uNonPawnMaterial[BLACK],
710           VALUE_PAWN,
711           OFFSET_OF(uNonPawnCount, POSITION),
712           pos->uNonPawnCount[WHITE][KNIGHT], pos->uNonPawnCount[BLACK][KNIGHT],
713           pos->uNonPawnCount[WHITE][BISHOP], pos->uNonPawnCount[BLACK][BISHOP],
714           pos->uWhiteSqBishopCount[WHITE], pos->uWhiteSqBishopCount[BLACK],
715           pos->uNonPawnCount[WHITE][ROOK], pos->uNonPawnCount[BLACK][ROOK],
716           pos->uNonPawnCount[WHITE][QUEEN], pos->uNonPawnCount[BLACK][QUEEN],
717           pos->uNonPawnCount[WHITE][0], pos->uNonPawnCount[BLACK][0]);
718 #else
719     Trace("\n%s\n", DrawTextBoardFromPosition(pos));
720 #endif
721
722  end:
723     if (NULL != p)
724     {
725         SystemFreeMemory(p);
726     }
727 }
728
729 //
730 // TODO: DumpBoard
731 //