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