1 //////////////////////////////////////////////////////////////////////
2 //
3 //  FILE:       position.cpp
4 //              Position class methods
5 //
6 //  Part of:    Scid (Shane's Chess Information Database)
7 //  Version:    3.5
8 //
9 //  Notice:     Copyright (c) 1999-2003 Shane Hudson.  All rights reserved.
10 //
11 //  Author:     Shane Hudson (sgh@users.sourceforge.net)
12 //
13 //////////////////////////////////////////////////////////////////////
14 
15 #include "common.h"
16 #include "position.h"
17 #include "attacks.h"
18 #include "misc.h"
19 #include "hash.h"
20 #include "sqmove.h"
21 #include "dstring.h"
22 #include "movegen.h"
23 #include <algorithm>
24 
25 static uint hashVal [16][64];
26 static uint stdStartHash = 0;
27 static uint stdStartPawnHash = 0;
28 
29 // HASH and UNHASH are identical: XOR the hash value for a (piece,square).
30 #define HASH(h,p,sq)    (h) ^= hashVal[(p)][(sq)]
31 #define UNHASH(h,p,sq)  (h) ^= hashVal[(p)][(sq)]
32 
33 inline void
AddHash(pieceT p,squareT sq)34 Position::AddHash (pieceT p, squareT sq)
35 {
36     HASH (Hash, p, sq);
37     if (piece_Type(p) == PAWN) {
38         HASH (PawnHash, p, sq);
39     }
40 }
41 
42 inline void
UnHash(pieceT p,squareT sq)43 Position::UnHash (pieceT p, squareT sq)
44 {
45     UNHASH (Hash, p, sq);
46     if (piece_Type(p) == PAWN) {
47         UNHASH (PawnHash, p, sq);
48     }
49 }
50 
51 inline void
AddToBoard(pieceT p,squareT sq)52 Position::AddToBoard (pieceT p, squareT sq)
53 {
54     ASSERT (Board[sq] == EMPTY);
55     Board[sq] = p;
56     NumOnRank[p][square_Rank(sq)]++;
57     NumOnFyle[p][square_Fyle(sq)]++;
58     NumOnLeftDiag[p][square_LeftDiag(sq)]++;
59     NumOnRightDiag[p][square_RightDiag(sq)]++;
60     NumOnSquareColor[p][square_Color(sq)]++;
61     AddHash (p, sq);
62 }
63 
64 inline void
RemoveFromBoard(pieceT p,squareT sq)65 Position::RemoveFromBoard (pieceT p, squareT sq)
66 {
67     ASSERT (Board[sq] == p);
68     Board[sq] = EMPTY;
69     NumOnRank[p][square_Rank(sq)]--;
70     NumOnFyle[p][square_Fyle(sq)]--;
71     NumOnLeftDiag[p][square_LeftDiag(sq)]--;
72     NumOnRightDiag[p][square_RightDiag(sq)]--;
73     NumOnSquareColor[p][square_Color(sq)]--;
74     UnHash (p, sq);
75 }
76 
77 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
78 // initHashValues:
79 //    Initialises the table of Zobrist hash values.
initHashValues(void)80 static void initHashValues (void)
81 {
82     // Ensure we set up the hash values only once:
83     static int firstCall = 1;
84     if (! firstCall) { return; }
85     firstCall = 0;
86 
87 
88     // First, set all values to 0:
89     uint sq;
90     for (uint p = 0; p < 16; p++) {
91         for (sq = A1; sq <= H8; sq++) { hashVal[p][sq] = 0; }
92     }
93 
94     // Fill in the hash values for each valid [piece][square] index,
95     // using a table of pre-generated good values:
96     const unsigned int * hash = goodHashValues;
97     for (sq=A1; sq <= H8; sq++) { hashVal[WK][sq] = *hash; hash++; }
98     for (sq=A1; sq <= H8; sq++) { hashVal[WQ][sq] = *hash; hash++; }
99     for (sq=A1; sq <= H8; sq++) { hashVal[WR][sq] = *hash; hash++; }
100     for (sq=A1; sq <= H8; sq++) { hashVal[WB][sq] = *hash; hash++; }
101     for (sq=A1; sq <= H8; sq++) { hashVal[WN][sq] = *hash; hash++; }
102     for (sq=A1; sq <= H8; sq++) { hashVal[WP][sq] = *hash; hash++; }
103     for (sq=A1; sq <= H8; sq++) { hashVal[BK][sq] = *hash; hash++; }
104     for (sq=A1; sq <= H8; sq++) { hashVal[BQ][sq] = *hash; hash++; }
105     for (sq=A1; sq <= H8; sq++) { hashVal[BR][sq] = *hash; hash++; }
106     for (sq=A1; sq <= H8; sq++) { hashVal[BB][sq] = *hash; hash++; }
107     for (sq=A1; sq <= H8; sq++) { hashVal[BN][sq] = *hash; hash++; }
108     for (sq=A1; sq <= H8; sq++) { hashVal[BP][sq] = *hash; hash++; }
109 
110     // Compute the hash values for the standard starting position:
111     uint h = 0;
112     // First the pawns:
113     HASH (h,WP,A2);  HASH (h,WP,B2);  HASH (h,WP,C2);  HASH (h,WP,D2);
114     HASH (h,WP,E2);  HASH (h,WP,F2);  HASH (h,WP,G2);  HASH (h,WP,H2);
115     HASH (h,BP,A7);  HASH (h,BP,B7);  HASH (h,BP,C7);  HASH (h,BP,D7);
116     HASH (h,BP,E7);  HASH (h,BP,F7);  HASH (h,BP,G7);  HASH (h,BP,H7);
117     stdStartPawnHash = h;
118     // Now the nonpawns:
119     HASH (h,WR,A1);  HASH (h,WN,B1);  HASH (h,WB,C1);  HASH (h,WQ,D1);
120     HASH (h,WK,E1);  HASH (h,WB,F1);  HASH (h,WN,G1);  HASH (h,WR,H1);
121     HASH (h,BR,A8);  HASH (h,BN,B8);  HASH (h,BB,C8);  HASH (h,BQ,D8);
122     HASH (h,BK,E8);  HASH (h,BB,F8);  HASH (h,BN,G8);  HASH (h,BR,H8);
123     stdStartHash = h;
124 }
125 
126 
127 ///////////////////////////////////////////////////////////////////////////
128 // sqDir[][]: Array listing the direction between any two squares.
129 //    For example, sqDir[A1][B2] == UP_RIGHT, and sqDir[A1][C2] == NULL_DIR.
130 directionT sqDir[66][66];
131 struct sqDir_Init
132 {
sqDir_InitsqDir_Init133     sqDir_Init() {
134         // Initialise the sqDir[][] array of directions between every pair
135         // of squares.
136         squareT i, j;
137         directionT dirArray[] = { UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT,
138                                   DOWN_LEFT, DOWN_RIGHT, NULL_DIR };
139         // First, set everything to NULL_DIR:
140         for (i=A1; i <= NS; i++) {
141             for (j=A1; j <= NS; j++) {
142                 sqDir[i][j] = NULL_DIR;
143             }
144         }
145         // Now fill in the valid directions:
146         for (i=A1; i <= H8; i++) {
147             directionT * dirptr = dirArray;
148             while (*dirptr != NULL_DIR) {
149                 j = square_Move (i, *dirptr);
150                 while (j != NS) {
151                     sqDir[i][j] = *dirptr;
152                     j = square_Move (j, *dirptr);
153                 }
154                 dirptr++;
155             }
156         }
157     }
158 } sqDir_Init_singleton;
159 
160 ///////////////////////////////////////////////////////////////////////////
161 //  PRIVATE FUNCTIONS -- small ones are inline for speed
162 
163 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
164 // Position::CalcPinsDir():
165 //      Look for a pinned piece in the direction 'dir' relative to
166 //      the position of the king to move.
167 //
168 inline void
CalcPinsDir(directionT dir,pieceT attacker)169 Position::CalcPinsDir (directionT dir, pieceT attacker)
170 {
171     // Two pieces can pin along any path. A queen is always one,
172     // the other is a bishop or rook. To save calculating it here, the
173     // appropriate piece (BISHOP) or (ROOK) is passed along with the
174     // direction.
175 
176     squareT king = GetKingSquare (ToMove);
177     squareT friendly = NULL_SQUARE;
178     squareT x = king;
179     squareT last = square_Last (king, dir);
180     int delta = direction_Delta (dir);
181 
182     while (x != last) {
183         x += delta;
184         pieceT p = Board[x];
185         if (p == EMPTY) {
186             // Empty square, so keep searching.
187         } else if (piece_Color_NotEmpty(p) == ToMove) {
188             // Found a friendly piece.
189             if (friendly == NULL_SQUARE) {
190                 // Found first friendly in this direction
191                 friendly = x;
192             } else {
193                 // Found second friendly in this direction, so stop.
194                 return;
195             }
196         } else {
197             // Found an enemy piece
198             if (friendly != NULL_SQUARE) {
199                 // Potential pin:
200                 pieceT ptype = piece_Type(p);
201                 if (ptype == QUEEN  ||  ptype == attacker) {
202                     Pinned[ListPos[friendly]] = dir;
203                 }
204             }
205             return;     // found an enemy piece, so end search
206         }
207     }
208     return;
209 }
210 
211 
212 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
213 // Position::AddLegalMove():
214 //      Add a legal move to the move list.
215 //
216 inline void
AddLegalMove(MoveList * mlist,squareT from,squareT to,pieceT promo)217 Position::AddLegalMove (MoveList * mlist, squareT from, squareT to, pieceT promo)
218 {
219     ASSERT (mlist != NULL);
220     // We do NOT set the pre-move castling/ep flags, or the captured
221     // piece info, here since that is ONLY needed if the move is
222     // going to be executed with DoSimpleMove() and then undone.
223     mlist->emplace_back(from, to, promo, Board[from], Board[to]);
224 }
225 
226 
227 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
228 // Position::GenSliderMoves():
229 //      Generate slider moves along a direction, for a sliding
230 //      piece of the specified color from the specified square.
231 //      If sqset != NULL, moves must be to a square in sqset.
232 inline void
GenSliderMoves(MoveList * mlist,colorT color,squareT fromSq,directionT dir,SquareSet * sqset,bool capturesOnly)233 Position::GenSliderMoves (MoveList * mlist, colorT color, squareT fromSq,
234                           directionT dir, SquareSet * sqset, bool capturesOnly)
235 {
236     squareT dest = fromSq;
237     squareT last = square_Last (fromSq, dir);
238     int delta = direction_Delta (dir);
239 
240     while (dest != last) {
241         dest += delta;
242         pieceT p = Board[dest];
243         if (p == EMPTY) {
244             if (! capturesOnly) {
245                 if (sqset == NULL  ||  sqset->Contains(dest)) {
246                     AddLegalMove (mlist, fromSq, dest, EMPTY);
247                 }
248             }
249             continue;
250         }
251         // We have reached a piece. Add the capture if it is an enemy.
252         if (piece_Color_NotEmpty(p) != color) {
253             if (sqset == NULL  ||  sqset->Contains(dest)) {
254                 AddLegalMove (mlist, fromSq, dest, EMPTY);
255             }
256         }
257         break;
258     }
259 }
260 
261 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
262 // Position::GenKnightMoves():
263 //      Generate knight moves.
264 //      If sqset != NULL, moves must be to a square in sqset.
265 inline void
GenKnightMoves(MoveList * mlist,colorT c,squareT fromSq,SquareSet * sqset,bool capturesOnly)266 Position::GenKnightMoves (MoveList * mlist, colorT c, squareT fromSq,
267                           SquareSet * sqset, bool capturesOnly)
268 {
269     const squareT * destPtr = knightAttacks[fromSq];
270     while (true) {
271         squareT dest = *destPtr;
272         if (dest == NULL_SQUARE) { break; }
273         destPtr++;
274         pieceT p = Board[dest];
275         if (capturesOnly  &&  p == EMPTY) { continue; }
276         if (piece_Color(p) != c) {
277             if (sqset == NULL  ||  sqset->Contains(dest)) {
278                 AddLegalMove (mlist, fromSq, dest, EMPTY);
279             }
280         }
281     }
282 }
283 
284 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
285 // Position::GenCastling():
286 //    Generate the legal castling moves.
287 //    Assumes the side to move is NOT in check, so the caller
288 //    should verify this first.
289 //
290 void
GenCastling(MoveList * mlist)291 Position::GenCastling (MoveList * mlist)
292 {
293     ASSERT (! IsKingInCheck());
294     squareT from = GetKingSquare(ToMove);
295     if (from != (ToMove == WHITE ? E1 : E8))  { return; }
296     squareT enemyKingSq = GetEnemyKingSquare();
297     squareT target, skip, rookSq;
298     pieceT rookPiece;
299 
300     // Try kingside first
301 
302     // Kingside Castling:
303     if (GetCastling (ToMove, KSIDE)) {
304         if (ToMove == WHITE) {
305             target = G1; skip = F1; rookSq = H1; rookPiece = WR;
306         } else {
307             target = G8; skip = F8; rookSq = H8; rookPiece = BR;
308         }
309         if (Board[target] == EMPTY  &&  Board[skip] == EMPTY
310                 &&  Board[rookSq] == rookPiece
311                 &&  CalcNumChecks (target) == 0
312                 &&  CalcNumChecks (skip) == 0
313                 &&  ! square_Adjacent (target, enemyKingSq)) {
314             AddLegalMove (mlist, from, target, EMPTY);
315         }
316     }
317 
318     // Queenside Castling:
319     if (GetCastling (ToMove, QSIDE)) {
320         if (ToMove == WHITE) {
321             target = C1; skip = D1; rookSq = A1; rookPiece = WR;
322         } else {
323             target = C8; skip = D8; rookSq = A8; rookPiece = BR;
324         }
325         if (Board[target] == EMPTY  &&  Board[skip] == EMPTY
326                 &&  Board[rookSq] == rookPiece
327                 &&  Board[target - 1] == EMPTY // B1 or B8 must be empty too!
328                 &&  CalcNumChecks (target) == 0
329                 &&  CalcNumChecks (skip) == 0
330                 &&  ! square_Adjacent (target, enemyKingSq)) {
331             AddLegalMove (mlist, from, target, EMPTY);
332         }
333     }
334 }
335 
336 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
337 // Position::GenKingMoves():
338 //      Generate the legal King moves. Castling is generated as well, if
339 //      the specified flag is true.
340 //
341 void
GenKingMoves(MoveList * mlist,genMovesT genType,bool castling)342 Position::GenKingMoves (MoveList * mlist, genMovesT genType, bool castling)
343 {
344     squareT kingSq = GetKingSquare();
345     squareT enemyKingSq = GetEnemyKingSquare();
346     colorT enemy = color_Flip(ToMove);
347     const squareT * destPtr;
348     pieceT king = piece_Make (ToMove, KING);
349     bool genNonCaptures = (genType & GEN_NON_CAPS);
350 
351     ASSERT (Board[kingSq] == king);
352 
353     destPtr = kingAttacks[kingSq];
354     while (*destPtr != NULL_SQUARE) {
355         // Try this move and see if it legal:
356 
357         squareT destSq = *destPtr;
358         bool addThisMove = false;
359 
360         // Only try this move if the target square has an enemy piece,
361         // or if it is empty and noncaptures are to be generated:
362         if ( (genNonCaptures  &&  Board[destSq] == EMPTY)
363              ||  piece_Color (Board[destSq]) == enemy) {
364 
365             // Empty or enemy piece there, so try the move:
366             pieceT captured = Board[destSq];
367             Board[destSq] = king;
368             Board[kingSq] = EMPTY;
369 
370             // It is legal if the two kings will not be adjacent and the
371             // moving king will not be in check on its new square:
372             if (! square_Adjacent (destSq, enemyKingSq)) {
373                 if (CalcNumChecks (destSq) == 0) {
374                     addThisMove = true;
375                 }
376             }
377             Board[kingSq] = king;
378             Board[destSq] = captured;
379         }
380         if (addThisMove) { AddLegalMove (mlist, kingSq, destSq, EMPTY); }
381         destPtr++;
382     }
383     // Now generate castling moves, if possible:
384     if (genNonCaptures  &&  castling) { GenCastling (mlist); }
385 }
386 
387 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
388 // Position::AddPromotions():
389 //      Add promotion moves.
390 //      Called by GenPawnMoves() when a pawn can be promoted.
391 //
392 inline void
AddPromotions(MoveList * mlist,squareT from,squareT dest)393 Position::AddPromotions (MoveList * mlist, squareT from, squareT dest)
394 {
395     ASSERT (piece_Type (Board[from]) == PAWN);
396     ASSERT (square_Rank (dest) == RANK_1  ||  square_Rank (dest) == RANK_8);
397 
398     AddLegalMove (mlist, from, dest, QUEEN);
399     AddLegalMove (mlist, from, dest, ROOK);
400     AddLegalMove (mlist, from, dest, BISHOP);
401     AddLegalMove (mlist, from, dest, KNIGHT);
402 }
403 
404 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
405 // Position::IsValidEnPassant
406 //   Used to verify that an en passant  pawn capture is valid.
407 //   This is needed because illegal en passant captures can appear
408 //   legal according to calculations of pinned pieces. For example,
409 //   consider WK d5, WP e5, BP f5 (just moved there), BR h5 and
410 //   the en passant capture exf6 would be illegal.
411 inline bool
IsValidEnPassant(squareT from,squareT to)412 Position::IsValidEnPassant (squareT from, squareT to)
413 {
414     ASSERT (from <= H8  &&  to <= H8);
415     ASSERT (to == EPTarget);
416 
417     // Check that this en passant capture is legal:
418     pieceT ownPawn = piece_Make(ToMove, PAWN);
419     pieceT enemyPawn = piece_Make (color_Flip(ToMove), PAWN);
420     squareT enemyPawnSq = (ToMove == WHITE) ? to - 8 : to + 8;
421     ASSERT (Board[from] == ownPawn);
422     ASSERT (Board[enemyPawnSq] == enemyPawn);
423     Board[from] = EMPTY;
424 
425     // PG
426     Board[to] = ownPawn;
427 
428     Board[enemyPawnSq] = EMPTY;
429     bool isValid = ! IsKingInCheck();
430 
431     //PG
432     Board[to] = EMPTY;
433 
434     Board[from] = ownPawn;
435     Board[enemyPawnSq] = enemyPawn;
436     return isValid;
437 }
438 
439 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
440 // Position::GenPawnMoves():
441 //      Generate legal pawn moves.
442 //      If dir != NULL_DIR, pawn MUST move in direction dir or its opposite.
443 //      If sqset != NULL, pawn MUST move to a square in sqset.
444 //      The dir and sq parameters are for pinned pawns and check evasions.
445 void
GenPawnMoves(MoveList * mlist,squareT from,directionT dir,SquareSet * sqset,genMovesT genType)446 Position::GenPawnMoves (MoveList * mlist, squareT from,
447                         directionT dir, SquareSet * sqset, genMovesT genType)
448 {
449     bool genNonCaptures = (genType & GEN_NON_CAPS);
450     directionT oppdir = direction_Opposite(dir);
451     directionT forward;
452     rankT promoRank;
453     rankT secondRank;
454     if (ToMove == WHITE) {
455         forward = UP;    promoRank = RANK_8;  secondRank = RANK_2;
456     } else  {
457         forward = DOWN;  promoRank = RANK_1;  secondRank = RANK_7;
458     }
459     squareT dest;
460 
461     ASSERT (Board[from] == piece_Make (ToMove, PAWN));
462 
463     if (genNonCaptures
464           &&  (dir == NULL_DIR  ||  dir == forward  ||  oppdir == forward)) {
465         dest = square_Move (from, forward);
466         if (Board[dest]==EMPTY && (sqset==NULL || sqset->Contains(dest))) {
467             if (square_Rank(dest) == promoRank) {
468                 AddPromotions (mlist, from, dest);
469             } else {
470                 AddLegalMove (mlist, from, dest, EMPTY);
471             }
472         }
473         if (square_Rank(from) == secondRank  &&  Board[dest] == EMPTY) {
474             dest = square_Move (dest, forward);
475             if (Board[dest]==EMPTY  &&  (sqset==NULL || sqset->Contains(dest))) {
476                 AddLegalMove (mlist, from, dest, EMPTY);
477             }
478         }
479     }
480 
481     // Now do captures: left, then right
482     // To be a possible capture, dest square must be EPTarget or hold
483     // an enemy piece.
484 #define POSSIBLE_CAPTURE(d) ((d != NULL_SQUARE)   \
485             &&  ((piece_Color (Board[d]) == (color_Flip(ToMove)))  \
486                     ||  (d == EPTarget  &&  IsValidEnPassant(from,d))))
487 
488     directionT capdir = forward | LEFT;
489     if (dir == NULL_DIR  ||  dir == capdir  ||  oppdir == capdir) {
490         dest = square_Move (from, capdir);
491         if (POSSIBLE_CAPTURE(dest)  &&  (sqset==NULL || sqset->Contains(dest))) {
492             if (square_Rank(dest) == promoRank) {
493                 AddPromotions (mlist, from, dest);
494             } else {
495                 AddLegalMove (mlist, from, dest, EMPTY);
496             }
497         }
498     }
499     capdir = forward | RIGHT;
500     if (dir == NULL_DIR  ||  dir == capdir  ||  oppdir == capdir) {
501         dest = square_Move (from, capdir);
502         if (POSSIBLE_CAPTURE(dest)  &&  (sqset==NULL || sqset->Contains(dest))) {
503             if (square_Rank(dest) == promoRank) {
504                 AddPromotions (mlist, from, dest);
505             } else {
506                 AddLegalMove (mlist, from, dest, EMPTY);
507             }
508         }
509     }
510     return;
511 }
512 
513 
514 //////////////////////////////////////////////////////////////////////
515 //  PUBLIC FUNCTIONS
516 
517 
518 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
519 // Position::GetHPSig():
520 //      Return the position's home pawn signature.
521 //
522 uint
GetHPSig(void)523 Position::GetHPSig (void)
524 {
525     uint hpSig = 0;
526     pieceT * b = &(Board[A2]);
527     if (*b == WP) { hpSig |= 0x8000; }  b++;  /* a2 */
528     if (*b == WP) { hpSig |= 0x4000; }  b++;  /* b2 */
529     if (*b == WP) { hpSig |= 0x2000; }  b++;  /* c2 */
530     if (*b == WP) { hpSig |= 0x1000; }  b++;  /* d2 */
531     if (*b == WP) { hpSig |= 0x0800; }  b++;  /* e2 */
532     if (*b == WP) { hpSig |= 0x0400; }  b++;  /* f2 */
533     if (*b == WP) { hpSig |= 0x0200; }  b++;  /* g2 */
534     if (*b == WP) { hpSig |= 0x0100; }        /* h2 */
535     b = &(Board[A7]);
536     if (*b == BP) { hpSig |= 0x0080; }  b++;  /* a7 */
537     if (*b == BP) { hpSig |= 0x0040; }  b++;  /* b7 */
538     if (*b == BP) { hpSig |= 0x0020; }  b++;  /* c7 */
539     if (*b == BP) { hpSig |= 0x0010; }  b++;  /* d7 */
540     if (*b == BP) { hpSig |= 0x0008; }  b++;  /* e7 */
541     if (*b == BP) { hpSig |= 0x0004; }  b++;  /* f7 */
542     if (*b == BP) { hpSig |= 0x0002; }  b++;  /* g7 */
543     if (*b == BP) { hpSig |= 0x0001; }        /* h7 */
544     return hpSig;
545 }
546 
Position()547 Position::Position() {
548     // Setting up a valid board is left to StdStart() or Clear().
549     Board [COLOR_SQUARE] = EMPTY;
550     Board [NULL_SQUARE] = END_OF_BOARD;
551 
552     // Make sure all tables used for move generation, hashing,
553     // square tests, etc have been computed:
554     initHashValues();
555 }
556 
557 
558 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
559 // Position::Clear():
560 //      Clear the board and associated structures.
561 //
562 void
Clear(void)563 Position::Clear (void)
564 {
565     int i;
566     for (i=A1; i <= H8; i++) { Board[i] = EMPTY; }
567     for (i=WK; i <= BP; i++) {
568         Material[i] = 0;
569         for (uint j=0; j < 8; j++) {
570             NumOnRank[i][j] = NumOnFyle[i][j] = 0;
571         }
572         for (uint d=0; d < 15; d++) {
573             NumOnLeftDiag[i][d] = NumOnRightDiag[i][d] = 0;
574         }
575         NumOnSquareColor[i][WHITE] = NumOnSquareColor[i][BLACK] = 0;
576     }
577     Count[WHITE] = Count[BLACK] = 0;
578     EPTarget = NULL_SQUARE;
579     Castling = 0;
580     Board [NULL_SQUARE] = END_OF_BOARD;
581     PlyCounter = 0;
582     HalfMoveClock = 0;
583     Hash = 0;
584     PawnHash = 0;
585     return;
586 }
587 
588 
589 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
590 // Position::StdStart():
591 //      Set up the standard chess starting position. For performance the data is copied from a
592 //      template.
593 //
getStdStart()594 const Position& Position::getStdStart()
595 {
596     static Position startPositionTemplate;
597     static bool init = true;
598 
599     if (init){
600         init = false;
601         Position* p = &startPositionTemplate;
602         p->Clear();
603         p->Material[WK] = p->Material[BK] = 1;
604         p->Material[WQ] = p->Material[BQ] = 1;
605         p->Material[WR] = p->Material[BR] = 2;
606         p->Material[WB] = p->Material[BB] = 2;
607         p->Material[WN] = p->Material[BN] = 2;
608         p->Material[WP] = p->Material[BP] = 8;
609         p->Count[WHITE] = p->Count[BLACK] = 16;
610 
611         p->AddToBoard(WK, E1);  p->List[WHITE][0] = E1;  p->ListPos[E1] = 0;
612         p->AddToBoard(BK, E8);  p->List[BLACK][0] = E8;  p->ListPos[E8] = 0;
613         p->AddToBoard(WR, A1);  p->List[WHITE][1] = A1;  p->ListPos[A1] = 1;
614         p->AddToBoard(BR, A8);  p->List[BLACK][1] = A8;  p->ListPos[A8] = 1;
615         p->AddToBoard(WN, B1);  p->List[WHITE][2] = B1;  p->ListPos[B1] = 2;
616         p->AddToBoard(BN, B8);  p->List[BLACK][2] = B8;  p->ListPos[B8] = 2;
617         p->AddToBoard(WB, C1);  p->List[WHITE][3] = C1;  p->ListPos[C1] = 3;
618         p->AddToBoard(BB, C8);  p->List[BLACK][3] = C8;  p->ListPos[C8] = 3;
619         p->AddToBoard(WQ, D1);  p->List[WHITE][4] = D1;  p->ListPos[D1] = 4;
620         p->AddToBoard(BQ, D8);  p->List[BLACK][4] = D8;  p->ListPos[D8] = 4;
621         p->AddToBoard(WB, F1);  p->List[WHITE][5] = F1;  p->ListPos[F1] = 5;
622         p->AddToBoard(BB, F8);  p->List[BLACK][5] = F8;  p->ListPos[F8] = 5;
623         p->AddToBoard(WN, G1);  p->List[WHITE][6] = G1;  p->ListPos[G1] = 6;
624         p->AddToBoard(BN, G8);  p->List[BLACK][6] = G8;  p->ListPos[G8] = 6;
625         p->AddToBoard(WR, H1);  p->List[WHITE][7] = H1;  p->ListPos[H1] = 7;
626         p->AddToBoard(BR, H8);  p->List[BLACK][7] = H8;  p->ListPos[H8] = 7;
627 
628         for (uint i=0; i < 8; i++) {
629             p->AddToBoard(WP, A2+i); p->List[WHITE][i+8] = A2+i; p->ListPos[A2+i] = i+8;
630             p->AddToBoard(BP, A7+i); p->List[BLACK][i+8] = A7+i; p->ListPos[A7+i] = i+8;
631         }
632 
633         p->Castling = 0;
634         p->SetCastling (WHITE, QSIDE, true);  p->SetCastling (WHITE, KSIDE, true);
635         p->SetCastling (BLACK, QSIDE, true);  p->SetCastling (BLACK, KSIDE, true);
636         p->EPTarget = NULL_SQUARE;
637         p->ToMove = WHITE;
638         p->PlyCounter = 0;
639         p->HalfMoveClock = 0;
640         p->Board [NULL_SQUARE] = END_OF_BOARD;
641         p->Hash = stdStartHash;
642         p->PawnHash = stdStartPawnHash;
643     }
644     return startPositionTemplate;
645 }
646 
647 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
648 // Position::IsStdStart
649 //   Returns true if the position is the standard starting position.
IsStdStart() const650 bool Position::IsStdStart() const {
651     if (ToMove != WHITE
652           ||  Hash != stdStartHash  ||  PawnHash != stdStartPawnHash
653           ||  GetCount(WHITE) != 16  ||  GetCount(BLACK) != 16
654           ||  RankCount(WP,RANK_2) != 8  ||  RankCount(BP,RANK_7) != 8
655           ||  RankCount(WN,RANK_1) != 2  ||  RankCount(BN,RANK_8) != 2
656           ||  !GetCastling(WHITE,KSIDE)  ||  !GetCastling(WHITE,QSIDE)
657           ||  !GetCastling(BLACK,KSIDE)  ||  !GetCastling(BLACK,QSIDE)) {
658         return false;
659     }
660     return true;
661 }
662 
663 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
664 // Position::AddPiece():
665 //      Add a piece to the board and piecelist.
666 //      Checks that a side cannot have more than 16 pieces or more
667 //      than one king.
668 //
669 errorT
AddPiece(pieceT p,squareT sq)670 Position::AddPiece (pieceT p, squareT sq)
671 {
672     ASSERT (p != EMPTY);
673     colorT c = piece_Color(p);
674     if ((c != WHITE && c != BLACK) || Count[c] > 15)
675         return ERROR_PieceCount;
676 
677     if (piece_Type(p) == KING) {
678         // Check there is not already a King:
679         if (Material[p] > 0) { return ERROR_PieceCount; }
680 
681         // King is always at the start of the piecelist, so move the piece
682         // already at location 0 if there is one:
683         if (Count[c] > 0) {
684             squareT oldsq = List[c][0];
685             List[c][Count[c]] = oldsq;
686             ListPos[oldsq] = Count[c];
687         }
688         List[c][0] = sq;
689         ListPos[sq] = 0;
690     } else {
691         ListPos[sq] = Count[c];
692         List[c][Count[c]] = sq;
693     }
694     Count[c]++;
695     Material[p]++;
696     AddToBoard (p, sq);
697     return OK;
698 }
699 
700 
701 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
702 // Position::CalcPins():
703 //      Calculate the pieces for the side to move that are
704 //      pinned to their king. The array Pinned[] stores, for
705 //      each piece, the direction in which it is pinned.
706 //
707 //      For example WK on e1, WQ on e2, BQ on e7 on the e-fyle
708 //      means the WQ is Pinned in the direction UP.
709 //
710 void
CalcPins(void)711 Position::CalcPins (void)
712 {
713     Pinned[ 0] = Pinned[ 1] = Pinned[ 2] = Pinned[ 3] = Pinned[ 4] =
714     Pinned[ 5] = Pinned[ 6] = Pinned[ 7] = Pinned[ 8] = Pinned[ 9] =
715     Pinned[10] = Pinned[11] = Pinned[12] = Pinned[13] = Pinned[14] =
716     Pinned[15] = NULL_DIR;
717 
718     squareT kingSq = GetKingSquare (ToMove);
719     colorT enemy = color_Flip (ToMove);
720     pieceT enemyQueen  = piece_Make (enemy, QUEEN);
721     pieceT enemyRook   = piece_Make (enemy, ROOK);
722     pieceT enemyBishop = piece_Make (enemy, BISHOP);
723 
724     // Pins and checks from Bishops/Queens/Rooks:
725     fyleT fyle = square_Fyle (kingSq);
726     if (FyleCount(enemyQueen,fyle) + FyleCount(enemyRook,fyle) > 0) {
727         CalcPinsDir (UP, ROOK);
728         CalcPinsDir (DOWN, ROOK);
729     }
730     rankT rank = square_Rank (kingSq);
731     if (RankCount(enemyQueen,rank) + RankCount(enemyRook,rank) > 0) {
732         CalcPinsDir (LEFT, ROOK);
733         CalcPinsDir (RIGHT, ROOK);
734     }
735     leftDiagT ld = square_LeftDiag (kingSq);
736     if (LeftDiagCount(enemyQueen,ld) + LeftDiagCount(enemyBishop,ld) > 0) {
737         CalcPinsDir (UP_LEFT, BISHOP);
738         CalcPinsDir (DOWN_RIGHT, BISHOP);
739     }
740     rightDiagT rd = square_RightDiag (kingSq);
741     if (RightDiagCount(enemyQueen,rd) + RightDiagCount(enemyBishop,rd) > 0) {
742         CalcPinsDir (UP_RIGHT, BISHOP);
743         CalcPinsDir (DOWN_LEFT, BISHOP);
744     }
745 }
746 
747 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
748 // Position::GenPieceMoves():
749 //      Generates moves for a nonpawn, nonking piece.
750 //      If sqset != NULL, moves must be to a square in sqset.<
751 void
GenPieceMoves(MoveList * mlist,squareT fromSq,SquareSet * sqset,bool capturesOnly)752 Position::GenPieceMoves (MoveList * mlist, squareT fromSq,
753                          SquareSet * sqset, bool capturesOnly)
754 {
755     colorT c = ToMove;
756     pieceT p = Board[fromSq];
757     pieceT ptype = piece_Type(p);
758     ASSERT (p != EMPTY  &&  ptype != KING  &&  ptype != PAWN);
759 
760     if (ptype == KNIGHT) {
761         GenKnightMoves (mlist, c, fromSq, sqset, capturesOnly);
762         return;
763     }
764     if (ptype != BISHOP) {
765         GenSliderMoves (mlist, c, fromSq, UP, sqset, capturesOnly);
766         GenSliderMoves (mlist, c, fromSq, DOWN, sqset, capturesOnly);
767         GenSliderMoves (mlist, c, fromSq, LEFT, sqset, capturesOnly);
768         GenSliderMoves (mlist, c, fromSq, RIGHT, sqset, capturesOnly);
769     }
770     if (ptype != ROOK) {
771         GenSliderMoves (mlist, c, fromSq, UP_LEFT, sqset, capturesOnly);
772         GenSliderMoves (mlist, c, fromSq, DOWN_LEFT, sqset, capturesOnly);
773         GenSliderMoves (mlist, c, fromSq, UP_RIGHT, sqset, capturesOnly);
774         GenSliderMoves (mlist, c, fromSq, DOWN_RIGHT, sqset, capturesOnly);
775     }
776 }
777 
778 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
779 // Position::GenerateMoves
780 //    Generate the legal moves list.
781 //    If the specified pieceType is not EMPTY, then only legal
782 //    moves for that type of piece are generated.
783 void
GenerateMoves(MoveList * mlist,pieceT pieceType,genMovesT genType,bool maybeInCheck)784 Position::GenerateMoves (MoveList* mlist, pieceT pieceType,
785                          genMovesT genType, bool maybeInCheck)
786 {
787     ASSERT(mlist != NULL);
788 
789     bool genNonCaptures = (genType & GEN_NON_CAPS);
790     bool capturesOnly = !genNonCaptures;
791 
792     uint mask = 0;
793     if (pieceType != EMPTY) {
794         mask = 1 << pieceType;
795     } else {
796         mask = (1 << KING) | (1 << QUEEN) | (1 << ROOK)
797              | (1 << BISHOP) | (1 << KNIGHT) | (1 << PAWN);
798     }
799 
800     // Use the objects own move list if none was provided:
801     mlist->Clear();
802 
803     // Compute which pieces of the side to move are pinned to the king:
804     CalcPins();
805 
806     // Determine if the side to move is in check and find where the
807     // checking pieces are, unless the caller has passed maybeInCheck=false
808     // indicating it is CERTAIN the side to move is not in check here.
809 
810     uint numChecks = 0;
811     if (maybeInCheck) {
812         SquareList checkSquares;
813         numChecks = CalcNumChecks (GetKingSquare(ToMove), &checkSquares);
814         if (numChecks > 0) {
815             // The side to move IS in check:
816             GenCheckEvasions (mlist, pieceType, genType, &checkSquares);
817             return;
818         }
819     }
820 
821     // The side to move is NOT in check. Iterate over each non-king
822     // piece, and then generate King moves last of all:
823 
824     uint npieces = Count[ToMove];
825     for (uint x = 1; x < npieces; x++) {
826         squareT sq = List[ToMove][x];
827         pieceT p = Board[sq];
828         pieceT ptype = piece_Type(p);
829         if (! (mask & (1 << ptype))) { continue; }
830         directionT pinned = Pinned[x];
831 
832         // If Pinned[x] == dir (not NULL_DIR), x can ONLY move along
833         // that direction or its opposite.
834 
835         if (pinned != NULL_DIR) {  // piece x is pinned to king
836             if (ptype == PAWN) {
837                 GenPawnMoves (mlist, sq, pinned, NULL, genType);
838             } else if (ptype == KNIGHT) {
839                 // do nothing -- pinned knights cannot move
840             } else {
841                 // Piece is a pinned Queen/Rook/Bishop. Only generate
842                 // moves along the pinned direction and its opposite:
843                 if (ptype == QUEEN
844                       || (ptype == ROOK && !direction_IsDiagonal(pinned))
845                       || (ptype == BISHOP && direction_IsDiagonal(pinned))) {
846                     GenSliderMoves (mlist, ToMove, sq, pinned, NULL, capturesOnly);
847                     GenSliderMoves (mlist, ToMove, sq, dirOpposite[pinned],
848                                     NULL, capturesOnly);
849                 }
850             }
851         } else {
852             // This piece is free to move anywhere
853             if (ptype == PAWN) {
854                 GenPawnMoves (mlist, sq, NULL_DIR, NULL, genType);
855             } else {
856                 // Knight/Queen/Bishop/Rook:
857                 GenPieceMoves (mlist, sq, NULL, capturesOnly);
858             }
859         }
860     }
861 
862     // Lastly, king moves...
863     if (mask & (1 << KING)) {
864         bool castling = !numChecks;
865         GenKingMoves (mlist, genType, castling);
866     }
867 }
868 
869 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
870 // Position::IsLegalMove
871 //   Determines whether the specified move is legal in this position,
872 //   without requiring move generation (except for castling moves).
873 bool
IsLegalMove(simpleMoveT * sm)874 Position::IsLegalMove (simpleMoveT * sm) {
875     squareT from = sm->from;
876     squareT to = sm->to;
877     if (from > H8  ||  to > H8) { return false; }
878     if (from == to) { return false; }
879     pieceT mover = Board[from];
880     pieceT captured = Board[to];
881     if (piece_Color(mover) != ToMove) { return false; }
882     if (piece_Color(captured) == ToMove) { return false; }
883     if (sm->movingPiece != mover) { return false; }
884     mover = piece_Type (mover);
885     if (sm->promote != EMPTY  &&  mover != PAWN) { return false; }
886 
887     if (mover == PAWN) {
888         rankT rfrom = square_Rank(from);
889         rankT rto = square_Rank(to);
890         if (ToMove == BLACK) { rfrom = RANK_8 - rfrom; rto = RANK_8 - rto; }
891         int rdiff = (int)rto - (int)rfrom;
892         int fdiff = (int)square_Fyle(to) - (int)square_Fyle(from);
893         if (rdiff < 1  ||  rdiff > 2) { return false; }
894         if (fdiff < -1  ||  fdiff > 1) { return false; }
895         if (fdiff == 0) {  // Pawn push:
896             if (captured != EMPTY) { return false; }
897             if (rdiff == 2) {  // Two-square push:
898                 if (rfrom != RANK_2) { return false; }
899                 // Make sure the square in between is empty:
900                 squareT midsquare = from + ((to - from) / 2);
901                 if (Board[midsquare] != EMPTY) { return false; }
902             }
903         } else {  // Pawn capture:
904             if (rdiff != 1) { return false; }
905             if (captured == EMPTY) {
906                 // It must be en passant, or illegal
907                 if (to != EPTarget) { return false; }
908             }
909         }
910         // Check the promotion piece:
911         if (rto == RANK_8) {
912             pieceT p = sm->promote;
913             if (p != QUEEN  &&  p != ROOK  &&  p != BISHOP  &&  p != KNIGHT) {
914                 return false;
915             }
916         } else {
917             if (sm->promote != EMPTY) { return false; }
918         }
919 
920     } else if (piece_IsSlider(mover)) {
921         // Make sure the direction is valid:
922         directionT dir = sqDir[from][to];
923         if (dir == NULL_DIR) { return false; }
924         if (mover == ROOK  &&  direction_IsDiagonal(dir)) { return false; }
925         if (mover == BISHOP  &&  !direction_IsDiagonal(dir)) { return false; }
926         int delta = direction_Delta (dir);
927         // Make sure all the in-between squares are empty:
928         squareT dest = from + delta;
929         while (dest != to) {
930             if (Board[dest] != EMPTY) { return false; }
931             dest += delta;
932         }
933 
934     } else if (mover == KNIGHT) {
935         if (! square_IsKnightHop (from, to)) { return false; }
936 
937     } else /* (mover == KING) */ {
938         colorT enemy = color_Flip(ToMove);
939         if (square_Adjacent (to, GetKingSquare(enemy))) { return false; }
940         if (! square_Adjacent (from, to)) {
941             // The move must be castling, or illegal.
942             if (IsKingInCheck()) { return false; }
943             MoveList mlist;
944             GenCastling (&mlist);
945             return std::find(mlist.begin(), mlist.end(), cmpMove(*sm)) != mlist.end();
946         }
947     }
948 
949     // The move looks good, but does it leave the king in check?
950     squareT kingSq = (mover == KING) ? to : GetKingSquare(ToMove);
951     colorT enemy = color_Flip(ToMove);
952     DoSimpleMove (sm);
953     uint nchecks = CalcAttacks (enemy, kingSq, NULL);
954     UndoSimpleMove (sm);
955     return (nchecks == 0);
956 }
957 
958 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
959 // Position::MatchPawnMove():
960 //      Sets the LegalMoves list to contain the matching pawn move,
961 //      if there is one.
962 //
963 errorT
MatchPawnMove(MoveList * mlist,fyleT fromFyle,squareT to,pieceT promote)964 Position::MatchPawnMove (MoveList * mlist, fyleT fromFyle, squareT to, pieceT promote)
965 {
966     ASSERT(mlist != NULL);
967     pieceT promote2 = promote;
968 
969     mlist->Clear();
970 
971     sint diff = (int)square_Fyle(to) - (int)fromFyle;
972     if (diff < -1  ||  diff > 1) { return ERROR_InvalidMove; }
973     pieceT pawn;
974     rankT toRank = square_Rank(to);
975     fyleT toFyle = square_Fyle(to);
976     rankT promoteRank = (ToMove == WHITE ? RANK_8 : RANK_1);
977 
978     // from is the from square; backup is the alternative from square
979     // for a pawn move two squares forward.
980 
981     squareT from, backup = NS;
982 
983     if (ToMove == WHITE) {
984         pawn = WP;
985         if (toRank < RANK_3) { return ERROR_InvalidMove; }
986         from = square_Make(fromFyle, toRank - 1);
987         if (toRank == RANK_4  &&  fromFyle == toFyle) { backup = to - 16; }
988     } else {
989         pawn = BP;
990         if (toRank > RANK_6) { return ERROR_InvalidMove; }
991         from = square_Make(fromFyle, toRank + 1);
992         if (toRank == RANK_5  &&  fromFyle == toFyle) { backup = to + 16; }
993     }
994 
995     // See if the promotion piece is valid:
996 
997     if (toRank == promoteRank) {
998         // if (promote == EMPTY)  { return ERROR_InvalidMove; }
999         if (promote == EMPTY)  {
1000           // autopromote to queen
1001           promote2 = (ToMove == WHITE ? WQ : BQ);
1002         }
1003     } else {
1004         if (promote != EMPTY)  { return ERROR_InvalidMove; }
1005     }
1006 
1007     if (Board[from] != pawn) {
1008         // No match; but it could be a foward-two-squares move:
1009         if (backup == NS || Board[from] != EMPTY || Board[backup] != pawn) {
1010             // A forward-two-squares move is impossible.
1011             return ERROR_InvalidMove;
1012         }
1013         from = backup;
1014     }
1015 
1016     // OK, now 'from' is the only possible from-square. Is the move legal?
1017     // We make the move on the board and see if the King is in check.
1018 
1019     uint legal = 0;
1020     if (fromFyle == toFyle) {
1021         // Not a capture:
1022 
1023         if (Board[to] != EMPTY) { return ERROR_InvalidMove; }
1024         Board[to] = pawn;  Board[from] = EMPTY;
1025         if (CalcNumChecks (GetKingSquare()) == 0) {
1026             legal = 1;
1027         }
1028        Board[to] = EMPTY; Board[from] = pawn;
1029 
1030     } else {
1031         // It is a capture -- is it legal?
1032 
1033         pieceT captured = Board[to];
1034         if (captured == EMPTY) {
1035             // Must be an en passant or illegal move.
1036             if (to != EPTarget) { return ERROR_InvalidMove; }
1037             squareT epSquare = square_Make(toFyle, square_Rank(from));
1038 
1039             pieceT enemyPawn = piece_Make (color_Flip(ToMove), PAWN);
1040             // If following assert fails, eptarget was corrupt
1041             ASSERT (Board[epSquare] == enemyPawn);
1042 
1043             Board[to] = pawn; Board[from] = EMPTY;
1044             Board[epSquare] = EMPTY;
1045             Material[enemyPawn] --;
1046             if (CalcNumChecks (GetKingSquare()) == 0) { legal = 1; }
1047             Board[epSquare] = enemyPawn;
1048             Board[to] = EMPTY;
1049             Board[from] = pawn;
1050             Material[enemyPawn]++;
1051 
1052         } else {
1053             if (piece_Color(captured) == ToMove) {
1054                 // Capturing a friendly!
1055                 return ERROR_InvalidMove;
1056             } else {
1057                 // A regular capture. See if it leaves King in check:
1058                 Board[to] = pawn;  Board[from] = EMPTY;
1059                 Material[captured]--;
1060                 if (CalcNumChecks (GetKingSquare()) == 0) {
1061                     legal = 1;
1062                 }
1063                 Material[captured]++;
1064                 Board[to] = captured; Board[from] = pawn;
1065             }
1066         }
1067     }
1068 
1069     if (legal == 1) {
1070         AddLegalMove (mlist, from, to, promote2);
1071         return OK;
1072     }
1073     return ERROR_InvalidMove;
1074 }
1075 
1076 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1077 // Position::GenCheckEvasions():
1078 //      Generate legal moves for the side to move when the
1079 //      King is in check.
1080 //
1081 void
GenCheckEvasions(MoveList * mlist,pieceT mask,genMovesT genType,SquareList * checkSquares)1082 Position::GenCheckEvasions (MoveList * mlist, pieceT mask, genMovesT genType,
1083                             SquareList * checkSquares)
1084 {
1085     ASSERT(mlist != NULL);
1086     uint numChecks = checkSquares->Size();
1087 
1088     // Assert that king IS actually in check:
1089     ASSERT (numChecks > 0);
1090 
1091     bool genNonCaptures = (genType & GEN_NON_CAPS);
1092     bool capturesOnly = !genNonCaptures;
1093     mlist->Clear();
1094 
1095     squareT king = GetKingSquare (ToMove);
1096 
1097     // if it's double check, we can ONLY move the king
1098     if (numChecks == 1) {
1099         // OK, it is NOT a double check
1100         // Try to block piece/capture piece. Remember en passant!
1101         // First, generate a list of targets: squares between the king
1102         // and attacker to block, and the attacker's square.
1103 
1104         squareT attackSq = checkSquares->Get(0);
1105         directionT dir = sqDir[king][attackSq];
1106         SquareSet targets;  // Set of blocking/capturing squares.
1107         targets.Add(attackSq);
1108 
1109         // Now add squares we can might be able to block on.
1110         if (dir != NULL_DIR) {
1111             squareT sq = square_Move (king, dir);
1112             while (sq != attackSq) {
1113                 if (Board[sq] == EMPTY) { targets.Add(sq); }
1114                 sq = square_Move (sq, dir);
1115             }
1116         }
1117 
1118         // Try each non-King piece in turn. If a piece is pinned to
1119         // the king, don't bother since it cannot possibly block or
1120         // capture the piece that is giving check!
1121 
1122         uint numPieces = Count[ToMove];
1123         for (uint p2 = 1; p2 < numPieces; p2++) {
1124             squareT from = List[ToMove][p2];
1125             pieceT p2piece = Board[from];
1126             if (Pinned[p2] != NULL_DIR) { continue; }
1127             if (mask == EMPTY  ||  mask == piece_Type(p2piece)) {
1128                 if (piece_Type(p2piece) == PAWN) {
1129                     GenPawnMoves (mlist, from, NULL_DIR, &targets, genType);
1130                     // Capturing a pawn en passant will only get out
1131                     // of check if the pawn that moved two squares
1132                     // is doing the checking. If it is not, that means
1133                     // a discovered check with the last pawn move so
1134                     // taking en passant cannot get out of check.
1135                     if (EPTarget != NULL_SQUARE) {
1136                         squareT pawnSq = (ToMove == WHITE ? EPTarget - 8 : EPTarget + 8);
1137                         if (pawnSq == attackSq) {
1138                             SquareSet epset;
1139                             epset.Add(EPTarget);
1140                             GenPawnMoves (mlist, from, NULL_DIR, &epset, genType);
1141                         }
1142                     }
1143                 } else {
1144                     GenPieceMoves (mlist, from, &targets, capturesOnly);
1145                 }
1146             }
1147         }  // end of search for captures/blocks
1148     }
1149 
1150     // Now king moves -- just compute them normally:
1151     if (mask == EMPTY  ||  mask == KING) { GenKingMoves(mlist, genType, false); }
1152 
1153     return;
1154 }
1155 
1156 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1157 // Position::TreeCalcAttacks():
1158 //      Calculate attack score for a side on a square,
1159 //      using a recursive tree search.
1160 int
TreeCalcAttacks(colorT side,squareT target)1161 Position::TreeCalcAttacks(colorT side, squareT target)
1162 {
1163   int maxScore = -2;
1164   uint moveCount = 0, zeroCount = 0;
1165   MoveList moveList;
1166   GenerateCaptures(&moveList);
1167   for (uint i=0; i < moveList.Size(); i++) {
1168     simpleMoveT *smPtr = moveList.Get(i);
1169     if (smPtr->to == target) {
1170       if (piece_IsKing(Board[target])) return -1;
1171       moveCount++;
1172       DoSimpleMove(smPtr);
1173       int score = TreeCalcAttacks(color_Flip(side), target);
1174       UndoSimpleMove(smPtr);
1175       if (!score && ++zeroCount > 1) return -2;
1176       if (score > maxScore) maxScore = score;
1177     }
1178   }
1179 
1180  if (!moveCount) return 0;
1181  if (!maxScore) return -1;
1182  return -maxScore;
1183 }
1184 
1185 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1186 // Position::CalcAttacks():
1187 //      Calculate the number of attacks by a side on a square.
1188 //      This function also puts a list of the attacking piece squares
1189 //      in the fromSqs parameter if it is non-NULL.
1190 //
1191 //      It ONLY uses the Board[] and Material[] lists of the Position, and
1192 //      ASSUMES they are correct with respect to each other, but it does
1193 //      NOT use the List[] or ListPos[] information.
1194 //      This allows us to move pieces quickly (altering only Board[] and
1195 //      Material[]) and detect whether they leave the king in check,
1196 //      without having to update other information.
1197 uint
CalcAttacks(colorT side,squareT target,SquareList * fromSquares)1198 Position::CalcAttacks (colorT side, squareT target, SquareList * fromSquares)
1199 {
1200     // If squares is NULL, caller doesn't want a list of the squares of
1201     // attacking pieces. To avoid comparing fromSquares with NULL every time
1202     // we find a check, we set up a local array to use instead if fromSquares
1203     // is NULL.
1204     SquareList fromSqs;
1205     if (fromSquares == NULL) { fromSquares = &fromSqs; }
1206 
1207     fromSquares->Clear();
1208     squareT sq;
1209 
1210     // Bishop/Queen/Rook attacks: look at each of the 8 directions
1211     pieceT king, queen, rook, bishop, knight;
1212     if (side == WHITE) {
1213         king = WK; queen = WQ; rook = WR; bishop = WB; knight = WN;
1214     } else {
1215         king = BK; queen = BQ; rook = BR; bishop = BB; knight = BN;
1216     }
1217 
1218     uint numQueensRooks = Material[queen] + Material[rook];
1219     uint numQueensBishops = Material[queen] + Material[bishop];
1220 
1221     // We only bother if there are any sliding pieces of each type:
1222     if (numQueensRooks > 0) {
1223         fyleT fyle = square_Fyle (target);
1224         rankT rank = square_Rank (target);
1225         directionT dirs[4];
1226         uint ndirs = 0;
1227         if (FyleCount(queen,fyle) + FyleCount(rook,fyle) > 0) {
1228             dirs[ndirs++] = UP;  dirs[ndirs++] = DOWN;
1229         }
1230         if (RankCount(queen,rank) + RankCount(rook,rank) > 0) {
1231             dirs[ndirs++] = LEFT; dirs[ndirs++] = RIGHT;
1232         }
1233         for (uint i = 0; i < ndirs; i++) {
1234             directionT dir = dirs[i];
1235             int delta = direction_Delta (dir);
1236             squareT dest = target;
1237             squareT last = square_Last (target, dir);
1238 
1239             while (dest != last) {
1240                 dest += delta;
1241                 pieceT p = Board[dest];
1242                 if (p == EMPTY) {
1243                     // empty square: keep searching
1244                 } else if (p == queen  ||  p == rook) {
1245                     // Found an attacking piece
1246                     fromSquares->Add(dest);
1247                     break;
1248                 } else {
1249                     // Found a piece, but not a queen or rook
1250                     break;
1251                 }
1252             }
1253         }
1254     }
1255 
1256     // Now diagonal sliders: Queens/Bishops:
1257     if (numQueensBishops > 0) {
1258         leftDiagT left = square_LeftDiag (target);
1259         rightDiagT right = square_RightDiag (target);
1260         directionT dirs[4];
1261         uint ndirs = 0;
1262         if (LeftDiagCount(queen,left) + LeftDiagCount(bishop,left) > 0) {
1263             dirs[ndirs++] = UP_LEFT;  dirs[ndirs++] = DOWN_RIGHT;
1264         }
1265         if (RightDiagCount(queen,right) + RightDiagCount(bishop,right) > 0) {
1266             dirs[ndirs++] = UP_RIGHT;  dirs[ndirs++] = DOWN_LEFT;
1267         }
1268         for (uint i = 0; i < ndirs; i++) {
1269             directionT dir = dirs[i];
1270             int delta = direction_Delta (dir);
1271             squareT dest = target;
1272             squareT last = square_Last (target, dir);
1273 
1274             while (dest != last) {
1275                 dest += delta;
1276                 pieceT p = Board[dest];
1277                 if (p == EMPTY) {
1278                     // empty square: keep searching
1279                 } else if (p == queen  ||  p == bishop) {
1280                     // Found an attacking piece
1281                     fromSquares->Add(dest);
1282                     break;
1283                 } else {
1284                     // Found a piece, but not an enemy queen or bishop
1285                     break;
1286                 }
1287             }
1288         }
1289     }
1290 
1291     // Now knight checks: we only bother if there is a knight on the
1292     // opposite square color of the target square color.
1293     if (Material[knight] > 0
1294          &&  SquareColorCount(knight, color_Flip(square_Color(target))) > 0) {
1295         const squareT * destPtr = knightAttacks[target];
1296         while (true) {
1297             squareT dest = *destPtr;
1298             if (dest == NULL_SQUARE) { break; }
1299             if (Board[dest] == knight) {
1300                 fromSquares->Add(dest);
1301             }
1302             destPtr++;
1303         }
1304     }
1305 
1306     // Now pawn attacks:
1307     if (side == WHITE) {
1308         if (square_Rank(target) != RANK_1) {  //if (Material[WP] > 0) {
1309             sq = square_Move (target, DOWN_LEFT);
1310             if (Board[sq] == WP)  {
1311                 fromSquares->Add(sq);
1312             }
1313             sq = square_Move (target, DOWN_RIGHT);
1314             if (Board[sq] == WP)  {
1315                 fromSquares->Add(sq);
1316             }
1317         }
1318     } else {
1319         if (square_Rank(target) != RANK_8) {  //if (Material[BP] > 0) {
1320             sq = square_Move (target, UP_LEFT);
1321             if (Board[sq] == BP)  {
1322                 fromSquares->Add(sq);
1323             }
1324             sq = square_Move (target, UP_RIGHT);
1325             if (Board[sq] == BP)  {
1326                 fromSquares->Add(sq);
1327             }
1328         }
1329     }
1330 
1331     // King attacks:
1332     const squareT *destPtr = kingAttacks[target];
1333     do if (Board[*destPtr] == king) fromSquares->Add(*destPtr);
1334     while (*++destPtr != NULL_SQUARE);
1335 
1336     return fromSquares->Size();
1337 }
1338 
1339 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1340 // Position::IsKingInCheckDir
1341 //   Returns true if the King of the side to move is attacked
1342 //   by an enemy sliding piece (Queen/Rook/Bishop) from the
1343 //   specified direction.
1344 bool
IsKingInCheckDir(directionT dir)1345 Position::IsKingInCheckDir (directionT dir)
1346 {
1347     ASSERT (dir != NULL_DIR);
1348     squareT kingSq = GetKingSquare(ToMove);
1349     colorT enemy = color_Flip(ToMove);
1350     bool isDiagonal = direction_IsDiagonal(dir);
1351     pieceT queen = piece_Make (enemy, QUEEN);
1352     pieceT slider = piece_Make (enemy, (isDiagonal ? BISHOP : ROOK));
1353 
1354     // First, make sure the enemy has sliding pieces that could give check:
1355     uint nSliders = PieceCount(queen) + PieceCount(slider);
1356     if (nSliders == 0) { return false; }
1357 
1358     // Now make sure the enemy has a sliding piece on the appropriate
1359     // rank, file or diagonal:
1360     fyleT fyle = square_Fyle (kingSq);
1361     rankT rank = square_Rank (kingSq);
1362     leftDiagT ldiag = square_LeftDiag (kingSq);
1363     rightDiagT rdiag = square_RightDiag (kingSq);
1364 
1365     switch (dir) {
1366     case UP:
1367     case DOWN:
1368         nSliders = FyleCount(queen,fyle) + FyleCount(slider,fyle);
1369         break;
1370     case LEFT:
1371     case RIGHT:
1372         nSliders = RankCount(queen,rank) + RankCount(slider,rank);
1373         break;
1374     case UP_LEFT:
1375     case DOWN_RIGHT:
1376         nSliders = LeftDiagCount(queen,ldiag) + LeftDiagCount(slider,ldiag);
1377         break;
1378     case UP_RIGHT:
1379     case DOWN_LEFT:
1380         nSliders = RightDiagCount(queen,rdiag) + RightDiagCount(slider,rdiag);
1381         break;
1382     }
1383     if (nSliders == 0) { return false; }
1384 
1385     // Now move along the specified direction looking for a checking piece:
1386     squareT dest = kingSq;
1387     squareT last = square_Last (kingSq, dir);
1388     int delta = direction_Delta (dir);
1389 
1390     while (dest != last) {
1391         dest += delta;
1392         pieceT p = Board[dest];
1393         if (p == EMPTY) {
1394              // empty square: keep searching
1395         } else if (p == queen  ||  p == slider) {
1396             // Found an checking slider piece
1397             return true;
1398         } else {
1399             // Found a piece, but not an enemy queen or rook/bishop
1400             break;
1401         }
1402     }
1403 
1404     return false;
1405 }
1406 
1407 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1408 // Position::IsKingInCheck
1409 //   Returns true if the king of the side to move is in check.
1410 //   If the specified move is not NULL, it must be the legal move
1411 //   that the opponent played to reach this position. This will
1412 //   be used as a speed optimization, by skipping cases where the
1413 //   move could not have left the king in check.
1414 //
1415 bool
IsKingInCheck(simpleMoveT * sm)1416 Position::IsKingInCheck (simpleMoveT * sm)
1417 {
1418     if (sm == NULL) { return IsKingInCheck(); }
1419 
1420     squareT kingSq = GetKingSquare(ToMove);
1421     pieceT p = piece_Type (sm->movingPiece);
1422     if (sm->promote != EMPTY) { p = piece_Type(sm->promote); }
1423 
1424     // No optimization of the last move was castling:
1425     if (p == KING  &&  square_Fyle(sm->from) == E_FYLE) {
1426         fyleT toFyle = square_Fyle(sm->to);
1427         if (toFyle == C_FYLE  ||  toFyle == G_FYLE) {
1428             return IsKingInCheck();
1429         }
1430     }
1431     // No optimization for en passant capture:
1432     if (p == PAWN  &&  piece_Type(sm->capturedPiece) == PAWN) {
1433         rankT fromRank = square_Rank(sm->from);
1434         rankT capturedRank = square_Rank(sm->capturedSquare);
1435         if (fromRank == capturedRank) { return IsKingInCheck(); }
1436     }
1437 
1438     if (p == PAWN) {
1439         if (ToMove == WHITE) {
1440             if (Material[BP] > 0) {
1441                 squareT sq = square_Move (kingSq, UP_LEFT);
1442                 if (Board[sq] == BP)  { return true; }
1443                 sq = square_Move (kingSq, UP_RIGHT);
1444                 if (Board[sq] == BP)  { return true; }
1445             }
1446         } else {
1447             if (Material[WP] > 0) {
1448                 squareT sq = square_Move (kingSq, DOWN_LEFT);
1449                 if (Board[sq] == WP)  { return true; }
1450                 sq = square_Move (kingSq, DOWN_RIGHT);
1451                 if (Board[sq] == WP)  { return true; }
1452             }
1453         }
1454     } else if (p == KNIGHT) {
1455         if (square_IsKnightHop (kingSq, sm->to)) { return true; }
1456     } else if (p == KING) {
1457         // A king cannot directly check its adversary.
1458     } else {
1459         // A sliding piece:
1460         directionT toDir = sqDir[kingSq][sm->to];
1461         if (toDir != NULL_DIR  &&  IsKingInCheckDir(toDir)) { return true; }
1462     }
1463 
1464     // Now look for a discovered check from a sliding piece:
1465     directionT dir = sqDir[kingSq][sm->from];
1466     if (dir != NULL_DIR  &&  IsKingInCheckDir(dir)) { return true; }
1467 
1468     ASSERT (IsKingInCheck() == false);
1469     return false;
1470 }
1471 
1472 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1473 // Position::Mobility
1474 //    Returns the number of squares a rook or bishop of the specified
1475 //    color would attack from the specified square.
1476 uint
Mobility(pieceT p,colorT color,squareT from)1477 Position::Mobility (pieceT p, colorT color, squareT from)
1478 {
1479     ASSERT (p == ROOK  ||  p == BISHOP);
1480     uint mobility = 0;
1481     directionT rookDirs[4] = { UP, DOWN, LEFT, RIGHT };
1482     directionT bishopDirs[4]
1483         = { UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT };
1484     directionT * dirPtr = (p == ROOK ? rookDirs : bishopDirs);
1485 
1486     for (uint i=0; i < 4; i++) {
1487         directionT dir = dirPtr[i];
1488         int delta = direction_Delta (dir);
1489         squareT dest = from;
1490         squareT last = square_Last (from, dir);
1491 
1492         while (dest != last) {
1493             dest += delta;
1494             pieceT p = Board[dest];
1495             if (p == EMPTY) {  // Empty square
1496                 mobility++;
1497             } else if (piece_Color(p) == color) {  // Friendly piece
1498                 break;  // Finished with this direction.
1499             } else {  // Enemy piece
1500                 mobility++;
1501                 break;  // Finished with this direction.
1502             }
1503         }
1504     }
1505     return mobility;
1506 }
1507 
1508 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1509 // Position::IsKingInMate():
1510 //      Quick check if king is in mate.
1511 //
1512 bool
IsKingInMate(void)1513 Position::IsKingInMate (void)
1514 {
1515     SquareList checkSquares;
1516     uint numChecks = CalcNumChecks (GetKingSquare(ToMove), &checkSquares);
1517     if (numChecks == 0) { return false; }
1518     CalcPins ();
1519     MoveList mlist;
1520     GenCheckEvasions (&mlist, EMPTY, GEN_ALL_MOVES, &checkSquares);
1521     if (mlist.Size() == 0) { return true; }
1522     return false;
1523 }
1524 
1525 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1526 // Position::IsLegal()
1527 //   Verifies the position as being legal.
1528 //   Returns false for any of the following:
1529 //     - if the two kings are adjacent;
1530 //     - if there are any pawns on the 1st/8th rank;
1531 //     - if the side to move is already checking the enemy king.
1532 bool
IsLegal(void)1533 Position::IsLegal (void)
1534 {
1535     squareT stmKing = GetKingSquare();
1536     squareT enemyKing = GetEnemyKingSquare();
1537     if (square_Adjacent (stmKing, enemyKing)) { return false; }
1538     if (RankCount (WP, RANK_1) != 0) { return false; }
1539     if (RankCount (WP, RANK_8) != 0) { return false; }
1540     if (RankCount (BP, RANK_1) != 0) { return false; }
1541     if (RankCount (BP, RANK_8) != 0) { return false; }
1542     if (CalcAttacks (ToMove, enemyKing, NULL) > 0) {
1543          return false;
1544     }
1545     return true;
1546 }
1547 
1548 
1549 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1550 // Position::IsPromoMove():
1551 //      Returns true if the move is a promotion move.
1552 //      NOTE that the move may not actually be legal!
1553 //      The arguments 'from' and 'to' can be in either order.
1554 bool
IsPromoMove(squareT from,squareT to)1555 Position::IsPromoMove (squareT from, squareT to)
1556 {
1557     rankT seventh, eighth;
1558     pieceT pawn;
1559     if (ToMove == WHITE) { seventh = RANK_7; eighth = RANK_8; pawn = WP; }
1560     else { seventh = RANK_2; eighth = RANK_1; pawn = BP; }
1561     rankT fromR, toR;
1562     fromR = square_Rank(from);
1563     toR = square_Rank(to);
1564     if ( (fromR == seventh  &&  toR == eighth  &&  Board[from] == pawn)  ||
1565          (toR == seventh  &&  fromR == eighth  &&  Board[to] == pawn) ) {
1566         return 1;
1567     }
1568     return 0;
1569 }
1570 
1571 
1572 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1573 // Position::DoSimpleMove():
1574 //      Make the move on the board and update all the necessary
1575 //      fields in the simpleMove structure so it can be undone.
1576 //
1577 void
DoSimpleMove(simpleMoveT * sm)1578 Position::DoSimpleMove (simpleMoveT * sm)
1579 {
1580     ASSERT (sm != NULL);
1581     squareT from = sm->from;
1582     squareT to = sm->to;
1583     pieceT p = Board[from];
1584     pieceT ptype = piece_Type(p);
1585     colorT enemy = color_Flip(ToMove);
1586     ASSERT (p != EMPTY);
1587 
1588     // update move fields that (maybe) have not yet been set:
1589 
1590     sm->pieceNum = ListPos[from];
1591     sm->capturedPiece = Board[to];
1592     sm->capturedSquare = to;
1593     sm->castleFlags = Castling;
1594     sm->epSquare = EPTarget;
1595     sm->oldHalfMoveClock = HalfMoveClock;
1596 
1597     HalfMoveClock++;
1598     PlyCounter++;
1599 
1600     // Check for a null (empty) move:
1601     if (sm->isNullMove()) {
1602         ToMove = enemy;
1603         EPTarget = NULL_SQUARE;
1604         return;
1605     }
1606 
1607     // Handle en passant capture:
1608 
1609     if (ptype == PAWN  &&  sm->capturedPiece == EMPTY
1610             && square_Fyle(from) != square_Fyle(to)) {
1611 
1612         // This was an EP capture. We do not need to check it was a capture
1613         // since if a pawn lands on EPTarget it must capture to get there.
1614 
1615         pieceT enemyPawn = piece_Make(enemy, PAWN);
1616         sm->capturedSquare = (ToMove == WHITE ? (to - 8) : (to + 8));
1617         ASSERT (Board[sm->capturedSquare] == enemyPawn);
1618         sm->capturedPiece = enemyPawn;
1619     }
1620 
1621     // handle captures:
1622 
1623     if (sm->capturedPiece != EMPTY) {
1624         ASSERT (piece_Type(sm->capturedPiece) != KING);
1625         sm->capturedNum = ListPos[sm->capturedSquare];
1626         // update opponents List of pieces
1627         Count[enemy]--;
1628         ListPos[List[enemy][Count[enemy]]] = sm->capturedNum;
1629         List[enemy][sm->capturedNum] = List[enemy][Count[enemy]];
1630         Material[sm->capturedPiece]--;
1631         HalfMoveClock = 0;
1632         RemoveFromBoard (sm->capturedPiece, sm->capturedSquare);
1633     }
1634 
1635     // handle promotion:
1636 
1637     if (sm->promote != EMPTY) {
1638         ASSERT (p == piece_Make(ToMove, PAWN));
1639         Material[p]--;
1640         RemoveFromBoard (p, from);
1641         p = piece_Make(ToMove, sm->promote);
1642         Material[p]++;
1643         AddToBoard (p, from);
1644     }
1645 
1646     // now make the move:
1647     List[ToMove][sm->pieceNum] = to;
1648     ListPos[to] = sm->pieceNum;
1649     RemoveFromBoard (p, from);
1650     AddToBoard (p, to);
1651 
1652     // handle Castling:
1653 
1654     if (ptype == KING  &&  square_Fyle(from) == E_FYLE  &&
1655             (square_Fyle(to) == C_FYLE  ||  square_Fyle(to) == G_FYLE)) {
1656         squareT rookfrom, rookto;
1657         pieceT rook = piece_Make (ToMove, ROOK);
1658         if (square_Fyle(to) == C_FYLE) {
1659             rookfrom = to - 2;
1660             rookto = to + 1;
1661         } else {
1662             rookfrom = to + 1;
1663             rookto = to - 1;
1664         }
1665         ListPos[rookto] = ListPos[rookfrom];
1666         List[ToMove][ListPos[rookto]] = rookto;
1667         RemoveFromBoard (rook, rookfrom);
1668         AddToBoard (rook, rookto);
1669     }
1670 
1671     // Handle clearing of castling flags:
1672 
1673     if (Castling) {
1674         if (ptype == KING) {   // The king moved.
1675             SetCastling (ToMove, QSIDE, false);
1676             SetCastling (ToMove, KSIDE, false);
1677         }
1678         // See if a rook moved or was captured:
1679         if (ToMove == WHITE) {
1680             if (from == A1)  { SetCastling (WHITE, QSIDE, false); }
1681             if (from == H1)  { SetCastling (WHITE, KSIDE, false); }
1682             if (to == A8)    { SetCastling (BLACK, QSIDE, false); }
1683             if (to == H8)    { SetCastling (BLACK, KSIDE, false); }
1684         } else {
1685             if (from == A8)  { SetCastling (BLACK, QSIDE, false); }
1686             if (from == H8)  { SetCastling (BLACK, KSIDE, false); }
1687             if (to == A1)    { SetCastling (WHITE, QSIDE, false); }
1688             if (to == H1)    { SetCastling (WHITE, KSIDE, false); }
1689         }
1690     }
1691 
1692     // Set the EPTarget square, if a pawn advanced two squares and an
1693     // enemy pawn is on a square where en passant may be possible.
1694     EPTarget = NULL_SQUARE;
1695     if (ptype == PAWN) {
1696         rankT fromRank = square_Rank(from);
1697         rankT toRank = square_Rank(to);
1698         if (fromRank == RANK_2  &&  toRank == RANK_4
1699               &&  (Board[square_Move(to,LEFT)] == BP
1700                      ||  Board[square_Move(to,RIGHT)] == BP)) {
1701             EPTarget = square_Move(from, UP);
1702         }
1703         if (fromRank == RANK_7  &&  toRank == RANK_5
1704               &&  (Board[square_Move(to,LEFT)] == WP
1705                      ||  Board[square_Move(to,RIGHT)] == WP)) {
1706             EPTarget = square_Move(from, DOWN);
1707         }
1708         HalfMoveClock = 0; // 50-move clock resets on pawn moves.
1709     }
1710 
1711     ToMove = enemy;
1712     return;
1713 }
1714 
1715 
1716 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1717 // Position::UndoSimpleMove():
1718 //      Take back a simple move that has been made with DoSimpleMove().
1719 //
1720 void
UndoSimpleMove(simpleMoveT * m)1721 Position::UndoSimpleMove (simpleMoveT * m)
1722 {
1723     ASSERT (m != NULL);
1724     squareT from = m->from;
1725     squareT to = m->to;
1726     pieceT p = Board[to];
1727     EPTarget = m->epSquare;
1728     Castling = m->castleFlags;
1729     HalfMoveClock = m->oldHalfMoveClock;
1730     PlyCounter--;
1731     ToMove = color_Flip(ToMove);
1732     m->pieceNum = ListPos[to];
1733 
1734     // Check for a null move:
1735     if (m->isNullMove()) {
1736         return;
1737     }
1738 
1739     // Handle a capture: insert piece back into piecelist.
1740     // This works for EP captures too, since the square of the captured
1741     // piece is in the "capturedSquare" field rather than assuming the
1742     // value of the "to" field. The only time these two fields are
1743     // different is for an en passant move.
1744 
1745     if (m->capturedPiece != EMPTY) {
1746         colorT c = color_Flip(ToMove);
1747         ListPos[List[c][m->capturedNum]] = Count[c];
1748         ListPos[m->capturedSquare] = m->capturedNum;
1749         List[c][Count[c]] = List[c][m->capturedNum];
1750         List[c][m->capturedNum] = m->capturedSquare;
1751         Material[m->capturedPiece]++;
1752         Count[c]++;
1753     }
1754 
1755     // handle promotion:
1756 
1757     if (m->promote != EMPTY) {
1758         Material[p]--;
1759         RemoveFromBoard (p, to);
1760         p = piece_Make(ToMove, PAWN);
1761         Material[p]++;
1762         AddToBoard (p, to);
1763     }
1764 
1765     // now make the move:
1766 
1767     List[ToMove][m->pieceNum] = from;
1768     ListPos[from] = m->pieceNum;
1769     RemoveFromBoard (p, to);
1770     AddToBoard (p, from);
1771     if (m->capturedPiece != EMPTY) {
1772         AddToBoard (m->capturedPiece, m->capturedSquare);
1773     }
1774 
1775     // handle Castling:
1776 
1777     if ((piece_Type(p) == KING) && square_Fyle(from) == E_FYLE
1778             && (square_Fyle(to) == C_FYLE || square_Fyle(to) == G_FYLE)) {
1779         squareT rookfrom, rookto;
1780         pieceT rook = (ToMove == WHITE? WR : BR);
1781         if (square_Fyle(to) == C_FYLE) {
1782             rookfrom = to - 2;   rookto = to + 1;
1783         } else {
1784             rookfrom = to + 1;   rookto = to - 1;
1785         }
1786         ListPos[rookfrom] = ListPos[rookto];
1787         List[ToMove][ListPos[rookto]] = rookfrom;
1788         RemoveFromBoard (rook, rookto);
1789         AddToBoard (rook, rookfrom);
1790     }
1791     return;
1792 }
1793 
1794 
1795 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1796 // Position::RelocatePiece():
1797 //    Given a from-square and to-square, modifies the position so
1798 //    the piece on the from-square is relocated to the to-square.
1799 //    Returns an error if the from square is empty, or the target
1800 //    square is not empty, or the relocation would otherwise
1801 //    produce an illegal position (e.g. pawn on the 1st or 8th rank
1802 //    or a King in check).
1803 //
1804 errorT
RelocatePiece(squareT fromSq,squareT toSq)1805 Position::RelocatePiece (squareT fromSq, squareT toSq)
1806 {
1807     // Must have on-board squares:
1808     if (fromSq == NS ||  toSq == NS) { return ERROR; }
1809 
1810     // If squares are identical, just return success:
1811     if (fromSq == toSq) { return OK; }
1812 
1813     pieceT piece = Board[fromSq];
1814     pieceT ptype = piece_Type(piece);
1815     colorT pcolor = piece_Color(piece);
1816 
1817     // Must be relocating a nonempty piece to an empty square:
1818     if (piece == EMPTY  ||  Board[toSq] != EMPTY) { return ERROR; }
1819 
1820     // Pawns cannot relocate to the first or last rank:
1821     if (ptype == PAWN) {
1822         rankT toRank = square_Rank(toSq);
1823         if (toRank == RANK_1  ||  toRank == RANK_8) { return ERROR; }
1824     }
1825 
1826     // Locate the piece index in the appropriate list of pieces:
1827     uint index = ListPos[fromSq];
1828     ASSERT(List[pcolor][index] == fromSq);
1829 
1830     // Relocate the piece:
1831     List[pcolor][index] = toSq;
1832     ListPos[toSq] = index;
1833     RemoveFromBoard (piece, fromSq);
1834     AddToBoard (piece, toSq);
1835 
1836     // Check for adjacent kings or side to move giving check:
1837     if (! IsLegal()) {
1838         // Undo the relocation and return error:
1839         List[pcolor][index] = fromSq;
1840         RemoveFromBoard (piece, toSq);
1841         AddToBoard (piece, fromSq);
1842         return ERROR;
1843     }
1844 
1845     // Relocation successful:
1846     return OK;
1847 }
1848 
1849 
1850 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1851 // Position::MaterialValue():
1852 //    Returns the sum value of material for a particular side,
1853 //    where piece values are:
1854 //    King: 0 (since both sides always have one)
1855 //    Queen: 9
1856 //    Rook: 5
1857 //    Bishop, Knight: 3 each
1858 //    Pawn: 1
1859 uint
MaterialValue(colorT c)1860 Position::MaterialValue (colorT c)
1861 {
1862     ASSERT (c == WHITE  ||  c == BLACK);
1863     uint value = 0;
1864     if (c == WHITE) {
1865         value += 9 * PieceCount(WQ);
1866         value += 5 * PieceCount(WR);
1867         value += 3 * PieceCount(WB);
1868         value += 3 * PieceCount(WN);
1869         value += 1 * PieceCount(WP);
1870     } else {
1871         value += 9 * PieceCount(BQ);
1872         value += 5 * PieceCount(BR);
1873         value += 3 * PieceCount(BB);
1874         value += 3 * PieceCount(BN);
1875         value += 1 * PieceCount(BP);
1876     }
1877     return value;
1878 }
1879 
1880 
1881 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1882 // Position::MakeSANString():
1883 //      Make the SAN string for a simpleMove.
1884 //      The parameter 'sanFlag' indicates whether '+' and '#' symbols
1885 //      should be added to checking or mating moves.
1886 //
1887 void
MakeSANString(simpleMoveT * m,char * s,sanFlagT flag)1888 Position::MakeSANString (simpleMoveT * m, char * s, sanFlagT flag)
1889 {
1890     ASSERT (m != NULL  &&  s != NULL);
1891 
1892     // Make sure m->pieceNum is updated:
1893     m->pieceNum = ListPos[m->from];
1894     squareT from = List[ToMove][m->pieceNum];
1895     squareT to   = m->to;
1896     char * c     = s;
1897     pieceT piece = Board[from];
1898     pieceT p = piece_Type(piece);
1899 
1900     if (p == PAWN) {
1901         if (square_Fyle(from) != square_Fyle(to)) {  // pawn capture
1902             *c++ = square_FyleChar(from);
1903             *c++ = 'x';
1904         }
1905         *c++ = square_FyleChar(to);
1906         *c++ = square_RankChar(to);
1907         if ((square_Rank(to)==RANK_1) || (square_Rank(to)==RANK_8)) {
1908             *c++ = '=';
1909             *c++ = piece_Char(m->promote);
1910             p = piece_Type(m->promote);
1911         }
1912 
1913     } else if (p == KING) {
1914         if (m->isNullMove()) {
1915             //*c++ = 'n'; *c++ = 'u'; *c++ = 'l'; *c++ = 'l';
1916             *c++ = '-'; *c++ = '-';
1917         } else {
1918             switch (m->isCastle()) {
1919             case 0:
1920                 *c++ = 'K';
1921                 if (Board[to] != EMPTY)
1922                     *c++ = 'x';
1923                 *c++ = square_FyleChar(to);
1924                 *c++ = square_RankChar(to);
1925                 break;
1926             case 1:
1927                 *c++ = 'O';
1928                 *c++ = '-';
1929                 *c++ = 'O';
1930                 break;
1931             case 2:
1932                 *c++ = 'O';
1933                 *c++ = '-';
1934                 *c++ = 'O';
1935                 *c++ = '-';
1936                 *c++ = 'O';
1937                 break;
1938             }
1939         }
1940 
1941     } else {    // Queen/Rook/Bishop/Knight
1942         *c++ = piece_Char(p);
1943 
1944         // We only need to calculate legal moves to disambiguate if there
1945         // are more than one of this type of piece.
1946         if (Material[piece] > 1) {
1947             int ambiguity = 0;
1948             for (uint i = 1, n = Count[ToMove]; i < n; i++) {
1949                 squareT sq = List[ToMove][i];
1950                 if (sq == from || Board[sq] != piece)
1951                     continue;
1952 
1953                 if (!movegen::pseudo(sq, to, ToMove, p, Board, EMPTY))
1954                     continue; // Skip illegal move
1955 
1956                 std::pair<pieceT, squareT> pin =
1957                     movegen::opens_ray(sq, to, GetKingSquare(), Board, EMPTY);
1958                 if (pin.first != INVALID_PIECE &&
1959                     piece_Color_NotEmpty(Board[pin.second]) != ToMove) {
1960                     pieceT pt = piece_Type(Board[pin.second]);
1961                     if (pt == QUEEN || pt == pin.first)
1962                         continue; // Skip pinned piece
1963                 }
1964 
1965                 // Ambiguity:
1966                 // 1 (0001) --> need from-file (preferred) or from-rank
1967                 // 3 (0011) --> need from-file
1968                 // 5 (0101) --> need from-rank
1969                 // 7 (0111) --> need both from-file and from-rank
1970                 ambiguity |= 1;
1971                 if (square_Rank(from) == square_Rank(sq)) {
1972                     ambiguity |= 2; // 0b0010
1973                 } else if (square_Fyle(from) == square_Fyle(sq)) {
1974                     ambiguity |= 4; // 0b0100
1975                 }
1976             }
1977             if (ambiguity) {
1978                 if (ambiguity != 5)
1979                     *c++ = square_FyleChar(from); // print from-fyle
1980                 if (ambiguity >= 5)
1981                     *c++ = square_RankChar(from); // print from-rank
1982             }
1983         }
1984         if (Board[to] != EMPTY)
1985             *c++ = 'x';
1986         *c++ = square_FyleChar(to);
1987         *c++ = square_RankChar(to);
1988     }
1989 
1990     bool check;
1991     if (flag != SAN_NO_CHECKTEST) {
1992         squareT oldTo = Board[to];
1993         Board[to] = Board[from];
1994         Board[from] = EMPTY;
1995         squareT enemyKingSq = GetEnemyKingSquare();
1996         check = (p != KING) &&
1997                 movegen::attack(to, enemyKingSq, ToMove, p, Board, EMPTY);
1998         if (!check) {
1999             bool enpassant = (p == PAWN && oldTo == EMPTY &&
2000                               square_Fyle(from) != square_Fyle(to));
2001             if (!enpassant && (p != KING || !m->isCastle()) &&
2002                 !movegen::attack_slider(from, enemyKingSq, QUEEN, Board,
2003                                         EMPTY)) {
2004                 flag = SAN_NO_CHECKTEST;
2005             }
2006 
2007         } else if (flag != SAN_MATETEST) {
2008             *c++ = '+';
2009             flag = SAN_NO_CHECKTEST;
2010         }
2011         Board[from] = Board[to];
2012         Board[to] = oldTo;
2013     }
2014 
2015     // Now do the check or mate symbol:
2016     if (flag != SAN_NO_CHECKTEST) {
2017         // Now we make the move to test for check:
2018         DoSimpleMove (m);
2019         if (check || CalcNumChecks(GetKingSquare()) > 0) {
2020             char ch = '+';
2021             if (flag == SAN_MATETEST) {
2022                 MoveList mlist;
2023                 GenerateMoves (&mlist);
2024                 if (mlist.Size() == 0) { ch = '#'; }
2025             }
2026             *c++ = ch;
2027         }
2028         UndoSimpleMove (m);
2029     }
2030     *c = 0;
2031 }
2032 
2033 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2034 // Position::MakeUCIString():
2035 //      Make the UCI string for a simpleMove.
2036 //
2037 void
MakeUCIString(simpleMoveT * m,char * s)2038 Position::MakeUCIString (simpleMoveT * m, char * s)
2039 {
2040     ASSERT (m != NULL  &&  s != NULL);
2041 
2042     // Make sure m->pieceNum is updated:
2043     m->pieceNum = ListPos[m->from];
2044     pieceT  p    = piece_Type (Board[List[ToMove][m->pieceNum]]);
2045     squareT from = List[ToMove][m->pieceNum];
2046     squareT to   = m->to;
2047 
2048     char * c     = s;
2049 
2050     if (from == to && to != NULL_SQUARE) {
2051       // UCI standard for null move
2052         c[0] = '0';
2053         c[1] = '0';
2054         c[2] = '0';
2055         c[3] = '0';
2056         c[4] = 0;
2057         return;
2058     }
2059 
2060     *c++ = square_FyleChar(from);
2061     *c++ = square_RankChar(from);
2062     *c++ = square_FyleChar(to);
2063     *c++ = square_RankChar(to);
2064     if (p == PAWN) {
2065         if ((square_Rank(to)==RANK_1) || (square_Rank(to)==RANK_8)) {
2066             *c++ = piece_Char(m->promote);
2067         }
2068     }
2069 
2070     *c = 0;
2071 }
2072 
2073 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2074 // Position::ReadCoordMove():
2075 //      Given a move in coordinate notation,
2076 //      e.g. "e2e4" or "g1f3", generates the legal move it represents.
2077 //      Returns: OK or ERROR_InvalidMove.
2078 //      If "reverse" is true, coordinates in reverse order are acceptable,
2079 //      e.g. "f3g1" for 1.Nf3.
2080 //
ReadCoordMove(simpleMoveT * m,const char * str,int slen,bool reverse)2081 errorT Position::ReadCoordMove(simpleMoveT* m, const char* str, int slen,
2082                                bool reverse) {
2083     ASSERT (m != NULL  &&  str != NULL);
2084     fyleT fromFyle, toFyle;
2085     rankT fromRank, toRank;
2086     squareT from, to;
2087     pieceT promo = EMPTY;
2088 
2089     if (slen == 5) {
2090         promo = piece_FromChar(toupper(str[4]));
2091     } else if (slen != 4) { return ERROR_InvalidMove; }
2092 
2093     fromFyle = fyle_FromChar (str[0]);
2094     fromRank = rank_FromChar (str[1]);
2095     from = square_Make (fromFyle, fromRank);
2096     if (from == NS) { return ERROR_InvalidMove; }
2097 
2098     toFyle = fyle_FromChar (str[2]);
2099     toRank = rank_FromChar (str[3]);
2100     to = square_Make (toFyle, toRank);
2101     if (to == NS) { return ERROR_InvalidMove; }
2102 
2103     MoveList mlist;
2104     GenerateMoves(&mlist);
2105     for (size_t i = 0, n = mlist.Size(); i < n; i++) {
2106         simpleMoveT* sm = mlist.Get(i);
2107         if (sm->promote == promo) {
2108             if (sm->from == from  &&  sm->to == to) {
2109                 *m = *sm;
2110                 return OK;
2111             }
2112             if (reverse  &&  sm->to == from  &&  sm->from == to) {
2113                 *m = *sm;
2114                 return OK;
2115             }
2116         }
2117     }
2118     return ERROR_InvalidMove;
2119 }
2120 
trimCheck(const char * str,int slen)2121 static int trimCheck(const char* str, int slen) {
2122 	while (slen > 0) { // trim mate '#' or check '+'
2123 		--slen;
2124 		if (str[slen] != '#' && str[slen] != '+') {
2125 			++slen;
2126 			break;
2127 		}
2128 	}
2129 	return slen;
2130 }
2131 
ReadMovePawn(simpleMoveT * sm,const char * str,int slen,fyleT frFyle)2132 errorT Position::ReadMovePawn(simpleMoveT* sm, const char* str, int slen,
2133                               fyleT frFyle) {
2134 	ASSERT(sm != NULL && str != NULL && frFyle <= H_FYLE);
2135 
2136 	slen = trimCheck(str, slen);
2137 	if (slen < 2)
2138 		return ERROR_InvalidMove;
2139 
2140 	auto is_digit = [](char ch) {
2141 		return isdigit(static_cast<unsigned char>(ch));
2142 	};
2143 	auto is_lower = [](char ch) {
2144 		return islower(static_cast<unsigned char>(ch));
2145 	};
2146 
2147 	if (slen >= 4 && // Check if it is a coordinates-style move ("e2e4")
2148 	    is_digit(str[1]) && is_lower(str[2]) && is_digit(str[3])) {
2149 		return ReadCoordMove(sm, str, slen, false);
2150 	}
2151 
2152 	MoveList mlist;
2153 	pieceT promo = EMPTY;
2154 	auto last_ch = static_cast<unsigned char>(str[slen - 1]);
2155 	if (!is_digit(last_ch)) {
2156 		// Promotion, last char must be Q/R/B/N.
2157 		promo = piece_FromChar(toupper(last_ch));
2158 		if (promo != QUEEN && promo != ROOK && promo != KNIGHT &&
2159 		    promo != BISHOP) {
2160 			return ERROR_InvalidMove;
2161 		}
2162 		slen--;
2163 		// Accept the move even if it is of the form "a8Q" not "a8=Q":
2164 		if (str[slen - 1] == '=') {
2165 			slen--;
2166 		}
2167 	}
2168 	if (slen < 2)
2169 		return ERROR_InvalidMove;
2170 
2171 	// Check for the compact form of capture with no rank,
2172 	// e.g. "ed" or "de=Q":
2173 	if (slen == 2 && (str[1] >= 'a' && str[1] <= 'h')) {
2174 		auto toFyle = fyle_FromChar(str[1]);
2175 		// Check each rank in turn, looking for the capture:
2176 		for (rankT r = RANK_1; r <= RANK_8; r++) {
2177 			auto to = square_Make(toFyle, r);
2178 			if (MatchPawnMove(&mlist, frFyle, to, promo) == OK) {
2179 				*sm = *(mlist.Get(0));
2180 				return OK;
2181 			}
2182 		}
2183 		// It is NOT a valid capture with no rank:
2184 		return ERROR_InvalidMove;
2185 	}
2186 
2187 	auto toFyle = fyle_FromChar(str[slen - 2]);
2188 	auto toRank = rank_FromChar(str[slen - 1]);
2189 	if (toRank == NO_RANK || toFyle == NO_FYLE)
2190 		return ERROR_InvalidMove;
2191 
2192 	auto to = square_Make(toFyle, toRank);
2193 	if (MatchPawnMove(&mlist, frFyle, to, promo) != OK)
2194 		return ERROR_InvalidMove;
2195 
2196 	*sm = *(mlist.Get(0));
2197 	return OK;
2198 }
2199 
ReadMoveKing(simpleMoveT * sm,const char * str,int slen)2200 errorT Position::ReadMoveKing(simpleMoveT* sm, const char* str, int slen) {
2201 	ASSERT(sm != NULL && str != NULL);
2202 
2203 	slen = trimCheck(str, slen);
2204 	if (slen < 3 || slen > 6)
2205 		return ERROR_InvalidMove;
2206 
2207 	auto toRank = rank_FromChar(str[slen - 1]);
2208 	auto toFyle = fyle_FromChar(str[slen - 2]);
2209 	if (toRank == NO_RANK || toFyle == NO_FYLE)
2210 		return ERROR_InvalidMove;
2211 
2212 	auto target = square_Make(toFyle, toRank);
2213 	squareT kingSq = GetKingSquare(ToMove);
2214 	if (!movegen::valid_king(kingSq, target))
2215 		return ERROR_InvalidMove;
2216 
2217 	pieceT captured = Board[target];
2218 	if (captured != EMPTY && (piece_Color_NotEmpty(captured) == ToMove ||
2219 	                          piece_Type(captured) == KING)) {
2220 		return ERROR_InvalidMove;
2221 	}
2222 
2223 	// XXX We should also check for adjacency to enemy King!!
2224 	if (movegen::valid_king(GetKingSquare(color_Flip(ToMove)), target))
2225 		return ERROR_InvalidMove;
2226 
2227 	// Now make the move on the Board and Material lists, and see if it
2228 	// leaves the King in check:
2229 	auto movingPiece = piece_Make(ToMove, KING);
2230 	Board[target] = movingPiece;
2231 	Board[kingSq] = EMPTY;
2232 	if (captured != EMPTY) {
2233 		Material[captured]--;
2234 	}
2235 	auto nChecks = CalcNumChecks(target);
2236 	if (captured != EMPTY) {
2237 		Material[captured]++;
2238 	}
2239 	Board[target] = captured;
2240 	Board[kingSq] = movingPiece;
2241 	if (nChecks)
2242 		return ERROR_InvalidMove;
2243 
2244 	sm->from = kingSq;
2245 	sm->to = target;
2246 	sm->promote = EMPTY;
2247 	sm->movingPiece = movingPiece;
2248 	return OK;
2249 }
2250 
2251 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2252 // Position::ReadMove():
2253 //      Given a move in (possibly sloppy) PGN notation,
2254 //      generates the legal move it corresponds to.
2255 //      Returns: OK or ERROR_InvalidMove.
2256 //
ReadMove(simpleMoveT * sm,const char * str,int slen,pieceT piece)2257 errorT Position::ReadMove(simpleMoveT* sm, const char* str, int slen,
2258                           pieceT piece) {
2259 	ASSERT(sm != NULL && str != NULL);
2260 	ASSERT(piece == QUEEN || piece == ROOK || piece == BISHOP ||
2261 	       piece == KNIGHT);
2262 
2263 	slen = trimCheck(str, slen);
2264 	if (slen < 3 || slen > 6)
2265 		return ERROR_InvalidMove;
2266 
2267 	auto toRank = rank_FromChar(str[slen - 1]);
2268 	auto toFyle = fyle_FromChar(str[slen - 2]);
2269 	if (toRank == NO_RANK || toFyle == NO_FYLE)
2270 		return ERROR_InvalidMove;
2271 	auto to = square_Make(toFyle, toRank);
2272 
2273 	pieceT captured = Board[to];
2274 	if (captured != EMPTY && (piece_Color_NotEmpty(captured) == ToMove ||
2275 	                          piece_Type(captured) == KING)) {
2276 		return ERROR_InvalidMove;
2277 	}
2278 
2279 	auto frFyle = NO_FYLE;
2280 	auto frRank = NO_RANK;
2281 	if (slen > 3) { // There is some ambiguity information in the input string.
2282 		frFyle = fyle_FromChar(str[1]);
2283 		frRank = rank_FromChar(str[1]);
2284 		if (frRank == NO_RANK && slen > 4) {
2285 			frRank = rank_FromChar(str[2]);
2286 		}
2287 	}
2288 
2289 	// Loop through looking for pieces of the corresponding type. We start at 1
2290 	// since the King is always the piece at position 0 in the list.
2291 	int matchCount = 0;
2292 	auto movingPiece = piece_Make(ToMove, piece);
2293 	int nPieces = Material[movingPiece];
2294 	squareT kingSq = GetKingSquare(ToMove);
2295 	for (unsigned i = 1, n = Count[ToMove]; i < n && nPieces; i++) {
2296 		auto from = List[ToMove][i];
2297 		if (Board[from] != movingPiece)
2298 			continue;
2299 
2300 		--nPieces;
2301 		if ((frFyle != NO_FYLE && frFyle != square_Fyle(from)) ||
2302 		    (frRank != NO_RANK && frRank != square_Rank(from)))
2303 			continue;
2304 
2305 		if (!movegen::pseudo(from, to, ToMove, piece, Board, EMPTY))
2306 			continue;
2307 
2308 		auto pin = movegen::opens_ray(from, to, kingSq, Board, EMPTY);
2309 		if (pin.first != INVALID_PIECE) {
2310 			auto p = Board[pin.second];
2311 			if (piece_Color_NotEmpty(p) != ToMove &&
2312 			    (piece_Type(p) == QUEEN || piece_Type(p) == pin.first))
2313 				continue;
2314 		}
2315 
2316 		++matchCount;
2317 		sm->from = from;
2318 		sm->to = to;
2319 		sm->promote = EMPTY;
2320 		sm->movingPiece = movingPiece;
2321 	}
2322 	return (matchCount == 1) ? OK                 // ok.
2323 	                         : ERROR_InvalidMove; // No match, or too many
2324 	                                              // (ambiguous) moves match.
2325 }
2326 
ReadMoveCastle(simpleMoveT * sm,const char * str,int slen)2327 errorT Position::ReadMoveCastle(simpleMoveT* sm, const char* str, int slen) {
2328 	slen = trimCheck(str, slen);
2329 
2330 	auto str_equal = [&](const char* const_str, const int len) {
2331 		return slen == len && std::equal(str, str + len, const_str);
2332 	};
2333 
2334 	// short castle
2335 	if (str_equal("O-O", 3) || str_equal("OO", 2)) {
2336 		squareT kingSq = GetKingSquare(ToMove);
2337 		if (kingSq != (ToMove == WHITE ? E1 : E8))
2338 			return ERROR_InvalidMove;
2339 
2340 		if (Board[kingSq + 1] != EMPTY || Board[kingSq + 2] != EMPTY ||
2341 		    CalcNumChecks(kingSq) > 0 || CalcNumChecks(kingSq + 1) > 0 ||
2342 		    CalcNumChecks(kingSq + 2) > 0) {
2343 			return ERROR_InvalidMove;
2344 		}
2345 		sm->from = kingSq;
2346 		sm->to = kingSq + 2;
2347 		sm->promote = EMPTY;
2348 		sm->movingPiece = KING;
2349 		sm->capturedPiece = EMPTY;
2350 		return GetCastling(ToMove, KSIDE) ? OK : ERROR_CastlingAvailability;
2351 	}
2352 	// long castle
2353 	if (str_equal("O-O-O", 5) || str_equal("OOO", 3)) {
2354 		squareT kingSq = GetKingSquare(ToMove);
2355 		if (kingSq != (ToMove == WHITE ? E1 : E8))
2356 			return ERROR_InvalidMove;
2357 
2358 		if (Board[kingSq - 1] != EMPTY || Board[kingSq - 2] != EMPTY ||
2359 		    Board[kingSq - 3] != EMPTY || CalcNumChecks(kingSq) > 0 ||
2360 		    CalcNumChecks(kingSq - 1) > 0 || CalcNumChecks(kingSq - 2) > 0) {
2361 			return ERROR_InvalidMove;
2362 		}
2363 		sm->from = kingSq;
2364 		sm->to = kingSq - 2;
2365 		sm->promote = EMPTY;
2366 		sm->movingPiece = KING;
2367 		sm->capturedPiece = EMPTY;
2368 		return GetCastling(ToMove, QSIDE) ? OK : ERROR_CastlingAvailability;
2369 	}
2370 	return ERROR_InvalidMove;
2371 }
2372 
ParseMove(simpleMoveT * sm,const char * str)2373 errorT Position::ParseMove(simpleMoveT* sm, const char* str) {
2374 	while (!isalpha(static_cast<unsigned char>(*str)) && *str != '\0') {
2375 		str++; // trim left
2376 	}
2377 	const char* begin = str;
2378 	while (!isspace(static_cast<unsigned char>(*str)) && *str != '\0') {
2379 		str++; // trim right
2380 	}
2381 	return ParseMove(sm, begin, str);
2382 }
2383 
2384 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2385 // Position::ParseMove():
2386 //      Parse a single move from SAN-style notation.
2387 //
ParseMove(simpleMoveT * sm,const char * str,const char * strEnd)2388 errorT Position::ParseMove(simpleMoveT* sm, const char* str,
2389                            const char* strEnd) {
2390 	ASSERT(str != NULL);
2391 
2392 	int length = static_cast<int>(std::distance(str, strEnd));
2393 	if (length < 2 || length > 9)
2394 		return ERROR_InvalidMove;
2395 
2396 	switch (str[0]) {
2397 	case 'a':
2398 		return ReadMovePawn(sm, str, length, A_FYLE);
2399 	case 'b':
2400 		return ReadMovePawn(sm, str, length, B_FYLE);
2401 	case 'c':
2402 		return ReadMovePawn(sm, str, length, C_FYLE);
2403 	case 'd':
2404 		return ReadMovePawn(sm, str, length, D_FYLE);
2405 	case 'e':
2406 		return ReadMovePawn(sm, str, length, E_FYLE);
2407 	case 'f':
2408 		return ReadMovePawn(sm, str, length, F_FYLE);
2409 	case 'g':
2410 		return ReadMovePawn(sm, str, length, G_FYLE);
2411 	case 'h':
2412 		return ReadMovePawn(sm, str, length, H_FYLE);
2413 	case 'K':
2414 		return ReadMoveKing(sm, str, length);
2415 	case 'Q':
2416 		return ReadMove(sm, str, length, QUEEN);
2417 	case 'R':
2418 		return ReadMove(sm, str, length, ROOK);
2419 	case 'B':
2420 		return ReadMove(sm, str, length, BISHOP);
2421 	case 'N':
2422 		return ReadMove(sm, str, length, KNIGHT);
2423 	case 'O':
2424 		return ReadMoveCastle(sm, str, length);
2425 	}
2426 
2427 	// Check for a null move:
2428 	if ((length == 2 && std::equal(str, str + 2, "--")) ||
2429 	    (length == 2 && std::equal(str, str + 2, "Z0")) ||
2430 	    (length == 4 && std::equal(str, str + 4, "null"))) {
2431 		sm->pieceNum = 0;
2432 		sm->from = GetKingSquare(ToMove);
2433 		sm->to = sm->from;
2434 		sm->movingPiece = Board[sm->from];
2435 		sm->promote = EMPTY;
2436 		return OK;
2437 	}
2438 
2439 	// Invalid move, check for a misspelled first char:
2440 	switch (str[0]) {
2441 	case 'A':
2442 		return ReadMovePawn(sm, str, length, A_FYLE);
2443 	case 'C':
2444 		return ReadMovePawn(sm, str, length, C_FYLE);
2445 	case 'D':
2446 		return ReadMovePawn(sm, str, length, D_FYLE);
2447 	case 'E':
2448 		return ReadMovePawn(sm, str, length, E_FYLE);
2449 	case 'F':
2450 		return ReadMovePawn(sm, str, length, F_FYLE);
2451 	case 'G':
2452 		return ReadMovePawn(sm, str, length, G_FYLE);
2453 	case 'H':
2454 		return ReadMovePawn(sm, str, length, H_FYLE);
2455 	case 'P':
2456 		return ParseMove(sm, str + 1, strEnd);
2457 	case 'k':
2458 		return ReadMoveKing(sm, str, length);
2459 	case 'q':
2460 		return ReadMove(sm, str, length, QUEEN);
2461 	case 'r':
2462 		return ReadMove(sm, str, length, ROOK);
2463 	case 'n':
2464 		return ReadMove(sm, str, length, KNIGHT);
2465 	}
2466 	return ERROR_InvalidMove;
2467 }
2468 
2469 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2470 // Position::CalcSANStrings():
2471 //      Calculate the SAN string for each move in the legal moves list.
2472 //
2473 void
CalcSANStrings(sanListT * sanList,sanFlagT flag)2474 Position::CalcSANStrings (sanListT *sanList, sanFlagT flag)
2475 {
2476     MoveList mlist;
2477     GenerateMoves(&mlist);
2478     for (size_t i = 0, n = mlist.Size(); i < n; i++) {
2479         MakeSANString(mlist.Get(i), sanList->list[i], flag);
2480     }
2481     sanList->num = mlist.Size();
2482     sanList->current = true;
2483 }
2484 
2485 errorT
ReadFromLongStr(const char * str)2486 Position::ReadFromLongStr (const char * str)
2487 {
2488     pieceT pieceFromByte [256] = {EMPTY};
2489     pieceFromByte [(int) 'K'] = WK;  pieceFromByte [(int) 'k'] = BK;
2490     pieceFromByte [(int) 'Q'] = WQ;  pieceFromByte [(int) 'q'] = BQ;
2491     pieceFromByte [(int) 'R'] = WR;  pieceFromByte [(int) 'r'] = BR;
2492     pieceFromByte [(int) 'B'] = WB;  pieceFromByte [(int) 'b'] = BB;
2493     pieceFromByte [(int) 'N'] = WN;  pieceFromByte [(int) 'n'] = BN;
2494     pieceFromByte [(int) 'P'] = WP;  pieceFromByte [(int) 'p'] = BP;
2495 
2496     Clear();
2497     for (squareT sq=A1; sq <= H8; sq++) {
2498         if (str[sq] == '.') { continue; }
2499         pieceT p = pieceFromByte [(byte) str[sq]];
2500         if (p == EMPTY) { return ERROR_Corrupt; }
2501         if (AddPiece (p,sq) != OK) { return ERROR_Corrupt; }
2502     }
2503     switch (str[65]) {
2504     case 'w':
2505         SetToMove (WHITE);
2506         break;
2507     case 'b':
2508         SetToMove (BLACK);
2509         break;
2510     default:
2511         return ERROR_Corrupt;
2512     }
2513     return OK;
2514 }
2515 
2516 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2517 // Position::MakeLongStr():
2518 //      Make a string representing the board. It will be 66 characters
2519 //      long, encoding the 64 squares (in the order a1,b1,...,g8,h8
2520 //      with white pieces in upper case, black pieces in lower case,
2521 //      and empty squares as dots) then a space, and finally "w" or "b"
2522 //      indicating the side to move. Example for the starting position:
2523 //      "RNBQKBNRPPPPPPPP................................pppppppprbnqkbnr w"
2524 //
2525 void
MakeLongStr(char * str)2526 Position::MakeLongStr (char * str)
2527 {
2528     ASSERT (str != NULL);
2529     char * s = str;
2530     for (squareT sq = A1; sq <= H8; sq++) {
2531         *s++ = PIECE_CHAR[Board[sq]];
2532     }
2533     *s++ = ' ';
2534     *s++ = (ToMove == WHITE ? 'w' : 'b');
2535     *s = 0;
2536 }
2537 
2538 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2539 // Position::ReadFromCompactStr():
2540 //    Sets the position from the provided Null-terminated 33-byte
2541 //    compact string.
2542 //    The first 32 bytes contain the square valued, 4 bits per value,
2543 //    for the square order A1, B1, ...., G8, H8.
2544 //    The next byte contains the side to move, 1 for White or 2 for Black.
2545 //    The final two bytes contain castling and en passant rights.
2546 //    To ensure no bytes within the staring are zero-valued (so it
2547 //    can be used as a regular null-terminated string), the value 1
2548 //    is added to the color, castling and en passant fields.
2549 errorT
ReadFromCompactStr(const byte * str)2550 Position::ReadFromCompactStr (const byte * str)
2551 {
2552     Clear();
2553     for (uint i=0; i < 32; i++) {
2554         pieceT p = str[i] >> 4;
2555         if (p != EMPTY) {
2556             if (AddPiece (p, i * 2) != OK) {
2557                 return ERROR_Corrupt;
2558             }
2559         }
2560         p = str[i] & 15;
2561         if (p != EMPTY) {
2562             if (AddPiece (p, i * 2 + 1) != OK) {
2563                 return ERROR_Corrupt;
2564             }
2565         }
2566     }
2567     colorT toMove = str[32] - 1;
2568     if (toMove != WHITE  &&  toMove != BLACK) {
2569         return ERROR_Corrupt;
2570     }
2571     ToMove = toMove;
2572     Castling = str[33] - 1;
2573     EPTarget = str[34] - 1;
2574     return OK;
2575 }
2576 
2577 void
PrintCompactStr(char * cboard)2578 Position::PrintCompactStr (char * cboard)
2579 {
2580     for (uint i=0; i < 32; i++) {
2581         uint i2 = i << 1;
2582         cboard[i] = (byte)(Board[i2] << 4) | Board[i2+1];
2583     }
2584     cboard[32] = 1 + ToMove;
2585     cboard[33] = 1 + Castling;
2586 
2587     // Check that there really is an enemy pawn that might
2588     // be able to capture to the en passant square. For example,
2589     // if the EP square is c6 but there is no white pawn on
2590     // b5 or d5, then en passant should be ignored.
2591 
2592     squareT ep = EPTarget;
2593     if (ToMove == WHITE) {
2594         if (Board[square_Move (ep, DOWN_LEFT)] != WP  &&
2595             Board[square_Move (ep, DOWN_RIGHT)] != WP) { ep = NULL_SQUARE; }
2596 
2597     } else {
2598         if (Board[square_Move (ep, UP_LEFT)] != BP  &&
2599             Board[square_Move (ep, UP_RIGHT)] != BP) { ep = NULL_SQUARE; }
2600 
2601     }
2602     cboard[34] = 1 + ep;
2603     cboard[35] = 0;
2604 }
2605 
2606 void
PrintCompactStrFlipped(char * cboard)2607 Position::PrintCompactStrFlipped (char * cboard)
2608 {
2609     for (uint i=0; i < 32; i++) {
2610         uint i2 = i << 1;
2611         // Flip 1st rank to 8th, etc:
2612         i2 = ((7 - (i2)/8) * 8 + ((i2) % 8));
2613         cboard[i] = (byte)(PIECE_FLIP[Board[i2]] << 4) |
2614             (byte)(PIECE_FLIP[Board[i2+1]]);
2615     }
2616     cboard[32] = 1 + color_Flip(ToMove);
2617     cboard[33] = 1 + Castling;
2618     cboard[34] = 1 + EPTarget;
2619     cboard[35] = 0;
2620 }
2621 
2622 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2623 // Position::ReadFromFEN():
2624 //      Setup the position from a FEN string.
2625 //      Note: the slashes usually found in Fen strings to mark the start
2626 //      of a new row do not need to be present, but if they are, they must
2627 //      appear at the actual start of a new row or the string will be
2628 //      considered corrupt.
2629 //
2630 //      IMPORTANT: the shortcut of having a two-digit number to represent
2631 //      a number of empty rows (e.g. "/24/" instead of "/8/8/8/") is NOT
2632 //      accepted by this function.
2633 //
2634 //      It is not considered an error for the halfmove clock or fullmove
2635 //      counter to be invalid, so this routine can also read positions
2636 //      from EPD lines (which only share the first four fields with FEN).
2637 errorT
ReadFromFEN(const char * str)2638 Position::ReadFromFEN (const char * str)
2639 {
2640     auto is_space = [](char ch) {
2641         return isspace(static_cast<unsigned char>(ch));
2642     };
2643 
2644     // pieceFromByte[] converts a character to its piece, e.g. 'k' -> BK.
2645     static pieceT pieceFromByte [256];
2646 
2647     // fenSqToRealSquare[] converts a fen square (0 to 63) to its real
2648     // square. E.g: [0] -> A8, [1] -> B8, .... [63] -> H1.
2649     static squareT fenSqToRealSquare [64];
2650 
2651     // Note the first Call to set up the static arrays only once:
2652     static int firstCall = 1;
2653 
2654     ASSERT (str != NULL);
2655     const char * s = str;
2656     int count = 0;
2657 
2658     if (firstCall) {
2659         firstCall = 0;
2660 
2661         // Set up pieceFromByte[]:
2662         for (int i=0; i < 256; i++) { pieceFromByte[i] = EMPTY; }
2663         pieceFromByte [(int) 'K'] = WK;  pieceFromByte [(int) 'k'] = BK;
2664         pieceFromByte [(int) 'Q'] = WQ;  pieceFromByte [(int) 'q'] = BQ;
2665         pieceFromByte [(int) 'R'] = WR;  pieceFromByte [(int) 'r'] = BR;
2666         pieceFromByte [(int) 'B'] = WB;  pieceFromByte [(int) 'b'] = BB;
2667         pieceFromByte [(int) 'N'] = WN;  pieceFromByte [(int) 'n'] = BN;
2668         pieceFromByte [(int) 'P'] = WP;  pieceFromByte [(int) 'p'] = BP;
2669 
2670         // Set up fenSqToRealSq[]:
2671         for (int sq=0; sq < 64; sq++) {
2672             fenSqToRealSquare [sq] = (squareT)((7 - (sq)/8) * 8 + ((sq) % 8));
2673         }
2674     }
2675 
2676     Clear ();
2677     while (count < 64) {
2678         if (*s == '/') {
2679             // A FEN string does not have to contain '/'s but if one
2680             // appears anywhere except the start of a row, it is an error:
2681 
2682             if (count % 8) { return ERROR_InvalidFEN; }
2683 
2684         } else if (*s > '0'  &&  *s < '9') {
2685             count += (*s - '0');
2686 
2687         } else {
2688             pieceT p = pieceFromByte [(byte) *s];
2689             if (p == EMPTY) { return ERROR_InvalidFEN; }
2690             if (AddPiece (p, fenSqToRealSquare[count]) != OK) {
2691                 return ERROR_InvalidFEN;
2692             }
2693             count++;
2694         }
2695         s++;
2696     }
2697     if (Material[WK] != 1  ||  Material[BK] != 1) { return ERROR_InvalidFEN; }
2698 
2699     // Now the side to move:
2700     while (is_space(*s)) { s++; }
2701     switch (*s) {
2702     case 'w':
2703         SetToMove (WHITE);
2704         break;
2705     case 'b':
2706         SetToMove (BLACK);
2707         break;
2708     default:
2709         return ERROR_InvalidFEN;
2710     }
2711     s++;
2712 
2713     if (! IsLegal()) { return ERROR_InvalidFEN; }
2714 
2715     // Now the castling flags:
2716     while (is_space(*s)) { s++; }
2717     if (*s == '-') {
2718         s++;  // do nothing
2719     } else if (*s == 0) {
2720         // The FEN has no castling field, so just guess that
2721         // castling is possible whenever a king and rook are
2722         // still on their starting squares:
2723         if (Board[E1] == WK) {
2724             if (Board[A1] == WR) { SetCastling (WHITE, QSIDE, true); }
2725             if (Board[H1] == WR) { SetCastling (WHITE, KSIDE, true); }
2726         }
2727         if (Board[E8] == BK) {
2728             if (Board[A8] == BR) { SetCastling (BLACK, QSIDE, true); }
2729             if (Board[H8] == BR) { SetCastling (BLACK, KSIDE, true); }
2730         }
2731     } else {
2732         while (!is_space(*s)  &&  *s != 0) {
2733             switch (*s) {
2734             case 'Q':
2735                 SetCastling (WHITE, QSIDE, true);
2736                 break;
2737             case 'q':
2738                 SetCastling (BLACK, QSIDE, true);
2739                 break;
2740             case 'K':
2741                 SetCastling (WHITE, KSIDE, true);
2742                 break;
2743             case 'k':
2744                 SetCastling (BLACK, KSIDE, true);
2745                 break;
2746             default:
2747                 return ERROR_InvalidFEN;
2748             }
2749             s++;
2750         }
2751     }
2752 
2753     // Now the EP target:
2754     while (is_space(*s)) { s++; }
2755     if (*s == 0) {
2756         // do nothing
2757     } else if (*s == '-') {
2758         EPTarget = NULL_SQUARE;
2759         s++;  // No EP target
2760     } else {
2761         char fylec = *s; s++;
2762         if (fylec < 'a'  ||  fylec > 'h') {
2763             return ERROR_InvalidFEN;
2764         }
2765         char rankc = *s; s++;
2766         if (rankc != '3'  &&  rankc != '6') {
2767             return ERROR_InvalidFEN;
2768         }
2769         EPTarget = square_Make(fyle_FromChar(fylec), rank_FromChar(rankc));
2770     }
2771 
2772     // Now the capture/pawn halfmove clock:
2773     while (is_space(*s)) { s++; }
2774     if (*s) {
2775         HalfMoveClock = (ushort) atoi(s);
2776     }
2777     while (!is_space(*s)  && *s != 0) { s++; }
2778 
2779     // Finally, the fullmove counter:
2780     while (is_space(*s)) { s++; }
2781     if (*s) {
2782         int i = atoi(s);
2783         if (i >= 1) {
2784             PlyCounter = (i - 1) * 2;
2785         }
2786     }
2787     if (ToMove == BLACK) { PlyCounter++; }
2788     return OK;
2789 }
2790 
2791 
2792 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2793 // Position::PrintFEN():
2794 //      Print the FEN representation of the position.
2795 //      If flags == FEN_COMPACT, only the board and side-to-move fields
2796 //              are printed, in compact form (no slashes between rows).
2797 //      If flags == FEN_BOARD, only the board and side-to-move fields
2798 //              are printed.
2799 //      If flags == FEN_CASTLING_EP, the castling and en passant fields
2800 //              are also printed.
2801 //      If flags == FEN_ALL_FIELDS, all fields are printed including
2802 //              the halfmove clock and ply counter.
2803 //
PrintFEN(char * str,uint flags) const2804 void Position::PrintFEN(char* str, uint flags) const {
2805     ASSERT (str != NULL);
2806     uint emptyRun, iRank, iFyle;
2807     for (iRank = 0; iRank < 8; iRank++) {
2808         const pieceT* pBoard = &(Board[(7 - iRank) * 8]);
2809         emptyRun = 0;
2810         if (iRank > 0  &&  flags > FEN_COMPACT) { *str++ = '/'; }
2811         for (iFyle = 0; iFyle < 8; iFyle++, pBoard++) {
2812             if (*pBoard != EMPTY) {
2813                 if (emptyRun) { *str++ = (byte) emptyRun + '0'; }
2814                 emptyRun = 0;
2815                 *str++ = PIECE_CHAR[*pBoard];
2816             } else {
2817                 emptyRun++;
2818             }
2819         }
2820         if (emptyRun) { *str++ = (byte) emptyRun + '0'; }
2821     }
2822 
2823     if (flags > FEN_COMPACT) { *str++ = ' '; }
2824     *str++ = (ToMove == WHITE ? 'w' : 'b');
2825     *str = 0;
2826 
2827     if (flags >= FEN_CASTLING_EP) {
2828         // Add the castling flags and EP flag as well:
2829         *str++ = ' ';
2830         if (Castling == 0)  {
2831             *str++ = '-';
2832         } else {
2833             if (GetCastling (WHITE, KSIDE))  { *str++ = 'K'; }
2834             if (GetCastling (WHITE, QSIDE))  { *str++ = 'Q'; }
2835             if (GetCastling (BLACK, KSIDE))  { *str++ = 'k'; }
2836             if (GetCastling (BLACK, QSIDE))  { *str++ = 'q'; }
2837         }
2838         *str++ = ' ';
2839 
2840         // Now the EP target square:
2841         if (EPTarget == NULL_SQUARE) {
2842             *str++ = '-';
2843         } else {
2844             *str++ = square_FyleChar (EPTarget);
2845             *str++ = square_RankChar (EPTarget);
2846         }
2847         *str = 0;
2848 
2849         if (flags >= FEN_ALL_FIELDS) {
2850             // Also print the Halfmove and ply counters:
2851             *str++ = ' ';
2852             sprintf (str, "%d %d", HalfMoveClock, (PlyCounter / 2) + 1);
2853         }
2854     }
2855     return;
2856 }
2857 
2858 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2859 // Position::DumpHtmlBoard():
2860 //      Prints the board in a format for use in HTML documents.
2861 //      Assumes that the HTML document will be in a directory that
2862 //      has a subdirectory bitmapsDir with files "bwr.gif", etc.
2863 //      The numeric arguments are the pixel width and height for each
2864 //      square -- if zero, then the bitmaps are not scaled.
2865 
2866 // The following values define the available HTML styles.
2867 // Style 0 has 40x40 non-transparent images in the "bitmaps" directory.
2868 // Style 1 has 36x35 non-transparent images in the "bitmaps2" directory.
2869 
2870 struct htmlStyleT {
2871     const char * dir;  // directory containing images.
2872     uint width;        // width value specified in <img> tag.
2873     uint height;       // height value specified in <img> tag.
2874     bool transparent;  // True if the style uses transparent images,
2875                        // with square colors set by "bgcolor".
2876 };
2877 
2878 void
DumpHtmlBoard(DString * dstr,uint style,const char * dir,bool flip)2879 Position::DumpHtmlBoard (DString * dstr, uint style, const char * dir, bool flip)
2880 {
2881     const uint HTML_DIAG_STYLES = 2;
2882     htmlStyleT hs [HTML_DIAG_STYLES];
2883     hs[0].dir = "bitmaps"; hs[0].width = 40; hs[0].height = 40;
2884     hs[1].dir = "bitmaps2"; hs[1].width = 36; hs[1].height = 35;
2885     if (style >= HTML_DIAG_STYLES) { style = 0; }
2886 
2887     uint width = hs[style].width;
2888     uint height = hs[style].height;
2889     uint iRank, iFyle;
2890     pieceT * pBoard;
2891     if (dir == NULL) { dir = hs[style].dir; }
2892 
2893     dstr->Append ("<br><br><center>\n");
2894     dstr->Append ("<table Border=1 CellSpacing=0 CellPadding=0>\n");
2895     for (iRank = 0; iRank < 8; iRank++) {
2896         dstr->Append ("<tr>\n");
2897         pBoard = &(Board[(7 - iRank) * 8]);
2898         for (iFyle = 0; iFyle < 8; iFyle++, pBoard++) {
2899             pieceT piece = *pBoard;
2900             if (flip) { piece = Board[iRank * 8 + (7 - iFyle)]; }
2901             dstr->Append ("  <td><img border=0 ");
2902             if (width > 0) {
2903                 char temp[40];
2904                 sprintf (temp, "width=%u ", width);
2905                 dstr->Append (temp);
2906             }
2907             if (height > 0) {
2908                 char temp[40];
2909                 sprintf (temp, "height=%u ", height);
2910                 dstr->Append (temp);
2911             }
2912             dstr->Append ("src=\"");
2913             dstr->Append (dir);
2914             dstr->AddChar ('/');
2915             bool lightSq = ((iRank % 2) == (iFyle % 2));
2916             if (lightSq) {
2917                 dstr->AddChar ('w');
2918             } else {
2919                 dstr->AddChar ('b');
2920             }
2921             if (piece == EMPTY) {
2922                 dstr->Append ("sq.gif");
2923             } else {
2924                 colorT c = piece_Color(piece);
2925                 dstr->AddChar (c == WHITE ? 'w' : 'b');
2926                 dstr->AddChar (tolower (PIECE_CHAR[piece]));
2927                 dstr->Append (".gif");
2928             }
2929             dstr->Append ("\" alt=\"");
2930             if (piece == EMPTY) {
2931                 if (! lightSq) { dstr->Append ("::"); }
2932             } else {
2933                 colorT c = piece_Color(piece);
2934                 dstr->AddChar (c == WHITE ? 'W' : 'B');
2935                 dstr->AddChar (toupper (PIECE_CHAR[piece]));
2936             }
2937             dstr->Append ("\"></td>\n");
2938         }
2939         dstr->Append ("</tr>\n");
2940     }
2941     dstr->Append ("</table>\n");
2942     //if (ToMove == WHITE) {
2943     //    dstr->Append ("<br><b>White to move.</b>\n");
2944     //} else {
2945     //    dstr->Append ("<br><b>Black to move.</b>\n");
2946     //}
2947     dstr->Append("</center><br>");
2948 }
2949 
2950 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2951 // Position::DumpLatexBoard():
2952 //      Prints the board in a format used by a chess package that is
2953 //      available for the LaTeX  or TeX typesetting language.
2954 void
DumpLatexBoard(DString * dstr,bool flip)2955 Position::DumpLatexBoard (DString * dstr, bool flip)
2956 {
2957     uint iRank, iFyle;
2958     pieceT * pBoard;
2959     dstr->Append ("\\board{");
2960     for (iRank = 0; iRank < 8; iRank++) {
2961         pBoard = &(Board[(7 - iRank) * 8]);
2962         for (iFyle = 0; iFyle < 8; iFyle++, pBoard++) {
2963             pieceT piece = *pBoard;
2964             if (flip) { piece = Board[iRank * 8 + (7 - iFyle)]; }
2965             if (piece != EMPTY) {
2966                 dstr->AddChar (PIECE_CHAR[piece]);
2967             } else { // put a space or a '*':
2968                 dstr->AddChar (((iRank % 2) == (iFyle % 2)) ? ' ' : '*');
2969             }
2970         }
2971         if (iRank < 7) {
2972             dstr->Append ("}\n {");
2973         } else { dstr->AddChar ('}'); }
2974     }
2975 }
2976 
2977 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2978 // Position::Compare():
2979 //      Compare another position with this one.
2980 //
2981 sint
Compare(Position * p)2982 Position::Compare (Position * p)
2983 {
2984     int i = 32;
2985     byte *p1, *p2;
2986     p1 = Board;
2987     p2 = p->Board;
2988     while (i   &&  *p1 == *p2) {
2989         i--;  p1++;  p2++;
2990     }
2991     if (p1 < p2) { return -1; }
2992     if (p1 > p2) { return 1; }
2993     return (ToMove - p->GetToMove());
2994 }
2995 
2996 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2997 // Position::GetSquares
2998 //    Adds to the provided square list all squares containing the specified
2999 //    piece, and return the number of pieces of that type on the board.
3000 uint
GetSquares(pieceT piece,SquareList * sqlist)3001 Position::GetSquares (pieceT piece, SquareList * sqlist)
3002 {
3003     colorT color = piece_Color(piece);
3004     squareT * squares = GetList(color);
3005     uint npieces = GetCount(color);
3006     for (uint i=0; i < npieces; i++) {
3007         squareT sq = squares[i];
3008         pieceT p = Board[sq];
3009         if (p == piece) { sqlist->Add (sq); }
3010     }
3011     return Material[piece];
3012 }
3013 
3014 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3015 // Position::Random
3016 //    Given a string such as "KRPKR" or "KRP-kr", sets up a
3017 //    random position with that material configuration.
3018 inline squareT
randomSquare(void)3019 randomSquare (void) { return rand() % 64; }
3020 
3021 inline squareT
randomPawnSquare(void)3022 randomPawnSquare (void) { return (rand() % 48) + A2; }
3023 
3024 errorT
Random(const char * material)3025 Position::Random (const char * material)
3026 {
3027     pieceT pieces [32];         // List of pieces excluding kings
3028     uint nPieces[2] = {0, 0};   // Number of pieces per side excluding kings.
3029     uint total = 0;             // Total number of pieces excluding kings.
3030 
3031     colorT side = WHITE;
3032 
3033     // The material string must start with a king:
3034     if (toupper(*material) != 'K') { return ERROR_Corrupt; }
3035     material++;
3036 
3037     // Read the material string:
3038     while (1) {
3039         char ch = toupper(*material);
3040         if (ch == 0) { break; }
3041         switch (ch) {
3042         case 'K':
3043             if (side == BLACK) { return ERROR_Corrupt; } // Seen third king!
3044             side = BLACK;
3045             break;
3046         case 'Q':  case 'R':  case 'B':  case 'N':  case 'P':
3047             if (nPieces[side] >= 15) { return ERROR_Corrupt; }
3048             nPieces[side]++;
3049             if (ch == 'P') {
3050                 pieces[total] = piece_Make (side, PAWN);
3051             } else {
3052                 pieces[total] = piece_Make (side, piece_FromChar(ch));
3053             }
3054             total++;
3055             break;
3056         case ' ':  case '-':  case '.':  case ',':  case ':':
3057             // Ignore spaces, commas, etc:
3058             break;
3059         default:
3060             return ERROR_Corrupt;
3061         }
3062         material++;
3063     }
3064     if (side != BLACK) { return ERROR_Corrupt; }  // Never saw Black king!
3065 
3066     // Generate two non-adjacent king squares:
3067     squareT wk = randomSquare();
3068     squareT bk = randomSquare();
3069     while (wk == bk  ||  square_Adjacent (wk, bk)) { bk = randomSquare(); }
3070 
3071     // Now add all other pieces to empty squares, looping until a legal
3072     // position is found:
3073     while (1) {
3074         Clear();
3075         ToMove = (rand() % 2) ? WHITE : BLACK;
3076         AddPiece (WK, wk);
3077         AddPiece (BK, bk);
3078 
3079         for (uint i=0; i < total; i++) {
3080             squareT sq;
3081             pieceT p = pieces[i];
3082             bool isPawn = (piece_Type(p) == PAWN);
3083             while (1) {
3084                 sq = isPawn ? randomPawnSquare() : randomSquare();
3085                 if (Board[sq] == EMPTY) { break; }
3086             }
3087             // Add this piece on the random empty square:
3088             AddPiece (p, sq);
3089         }
3090         // Generated a random position with kings not adjacent and
3091         // every piece on its own square. We can stop at this
3092         // attempt if the enemy king is not in check:
3093         squareT enemyKing = (ToMove == WHITE) ? bk : wk;
3094         if (CalcAttacks (ToMove, enemyKing, NULL) == 0) { break; }
3095     }
3096     return OK;
3097 }
3098 
3099 //////////////////////////////////////////////////////////////////////
3100 //  EOF: position.cpp
3101 //////////////////////////////////////////////////////////////////////
3102