Line data Source code
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
34 13704453 : Position::AddHash (pieceT p, squareT sq)
35 : {
36 13704453 : HASH (Hash, p, sq);
37 13704453 : if (piece_Type(p) == PAWN) {
38 1873213 : HASH (PawnHash, p, sq);
39 : }
40 13704125 : }
41 :
42 : inline void
43 14065107 : Position::UnHash (pieceT p, squareT sq)
44 : {
45 14065107 : UNHASH (Hash, p, sq);
46 14065107 : if (piece_Type(p) == PAWN) {
47 2071957 : UNHASH (PawnHash, p, sq);
48 : }
49 14064710 : }
50 :
51 : inline void
52 13704453 : Position::AddToBoard (pieceT p, squareT sq)
53 : {
54 13704453 : ASSERT (Board[sq] == EMPTY);
55 13704453 : Board[sq] = p;
56 13704453 : NumOnRank[p][square_Rank(sq)]++;
57 13704453 : NumOnFyle[p][square_Fyle(sq)]++;
58 13703842 : NumOnLeftDiag[p][square_LeftDiag(sq)]++;
59 13704214 : NumOnRightDiag[p][square_RightDiag(sq)]++;
60 13704453 : NumOnSquareColor[p][square_Color(sq)]++;
61 13704348 : AddHash (p, sq);
62 13704372 : }
63 :
64 : inline void
65 14065107 : Position::RemoveFromBoard (pieceT p, squareT sq)
66 : {
67 14065107 : ASSERT (Board[sq] == p);
68 14065107 : Board[sq] = EMPTY;
69 14065107 : NumOnRank[p][square_Rank(sq)]--;
70 14064745 : NumOnFyle[p][square_Fyle(sq)]--;
71 14065107 : NumOnLeftDiag[p][square_LeftDiag(sq)]--;
72 14063654 : NumOnRightDiag[p][square_RightDiag(sq)]--;
73 14064716 : NumOnSquareColor[p][square_Color(sq)]--;
74 14064413 : UnHash (p, sq);
75 14065107 : }
76 :
77 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
78 : // initHashValues:
79 : // Initialises the table of Zobrist hash values.
80 2150 : static void initHashValues (void)
81 : {
82 : // Ensure we set up the hash values only once:
83 : static int firstCall = 1;
84 2150 : if (! firstCall) { return; }
85 1 : firstCall = 0;
86 :
87 :
88 : // First, set all values to 0:
89 : uint sq;
90 17 : for (uint p = 0; p < 16; p++) {
91 16 : 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 1 : const unsigned int * hash = goodHashValues;
97 1 : for (sq=A1; sq <= H8; sq++) { hashVal[WK][sq] = *hash; hash++; }
98 1 : for (sq=A1; sq <= H8; sq++) { hashVal[WQ][sq] = *hash; hash++; }
99 1 : for (sq=A1; sq <= H8; sq++) { hashVal[WR][sq] = *hash; hash++; }
100 1 : for (sq=A1; sq <= H8; sq++) { hashVal[WB][sq] = *hash; hash++; }
101 1 : for (sq=A1; sq <= H8; sq++) { hashVal[WN][sq] = *hash; hash++; }
102 1 : for (sq=A1; sq <= H8; sq++) { hashVal[WP][sq] = *hash; hash++; }
103 1 : for (sq=A1; sq <= H8; sq++) { hashVal[BK][sq] = *hash; hash++; }
104 1 : for (sq=A1; sq <= H8; sq++) { hashVal[BQ][sq] = *hash; hash++; }
105 1 : for (sq=A1; sq <= H8; sq++) { hashVal[BR][sq] = *hash; hash++; }
106 1 : for (sq=A1; sq <= H8; sq++) { hashVal[BB][sq] = *hash; hash++; }
107 1 : for (sq=A1; sq <= H8; sq++) { hashVal[BN][sq] = *hash; hash++; }
108 1 : for (sq=A1; sq <= H8; sq++) { hashVal[BP][sq] = *hash; hash++; }
109 :
110 : // Compute the hash values for the standard starting position:
111 1 : uint h = 0;
112 : // First the pawns:
113 1 : HASH (h,WP,A2); HASH (h,WP,B2); HASH (h,WP,C2); HASH (h,WP,D2);
114 1 : HASH (h,WP,E2); HASH (h,WP,F2); HASH (h,WP,G2); HASH (h,WP,H2);
115 1 : HASH (h,BP,A7); HASH (h,BP,B7); HASH (h,BP,C7); HASH (h,BP,D7);
116 1 : HASH (h,BP,E7); HASH (h,BP,F7); HASH (h,BP,G7); HASH (h,BP,H7);
117 1 : stdStartPawnHash = h;
118 : // Now the nonpawns:
119 1 : HASH (h,WR,A1); HASH (h,WN,B1); HASH (h,WB,C1); HASH (h,WQ,D1);
120 1 : HASH (h,WK,E1); HASH (h,WB,F1); HASH (h,WN,G1); HASH (h,WR,H1);
121 1 : HASH (h,BR,A8); HASH (h,BN,B8); HASH (h,BB,C8); HASH (h,BQ,D8);
122 1 : HASH (h,BK,E8); HASH (h,BB,F8); HASH (h,BN,G8); HASH (h,BR,H8);
123 1 : 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 : {
133 1 : sqDir_Init() {
134 : // Initialise the sqDir[][] array of directions between every pair
135 : // of squares.
136 : squareT i, j;
137 1 : 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 67 : for (i=A1; i <= NS; i++) {
141 4422 : for (j=A1; j <= NS; j++) {
142 4356 : sqDir[i][j] = NULL_DIR;
143 : }
144 : }
145 : // Now fill in the valid directions:
146 65 : for (i=A1; i <= H8; i++) {
147 64 : directionT * dirptr = dirArray;
148 1088 : while (*dirptr != NULL_DIR) {
149 512 : j = square_Move (i, *dirptr);
150 3424 : while (j != NS) {
151 1456 : sqDir[i][j] = *dirptr;
152 1456 : j = square_Move (j, *dirptr);
153 : }
154 512 : dirptr++;
155 : }
156 : }
157 1 : }
158 1 : } 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
169 288560 : 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 288560 : squareT king = GetKingSquare (ToMove);
177 288560 : squareT friendly = NULL_SQUARE;
178 288560 : squareT x = king;
179 288560 : squareT last = square_Last (king, dir);
180 288560 : int delta = direction_Delta (dir);
181 :
182 1138462 : while (x != last) {
183 578345 : x += delta;
184 578345 : pieceT p = Board[x];
185 578345 : if (p == EMPTY) {
186 : // Empty square, so keep searching.
187 252902 : } else if (piece_Color_NotEmpty(p) == ToMove) {
188 : // Found a friendly piece.
189 116983 : if (friendly == NULL_SQUARE) {
190 : // Found first friendly in this direction
191 99508 : friendly = x;
192 : } else {
193 : // Found second friendly in this direction, so stop.
194 17475 : return;
195 : }
196 : } else {
197 : // Found an enemy piece
198 135919 : if (friendly != NULL_SQUARE) {
199 : // Potential pin:
200 63146 : pieceT ptype = piece_Type(p);
201 63146 : if (ptype == QUEEN || ptype == attacker) {
202 39367 : Pinned[ListPos[friendly]] = dir;
203 : }
204 : }
205 135919 : return; // found an enemy piece, so end search
206 : }
207 : }
208 135166 : return;
209 : }
210 :
211 :
212 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
213 : // Position::AddLegalMove():
214 : // Add a legal move to the move list.
215 : //
216 : inline void
217 17970512 : Position::AddLegalMove (MoveList * mlist, squareT from, squareT to, pieceT promo)
218 : {
219 17970512 : 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 17970512 : mlist->emplace_back(from, to, promo, Board[from], Board[to]);
224 17970512 : }
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
233 8395722 : Position::GenSliderMoves (MoveList * mlist, colorT color, squareT fromSq,
234 : directionT dir, SquareSet * sqset, bool capturesOnly)
235 : {
236 8395722 : squareT dest = fromSq;
237 8395722 : squareT last = square_Last (fromSq, dir);
238 8395722 : int delta = direction_Delta (dir);
239 :
240 26603844 : while (dest != last) {
241 12959320 : dest += delta;
242 12959320 : pieceT p = Board[dest];
243 22063381 : if (p == EMPTY) {
244 9104061 : if (! capturesOnly) {
245 9104061 : if (sqset == NULL || sqset->Contains(dest)) {
246 8466436 : AddLegalMove (mlist, fromSq, dest, EMPTY);
247 : }
248 : }
249 9104061 : continue;
250 : }
251 : // We have reached a piece. Add the capture if it is an enemy.
252 3855259 : if (piece_Color_NotEmpty(p) != color) {
253 956587 : if (sqset == NULL || sqset->Contains(dest)) {
254 899031 : AddLegalMove (mlist, fromSq, dest, EMPTY);
255 : }
256 : }
257 3855259 : break;
258 : }
259 8395722 : }
260 :
261 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
262 : // Position::GenKnightMoves():
263 : // Generate knight moves.
264 : // If sqset != NULL, moves must be to a square in sqset.
265 : inline void
266 717862 : Position::GenKnightMoves (MoveList * mlist, colorT c, squareT fromSq,
267 : SquareSet * sqset, bool capturesOnly)
268 : {
269 717862 : const squareT * destPtr = knightAttacks[fromSq];
270 : while (true) {
271 4168661 : squareT dest = *destPtr;
272 4168661 : if (dest == NULL_SQUARE) { break; }
273 3450799 : destPtr++;
274 3450799 : pieceT p = Board[dest];
275 3450799 : if (capturesOnly && p == EMPTY) { continue; }
276 3450799 : if (piece_Color(p) != c) {
277 2770408 : if (sqset == NULL || sqset->Contains(dest)) {
278 2594033 : AddLegalMove (mlist, fromSq, dest, EMPTY);
279 : }
280 : }
281 3450799 : }
282 717862 : }
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
291 740844 : Position::GenCastling (MoveList * mlist)
292 : {
293 740844 : ASSERT (! IsKingInCheck());
294 740844 : squareT from = GetKingSquare(ToMove);
295 740844 : if (from != (ToMove == WHITE ? E1 : E8)) { return; }
296 135657 : squareT enemyKingSq = GetEnemyKingSquare();
297 : squareT target, skip, rookSq;
298 : pieceT rookPiece;
299 :
300 : // Try kingside first
301 :
302 : // Kingside Castling:
303 135657 : if (GetCastling (ToMove, KSIDE)) {
304 66783 : if (ToMove == WHITE) {
305 33464 : target = G1; skip = F1; rookSq = H1; rookPiece = WR;
306 : } else {
307 33319 : target = G8; skip = F8; rookSq = H8; rookPiece = BR;
308 : }
309 158281 : if (Board[target] == EMPTY && Board[skip] == EMPTY
310 7231 : && Board[rookSq] == rookPiece
311 7231 : && CalcNumChecks (target) == 0
312 6964 : && CalcNumChecks (skip) == 0
313 73122 : && ! square_Adjacent (target, enemyKingSq)) {
314 6339 : AddLegalMove (mlist, from, target, EMPTY);
315 : }
316 : }
317 :
318 : // Queenside Castling:
319 135657 : if (GetCastling (ToMove, QSIDE)) {
320 66835 : if (ToMove == WHITE) {
321 32650 : target = C1; skip = D1; rookSq = A1; rookPiece = WR;
322 : } else {
323 34185 : target = C8; skip = D8; rookSq = A8; rookPiece = BR;
324 : }
325 150290 : if (Board[target] == EMPTY && Board[skip] == EMPTY
326 8644 : && Board[rookSq] == rookPiece
327 8644 : && Board[target - 1] == EMPTY // B1 or B8 must be empty too!
328 3633 : && CalcNumChecks (target) == 0
329 3330 : && CalcNumChecks (skip) == 0
330 69780 : && ! square_Adjacent (target, enemyKingSq)) {
331 2945 : 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
342 795750 : Position::GenKingMoves (MoveList * mlist, genMovesT genType, bool castling)
343 : {
344 795750 : squareT kingSq = GetKingSquare();
345 795750 : squareT enemyKingSq = GetEnemyKingSquare();
346 795750 : colorT enemy = color_Flip(ToMove);
347 : const squareT * destPtr;
348 795750 : pieceT king = piece_Make (ToMove, KING);
349 795750 : bool genNonCaptures = (genType & GEN_NON_CAPS);
350 :
351 795750 : ASSERT (Board[kingSq] == king);
352 :
353 795750 : destPtr = kingAttacks[kingSq];
354 10905736 : while (*destPtr != NULL_SQUARE) {
355 : // Try this move and see if it legal:
356 :
357 5054993 : squareT destSq = *destPtr;
358 5054993 : 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 10109986 : if ( (genNonCaptures && Board[destSq] == EMPTY)
363 6045297 : || piece_Color (Board[destSq]) == enemy) {
364 :
365 : // Empty or enemy piece there, so try the move:
366 4199565 : pieceT captured = Board[destSq];
367 4199565 : Board[destSq] = king;
368 4199565 : 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 4199565 : if (! square_Adjacent (destSq, enemyKingSq)) {
373 4084031 : if (CalcNumChecks (destSq) == 0) {
374 3147394 : addThisMove = true;
375 : }
376 : }
377 4199565 : Board[kingSq] = king;
378 4199565 : Board[destSq] = captured;
379 : }
380 5054993 : if (addThisMove) { AddLegalMove (mlist, kingSq, destSq, EMPTY); }
381 5054993 : destPtr++;
382 : }
383 : // Now generate castling moves, if possible:
384 795750 : if (genNonCaptures && castling) { GenCastling (mlist); }
385 795750 : }
386 :
387 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
388 : // Position::AddPromotions():
389 : // Add promotion moves.
390 : // Called by GenPawnMoves() when a pawn can be promoted.
391 : //
392 : inline void
393 26353 : Position::AddPromotions (MoveList * mlist, squareT from, squareT dest)
394 : {
395 26353 : ASSERT (piece_Type (Board[from]) == PAWN);
396 26353 : ASSERT (square_Rank (dest) == RANK_1 || square_Rank (dest) == RANK_8);
397 :
398 26353 : AddLegalMove (mlist, from, dest, QUEEN);
399 26353 : AddLegalMove (mlist, from, dest, ROOK);
400 26353 : AddLegalMove (mlist, from, dest, BISHOP);
401 26353 : AddLegalMove (mlist, from, dest, KNIGHT);
402 26353 : }
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
412 1534 : Position::IsValidEnPassant (squareT from, squareT to)
413 : {
414 1534 : ASSERT (from <= H8 && to <= H8);
415 1534 : ASSERT (to == EPTarget);
416 :
417 : // Check that this en passant capture is legal:
418 1534 : pieceT ownPawn = piece_Make(ToMove, PAWN);
419 1534 : pieceT enemyPawn = piece_Make (color_Flip(ToMove), PAWN);
420 1534 : squareT enemyPawnSq = (ToMove == WHITE) ? to - 8 : to + 8;
421 1534 : ASSERT (Board[from] == ownPawn);
422 1534 : ASSERT (Board[enemyPawnSq] == enemyPawn);
423 1534 : Board[from] = EMPTY;
424 :
425 : // PG
426 1534 : Board[to] = ownPawn;
427 :
428 1534 : Board[enemyPawnSq] = EMPTY;
429 1534 : bool isValid = ! IsKingInCheck();
430 :
431 : //PG
432 1534 : Board[to] = EMPTY;
433 :
434 1534 : Board[from] = ownPawn;
435 1534 : Board[enemyPawnSq] = enemyPawn;
436 1534 : 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
446 2593165 : Position::GenPawnMoves (MoveList * mlist, squareT from,
447 : directionT dir, SquareSet * sqset, genMovesT genType)
448 : {
449 2593165 : bool genNonCaptures = (genType & GEN_NON_CAPS);
450 2593165 : directionT oppdir = direction_Opposite(dir);
451 : directionT forward;
452 : rankT promoRank;
453 : rankT secondRank;
454 2593165 : if (ToMove == WHITE) {
455 1288786 : forward = UP; promoRank = RANK_8; secondRank = RANK_2;
456 : } else {
457 1304379 : forward = DOWN; promoRank = RANK_1; secondRank = RANK_7;
458 : }
459 : squareT dest;
460 :
461 2593165 : ASSERT (Board[from] == piece_Make (ToMove, PAWN));
462 :
463 2593165 : if (genNonCaptures
464 2593165 : && (dir == NULL_DIR || dir == forward || oppdir == forward)) {
465 2580449 : dest = square_Move (from, forward);
466 2580449 : if (Board[dest]==EMPTY && (sqset==NULL || sqset->Contains(dest))) {
467 1624931 : if (square_Rank(dest) == promoRank) {
468 22771 : AddPromotions (mlist, from, dest);
469 : } else {
470 1602160 : AddLegalMove (mlist, from, dest, EMPTY);
471 : }
472 : }
473 2580449 : if (square_Rank(from) == secondRank && Board[dest] == EMPTY) {
474 607788 : dest = square_Move (dest, forward);
475 607788 : if (Board[dest]==EMPTY && (sqset==NULL || sqset->Contains(dest))) {
476 499695 : 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 2593165 : directionT capdir = forward | LEFT;
489 2593165 : if (dir == NULL_DIR || dir == capdir || oppdir == capdir) {
490 2578983 : dest = square_Move (from, capdir);
491 2578983 : if (POSSIBLE_CAPTURE(dest) && (sqset==NULL || sqset->Contains(dest))) {
492 227078 : if (square_Rank(dest) == promoRank) {
493 1707 : AddPromotions (mlist, from, dest);
494 : } else {
495 225371 : AddLegalMove (mlist, from, dest, EMPTY);
496 : }
497 : }
498 : }
499 2593165 : capdir = forward | RIGHT;
500 2593165 : if (dir == NULL_DIR || dir == capdir || oppdir == capdir) {
501 2576896 : dest = square_Move (from, capdir);
502 2576896 : if (POSSIBLE_CAPTURE(dest) && (sqset==NULL || sqset->Contains(dest))) {
503 226848 : if (square_Rank(dest) == promoRank) {
504 1875 : AddPromotions (mlist, from, dest);
505 : } else {
506 224973 : AddLegalMove (mlist, from, dest, EMPTY);
507 : }
508 : }
509 : }
510 2593165 : 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
523 2816696 : Position::GetHPSig (void)
524 : {
525 2816696 : uint hpSig = 0;
526 2816696 : pieceT * b = &(Board[A2]);
527 2816696 : if (*b == WP) { hpSig |= 0x8000; } b++; /* a2 */
528 2816696 : if (*b == WP) { hpSig |= 0x4000; } b++; /* b2 */
529 2816696 : if (*b == WP) { hpSig |= 0x2000; } b++; /* c2 */
530 2816696 : if (*b == WP) { hpSig |= 0x1000; } b++; /* d2 */
531 2816696 : if (*b == WP) { hpSig |= 0x0800; } b++; /* e2 */
532 2816696 : if (*b == WP) { hpSig |= 0x0400; } b++; /* f2 */
533 2816696 : if (*b == WP) { hpSig |= 0x0200; } b++; /* g2 */
534 2816696 : if (*b == WP) { hpSig |= 0x0100; } /* h2 */
535 2816696 : b = &(Board[A7]);
536 2816696 : if (*b == BP) { hpSig |= 0x0080; } b++; /* a7 */
537 2816696 : if (*b == BP) { hpSig |= 0x0040; } b++; /* b7 */
538 2816696 : if (*b == BP) { hpSig |= 0x0020; } b++; /* c7 */
539 2816696 : if (*b == BP) { hpSig |= 0x0010; } b++; /* d7 */
540 2816696 : if (*b == BP) { hpSig |= 0x0008; } b++; /* e7 */
541 2816696 : if (*b == BP) { hpSig |= 0x0004; } b++; /* f7 */
542 2816696 : if (*b == BP) { hpSig |= 0x0002; } b++; /* g7 */
543 2816696 : if (*b == BP) { hpSig |= 0x0001; } /* h7 */
544 2816696 : return hpSig;
545 : }
546 :
547 2150 : Position::Position() {
548 : // Setting up a valid board is left to StdStart() or Clear().
549 2150 : Board [COLOR_SQUARE] = EMPTY;
550 2150 : 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 2150 : initHashValues();
555 2150 : }
556 :
557 :
558 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
559 : // Position::Clear():
560 : // Clear the board and associated structures.
561 : //
562 : void
563 22 : Position::Clear (void)
564 : {
565 : int i;
566 22 : for (i=A1; i <= H8; i++) { Board[i] = EMPTY; }
567 330 : for (i=WK; i <= BP; i++) {
568 308 : Material[i] = 0;
569 2772 : for (uint j=0; j < 8; j++) {
570 2464 : NumOnRank[i][j] = NumOnFyle[i][j] = 0;
571 : }
572 4928 : for (uint d=0; d < 15; d++) {
573 4620 : NumOnLeftDiag[i][d] = NumOnRightDiag[i][d] = 0;
574 : }
575 308 : NumOnSquareColor[i][WHITE] = NumOnSquareColor[i][BLACK] = 0;
576 : }
577 22 : Count[WHITE] = Count[BLACK] = 0;
578 22 : EPTarget = NULL_SQUARE;
579 22 : Castling = 0;
580 22 : Board [NULL_SQUARE] = END_OF_BOARD;
581 22 : PlyCounter = 0;
582 22 : HalfMoveClock = 0;
583 22 : Hash = 0;
584 22 : PawnHash = 0;
585 22 : 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 : //
594 19867 : const Position& Position::getStdStart()
595 : {
596 19867 : static Position startPositionTemplate;
597 : static bool init = true;
598 :
599 19869 : if (init){
600 1 : init = false;
601 1 : Position* p = &startPositionTemplate;
602 1 : p->Clear();
603 1 : p->Material[WK] = p->Material[BK] = 1;
604 1 : p->Material[WQ] = p->Material[BQ] = 1;
605 1 : p->Material[WR] = p->Material[BR] = 2;
606 1 : p->Material[WB] = p->Material[BB] = 2;
607 1 : p->Material[WN] = p->Material[BN] = 2;
608 1 : p->Material[WP] = p->Material[BP] = 8;
609 1 : p->Count[WHITE] = p->Count[BLACK] = 16;
610 :
611 1 : p->AddToBoard(WK, E1); p->List[WHITE][0] = E1; p->ListPos[E1] = 0;
612 1 : p->AddToBoard(BK, E8); p->List[BLACK][0] = E8; p->ListPos[E8] = 0;
613 1 : p->AddToBoard(WR, A1); p->List[WHITE][1] = A1; p->ListPos[A1] = 1;
614 1 : p->AddToBoard(BR, A8); p->List[BLACK][1] = A8; p->ListPos[A8] = 1;
615 1 : p->AddToBoard(WN, B1); p->List[WHITE][2] = B1; p->ListPos[B1] = 2;
616 1 : p->AddToBoard(BN, B8); p->List[BLACK][2] = B8; p->ListPos[B8] = 2;
617 1 : p->AddToBoard(WB, C1); p->List[WHITE][3] = C1; p->ListPos[C1] = 3;
618 1 : p->AddToBoard(BB, C8); p->List[BLACK][3] = C8; p->ListPos[C8] = 3;
619 1 : p->AddToBoard(WQ, D1); p->List[WHITE][4] = D1; p->ListPos[D1] = 4;
620 1 : p->AddToBoard(BQ, D8); p->List[BLACK][4] = D8; p->ListPos[D8] = 4;
621 1 : p->AddToBoard(WB, F1); p->List[WHITE][5] = F1; p->ListPos[F1] = 5;
622 1 : p->AddToBoard(BB, F8); p->List[BLACK][5] = F8; p->ListPos[F8] = 5;
623 1 : p->AddToBoard(WN, G1); p->List[WHITE][6] = G1; p->ListPos[G1] = 6;
624 1 : p->AddToBoard(BN, G8); p->List[BLACK][6] = G8; p->ListPos[G8] = 6;
625 1 : p->AddToBoard(WR, H1); p->List[WHITE][7] = H1; p->ListPos[H1] = 7;
626 1 : p->AddToBoard(BR, H8); p->List[BLACK][7] = H8; p->ListPos[H8] = 7;
627 :
628 9 : for (uint i=0; i < 8; i++) {
629 8 : p->AddToBoard(WP, A2+i); p->List[WHITE][i+8] = A2+i; p->ListPos[A2+i] = i+8;
630 8 : p->AddToBoard(BP, A7+i); p->List[BLACK][i+8] = A7+i; p->ListPos[A7+i] = i+8;
631 : }
632 :
633 1 : p->Castling = 0;
634 1 : p->SetCastling (WHITE, QSIDE, true); p->SetCastling (WHITE, KSIDE, true);
635 1 : p->SetCastling (BLACK, QSIDE, true); p->SetCastling (BLACK, KSIDE, true);
636 1 : p->EPTarget = NULL_SQUARE;
637 1 : p->ToMove = WHITE;
638 1 : p->PlyCounter = 0;
639 1 : p->HalfMoveClock = 0;
640 1 : p->Board [NULL_SQUARE] = END_OF_BOARD;
641 1 : p->Hash = stdStartHash;
642 1 : p->PawnHash = stdStartPawnHash;
643 : }
644 19869 : return startPositionTemplate;
645 : }
646 :
647 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
648 : // Position::IsStdStart
649 : // Returns true if the position is the standard starting position.
650 0 : bool Position::IsStdStart() const {
651 0 : if (ToMove != WHITE
652 0 : || Hash != stdStartHash || PawnHash != stdStartPawnHash
653 0 : || GetCount(WHITE) != 16 || GetCount(BLACK) != 16
654 0 : || RankCount(WP,RANK_2) != 8 || RankCount(BP,RANK_7) != 8
655 0 : || RankCount(WN,RANK_1) != 2 || RankCount(BN,RANK_8) != 2
656 0 : || !GetCastling(WHITE,KSIDE) || !GetCastling(WHITE,QSIDE)
657 0 : || !GetCastling(BLACK,KSIDE) || !GetCastling(BLACK,QSIDE)) {
658 0 : return false;
659 : }
660 0 : 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
670 362 : Position::AddPiece (pieceT p, squareT sq)
671 : {
672 362 : ASSERT (p != EMPTY);
673 362 : colorT c = piece_Color(p);
674 362 : if ((c != WHITE && c != BLACK) || Count[c] > 15)
675 0 : return ERROR_PieceCount;
676 :
677 362 : if (piece_Type(p) == KING) {
678 : // Check there is not already a King:
679 42 : 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 42 : if (Count[c] > 0) {
684 31 : squareT oldsq = List[c][0];
685 31 : List[c][Count[c]] = oldsq;
686 31 : ListPos[oldsq] = Count[c];
687 : }
688 42 : List[c][0] = sq;
689 42 : ListPos[sq] = 0;
690 : } else {
691 320 : ListPos[sq] = Count[c];
692 320 : List[c][Count[c]] = sq;
693 : }
694 362 : Count[c]++;
695 362 : Material[p]++;
696 362 : AddToBoard (p, sq);
697 362 : 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
711 795750 : Position::CalcPins (void)
712 : {
713 795750 : Pinned[ 0] = Pinned[ 1] = Pinned[ 2] = Pinned[ 3] = Pinned[ 4] =
714 795750 : Pinned[ 5] = Pinned[ 6] = Pinned[ 7] = Pinned[ 8] = Pinned[ 9] =
715 795750 : Pinned[10] = Pinned[11] = Pinned[12] = Pinned[13] = Pinned[14] =
716 795750 : Pinned[15] = NULL_DIR;
717 :
718 795750 : squareT kingSq = GetKingSquare (ToMove);
719 795750 : colorT enemy = color_Flip (ToMove);
720 795750 : pieceT enemyQueen = piece_Make (enemy, QUEEN);
721 795750 : pieceT enemyRook = piece_Make (enemy, ROOK);
722 795750 : pieceT enemyBishop = piece_Make (enemy, BISHOP);
723 :
724 : // Pins and checks from Bishops/Queens/Rooks:
725 795750 : fyleT fyle = square_Fyle (kingSq);
726 795750 : if (FyleCount(enemyQueen,fyle) + FyleCount(enemyRook,fyle) > 0) {
727 69703 : CalcPinsDir (UP, ROOK);
728 69703 : CalcPinsDir (DOWN, ROOK);
729 : }
730 795750 : rankT rank = square_Rank (kingSq);
731 795750 : if (RankCount(enemyQueen,rank) + RankCount(enemyRook,rank) > 0) {
732 24675 : CalcPinsDir (LEFT, ROOK);
733 24675 : CalcPinsDir (RIGHT, ROOK);
734 : }
735 795750 : leftDiagT ld = square_LeftDiag (kingSq);
736 795750 : if (LeftDiagCount(enemyQueen,ld) + LeftDiagCount(enemyBishop,ld) > 0) {
737 25449 : CalcPinsDir (UP_LEFT, BISHOP);
738 25449 : CalcPinsDir (DOWN_RIGHT, BISHOP);
739 : }
740 795750 : rightDiagT rd = square_RightDiag (kingSq);
741 795750 : if (RightDiagCount(enemyQueen,rd) + RightDiagCount(enemyBishop,rd) > 0) {
742 24453 : CalcPinsDir (UP_RIGHT, BISHOP);
743 24453 : CalcPinsDir (DOWN_LEFT, BISHOP);
744 : }
745 795750 : }
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
752 2548396 : Position::GenPieceMoves (MoveList * mlist, squareT fromSq,
753 : SquareSet * sqset, bool capturesOnly)
754 : {
755 2548396 : colorT c = ToMove;
756 2548396 : pieceT p = Board[fromSq];
757 2548396 : pieceT ptype = piece_Type(p);
758 2548396 : ASSERT (p != EMPTY && ptype != KING && ptype != PAWN);
759 :
760 2548396 : if (ptype == KNIGHT) {
761 717862 : GenKnightMoves (mlist, c, fromSq, sqset, capturesOnly);
762 717862 : return;
763 : }
764 1830534 : if (ptype != BISHOP) {
765 1079299 : GenSliderMoves (mlist, c, fromSq, UP, sqset, capturesOnly);
766 1079299 : GenSliderMoves (mlist, c, fromSq, DOWN, sqset, capturesOnly);
767 1079299 : GenSliderMoves (mlist, c, fromSq, LEFT, sqset, capturesOnly);
768 1079299 : GenSliderMoves (mlist, c, fromSq, RIGHT, sqset, capturesOnly);
769 : }
770 1830534 : if (ptype != ROOK) {
771 1016271 : GenSliderMoves (mlist, c, fromSq, UP_LEFT, sqset, capturesOnly);
772 1016271 : GenSliderMoves (mlist, c, fromSq, DOWN_LEFT, sqset, capturesOnly);
773 1016271 : GenSliderMoves (mlist, c, fromSq, UP_RIGHT, sqset, capturesOnly);
774 1016271 : 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
784 795750 : Position::GenerateMoves (MoveList* mlist, pieceT pieceType,
785 : genMovesT genType, bool maybeInCheck)
786 : {
787 795750 : ASSERT(mlist != NULL);
788 :
789 795750 : bool genNonCaptures = (genType & GEN_NON_CAPS);
790 795750 : bool capturesOnly = !genNonCaptures;
791 :
792 795750 : uint mask = 0;
793 795750 : if (pieceType != EMPTY) {
794 0 : mask = 1 << pieceType;
795 : } else {
796 795750 : 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 795750 : mlist->Clear();
802 :
803 : // Compute which pieces of the side to move are pinned to the king:
804 795750 : 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 795750 : uint numChecks = 0;
811 795750 : if (maybeInCheck) {
812 795750 : SquareList checkSquares;
813 795750 : numChecks = CalcNumChecks (GetKingSquare(ToMove), &checkSquares);
814 795750 : if (numChecks > 0) {
815 : // The side to move IS in check:
816 54906 : GenCheckEvasions (mlist, pieceType, genType, &checkSquares);
817 54906 : 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 740844 : uint npieces = Count[ToMove];
825 5582753 : for (uint x = 1; x < npieces; x++) {
826 4841909 : squareT sq = List[ToMove][x];
827 4841909 : pieceT p = Board[sq];
828 4841909 : pieceT ptype = piece_Type(p);
829 4841909 : if (! (mask & (1 << ptype))) { continue; }
830 4841909 : 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 4841909 : if (pinned != NULL_DIR) { // piece x is pinned to king
836 37492 : if (ptype == PAWN) {
837 20602 : GenPawnMoves (mlist, sq, pinned, NULL, genType);
838 16890 : } 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 11512 : if (ptype == QUEEN
844 9641 : || (ptype == ROOK && !direction_IsDiagonal(pinned))
845 18570 : || (ptype == BISHOP && direction_IsDiagonal(pinned))) {
846 6721 : GenSliderMoves (mlist, ToMove, sq, pinned, NULL, capturesOnly);
847 6721 : GenSliderMoves (mlist, ToMove, sq, dirOpposite[pinned],
848 : NULL, capturesOnly);
849 : }
850 : }
851 : } else {
852 : // This piece is free to move anywhere
853 4804417 : if (ptype == PAWN) {
854 2415826 : GenPawnMoves (mlist, sq, NULL_DIR, NULL, genType);
855 : } else {
856 : // Knight/Queen/Bishop/Rook:
857 2388591 : GenPieceMoves (mlist, sq, NULL, capturesOnly);
858 : }
859 : }
860 : }
861 :
862 : // Lastly, king moves...
863 740844 : if (mask & (1 << KING)) {
864 740844 : bool castling = !numChecks;
865 740844 : 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
874 0 : Position::IsLegalMove (simpleMoveT * sm) {
875 0 : squareT from = sm->from;
876 0 : squareT to = sm->to;
877 0 : if (from > H8 || to > H8) { return false; }
878 0 : if (from == to) { return false; }
879 0 : pieceT mover = Board[from];
880 0 : pieceT captured = Board[to];
881 0 : if (piece_Color(mover) != ToMove) { return false; }
882 0 : if (piece_Color(captured) == ToMove) { return false; }
883 0 : if (sm->movingPiece != mover) { return false; }
884 0 : mover = piece_Type (mover);
885 0 : if (sm->promote != EMPTY && mover != PAWN) { return false; }
886 :
887 0 : if (mover == PAWN) {
888 0 : rankT rfrom = square_Rank(from);
889 0 : rankT rto = square_Rank(to);
890 0 : if (ToMove == BLACK) { rfrom = RANK_8 - rfrom; rto = RANK_8 - rto; }
891 0 : int rdiff = (int)rto - (int)rfrom;
892 0 : int fdiff = (int)square_Fyle(to) - (int)square_Fyle(from);
893 0 : if (rdiff < 1 || rdiff > 2) { return false; }
894 0 : if (fdiff < -1 || fdiff > 1) { return false; }
895 0 : if (fdiff == 0) { // Pawn push:
896 0 : if (captured != EMPTY) { return false; }
897 0 : if (rdiff == 2) { // Two-square push:
898 0 : if (rfrom != RANK_2) { return false; }
899 : // Make sure the square in between is empty:
900 0 : squareT midsquare = from + ((to - from) / 2);
901 0 : if (Board[midsquare] != EMPTY) { return false; }
902 : }
903 : } else { // Pawn capture:
904 0 : if (rdiff != 1) { return false; }
905 0 : if (captured == EMPTY) {
906 : // It must be en passant, or illegal
907 0 : if (to != EPTarget) { return false; }
908 : }
909 : }
910 : // Check the promotion piece:
911 0 : if (rto == RANK_8) {
912 0 : pieceT p = sm->promote;
913 0 : if (p != QUEEN && p != ROOK && p != BISHOP && p != KNIGHT) {
914 0 : return false;
915 : }
916 : } else {
917 0 : if (sm->promote != EMPTY) { return false; }
918 : }
919 :
920 0 : } else if (piece_IsSlider(mover)) {
921 : // Make sure the direction is valid:
922 0 : directionT dir = sqDir[from][to];
923 0 : if (dir == NULL_DIR) { return false; }
924 0 : if (mover == ROOK && direction_IsDiagonal(dir)) { return false; }
925 0 : if (mover == BISHOP && !direction_IsDiagonal(dir)) { return false; }
926 0 : int delta = direction_Delta (dir);
927 : // Make sure all the in-between squares are empty:
928 0 : squareT dest = from + delta;
929 0 : while (dest != to) {
930 0 : if (Board[dest] != EMPTY) { return false; }
931 0 : dest += delta;
932 : }
933 :
934 0 : } else if (mover == KNIGHT) {
935 0 : if (! square_IsKnightHop (from, to)) { return false; }
936 :
937 : } else /* (mover == KING) */ {
938 0 : colorT enemy = color_Flip(ToMove);
939 0 : if (square_Adjacent (to, GetKingSquare(enemy))) { return false; }
940 0 : if (! square_Adjacent (from, to)) {
941 : // The move must be castling, or illegal.
942 0 : if (IsKingInCheck()) { return false; }
943 0 : MoveList mlist;
944 0 : GenCastling (&mlist);
945 0 : 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 0 : squareT kingSq = (mover == KING) ? to : GetKingSquare(ToMove);
951 0 : colorT enemy = color_Flip(ToMove);
952 0 : DoSimpleMove (sm);
953 0 : uint nchecks = CalcAttacks (enemy, kingSq, NULL);
954 0 : UndoSimpleMove (sm);
955 0 : 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
964 196723 : Position::MatchPawnMove (MoveList * mlist, fyleT fromFyle, squareT to, pieceT promote)
965 : {
966 196723 : ASSERT(mlist != NULL);
967 196723 : pieceT promote2 = promote;
968 :
969 196723 : mlist->Clear();
970 :
971 196723 : sint diff = (int)square_Fyle(to) - (int)fromFyle;
972 196723 : if (diff < -1 || diff > 1) { return ERROR_InvalidMove; }
973 : pieceT pawn;
974 196723 : rankT toRank = square_Rank(to);
975 196723 : fyleT toFyle = square_Fyle(to);
976 196723 : 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 196723 : squareT from, backup = NS;
982 :
983 196723 : if (ToMove == WHITE) {
984 96845 : pawn = WP;
985 96845 : if (toRank < RANK_3) { return ERROR_InvalidMove; }
986 96845 : from = square_Make(fromFyle, toRank - 1);
987 96845 : if (toRank == RANK_4 && fromFyle == toFyle) { backup = to - 16; }
988 : } else {
989 99878 : pawn = BP;
990 99878 : if (toRank > RANK_6) { return ERROR_InvalidMove; }
991 99878 : from = square_Make(fromFyle, toRank + 1);
992 99878 : if (toRank == RANK_5 && fromFyle == toFyle) { backup = to + 16; }
993 : }
994 :
995 : // See if the promotion piece is valid:
996 :
997 196723 : if (toRank == promoteRank) {
998 : // if (promote == EMPTY) { return ERROR_InvalidMove; }
999 9886 : if (promote == EMPTY) {
1000 : // autopromote to queen
1001 0 : promote2 = (ToMove == WHITE ? WQ : BQ);
1002 : }
1003 : } else {
1004 186837 : if (promote != EMPTY) { return ERROR_InvalidMove; }
1005 : }
1006 :
1007 196723 : if (Board[from] != pawn) {
1008 : // No match; but it could be a foward-two-squares move:
1009 35154 : if (backup == NS || Board[from] != EMPTY || Board[backup] != pawn) {
1010 : // A forward-two-squares move is impossible.
1011 0 : return ERROR_InvalidMove;
1012 : }
1013 35154 : 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 196723 : uint legal = 0;
1020 196723 : if (fromFyle == toFyle) {
1021 : // Not a capture:
1022 :
1023 164068 : if (Board[to] != EMPTY) { return ERROR_InvalidMove; }
1024 164068 : Board[to] = pawn; Board[from] = EMPTY;
1025 164068 : if (CalcNumChecks (GetKingSquare()) == 0) {
1026 164068 : legal = 1;
1027 : }
1028 164068 : Board[to] = EMPTY; Board[from] = pawn;
1029 :
1030 : } else {
1031 : // It is a capture -- is it legal?
1032 :
1033 32655 : pieceT captured = Board[to];
1034 32655 : if (captured == EMPTY) {
1035 : // Must be an en passant or illegal move.
1036 72 : if (to != EPTarget) { return ERROR_InvalidMove; }
1037 72 : squareT epSquare = square_Make(toFyle, square_Rank(from));
1038 :
1039 72 : pieceT enemyPawn = piece_Make (color_Flip(ToMove), PAWN);
1040 : // If following assert fails, eptarget was corrupt
1041 72 : ASSERT (Board[epSquare] == enemyPawn);
1042 :
1043 72 : Board[to] = pawn; Board[from] = EMPTY;
1044 72 : Board[epSquare] = EMPTY;
1045 72 : Material[enemyPawn] --;
1046 72 : if (CalcNumChecks (GetKingSquare()) == 0) { legal = 1; }
1047 72 : Board[epSquare] = enemyPawn;
1048 72 : Board[to] = EMPTY;
1049 72 : Board[from] = pawn;
1050 72 : Material[enemyPawn]++;
1051 :
1052 : } else {
1053 32583 : if (piece_Color(captured) == ToMove) {
1054 : // Capturing a friendly!
1055 0 : return ERROR_InvalidMove;
1056 : } else {
1057 : // A regular capture. See if it leaves King in check:
1058 32583 : Board[to] = pawn; Board[from] = EMPTY;
1059 32583 : Material[captured]--;
1060 32583 : if (CalcNumChecks (GetKingSquare()) == 0) {
1061 32583 : legal = 1;
1062 : }
1063 32583 : Material[captured]++;
1064 32583 : Board[to] = captured; Board[from] = pawn;
1065 : }
1066 : }
1067 : }
1068 :
1069 196723 : if (legal == 1) {
1070 196723 : AddLegalMove (mlist, from, to, promote2);
1071 196723 : return OK;
1072 : }
1073 0 : 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
1082 54906 : Position::GenCheckEvasions (MoveList * mlist, pieceT mask, genMovesT genType,
1083 : SquareList * checkSquares)
1084 : {
1085 54906 : ASSERT(mlist != NULL);
1086 54906 : uint numChecks = checkSquares->Size();
1087 :
1088 : // Assert that king IS actually in check:
1089 54906 : ASSERT (numChecks > 0);
1090 :
1091 54906 : bool genNonCaptures = (genType & GEN_NON_CAPS);
1092 54906 : bool capturesOnly = !genNonCaptures;
1093 54906 : mlist->Clear();
1094 :
1095 54906 : squareT king = GetKingSquare (ToMove);
1096 :
1097 : // if it's double check, we can ONLY move the king
1098 54906 : 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 54719 : squareT attackSq = checkSquares->Get(0);
1105 54719 : directionT dir = sqDir[king][attackSq];
1106 54719 : SquareSet targets; // Set of blocking/capturing squares.
1107 54719 : targets.Add(attackSq);
1108 :
1109 : // Now add squares we can might be able to block on.
1110 54719 : if (dir != NULL_DIR) {
1111 45796 : squareT sq = square_Move (king, dir);
1112 160900 : while (sq != attackSq) {
1113 57552 : if (Board[sq] == EMPTY) { targets.Add(sq); }
1114 57552 : 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 54719 : uint numPieces = Count[ToMove];
1123 373083 : for (uint p2 = 1; p2 < numPieces; p2++) {
1124 318364 : squareT from = List[ToMove][p2];
1125 318364 : pieceT p2piece = Board[from];
1126 318364 : if (Pinned[p2] != NULL_DIR) { continue; }
1127 316493 : if (mask == EMPTY || mask == piece_Type(p2piece)) {
1128 316493 : if (piece_Type(p2piece) == PAWN) {
1129 156688 : 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 156688 : if (EPTarget != NULL_SQUARE) {
1136 63 : squareT pawnSq = (ToMove == WHITE ? EPTarget - 8 : EPTarget + 8);
1137 63 : if (pawnSq == attackSq) {
1138 49 : SquareSet epset;
1139 49 : epset.Add(EPTarget);
1140 49 : GenPawnMoves (mlist, from, NULL_DIR, &epset, genType);
1141 : }
1142 : }
1143 : } else {
1144 159805 : 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 54906 : if (mask == EMPTY || mask == KING) { GenKingMoves(mlist, genType, false); }
1152 :
1153 54906 : 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
1161 0 : Position::TreeCalcAttacks(colorT side, squareT target)
1162 : {
1163 0 : int maxScore = -2;
1164 0 : uint moveCount = 0, zeroCount = 0;
1165 0 : MoveList moveList;
1166 0 : GenerateCaptures(&moveList);
1167 0 : for (uint i=0; i < moveList.Size(); i++) {
1168 0 : simpleMoveT *smPtr = moveList.Get(i);
1169 0 : if (smPtr->to == target) {
1170 0 : if (piece_IsKing(Board[target])) return -1;
1171 0 : moveCount++;
1172 0 : DoSimpleMove(smPtr);
1173 0 : int score = TreeCalcAttacks(color_Flip(side), target);
1174 0 : UndoSimpleMove(smPtr);
1175 0 : if (!score && ++zeroCount > 1) return -2;
1176 0 : if (score > maxScore) maxScore = score;
1177 : }
1178 : }
1179 :
1180 0 : if (!moveCount) return 0;
1181 0 : if (!maxScore) return -1;
1182 0 : 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
1198 6557057 : 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 6557057 : SquareList fromSqs;
1205 6557057 : if (fromSquares == NULL) { fromSquares = &fromSqs; }
1206 :
1207 6557057 : 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 6557057 : if (side == WHITE) {
1213 3275283 : king = WK; queen = WQ; rook = WR; bishop = WB; knight = WN;
1214 : } else {
1215 3281774 : king = BK; queen = BQ; rook = BR; bishop = BB; knight = BN;
1216 : }
1217 :
1218 6557057 : uint numQueensRooks = Material[queen] + Material[rook];
1219 6557057 : uint numQueensBishops = Material[queen] + Material[bishop];
1220 :
1221 : // We only bother if there are any sliding pieces of each type:
1222 6557057 : if (numQueensRooks > 0) {
1223 3977359 : fyleT fyle = square_Fyle (target);
1224 3977359 : rankT rank = square_Rank (target);
1225 : directionT dirs[4];
1226 3977359 : uint ndirs = 0;
1227 3977359 : if (FyleCount(queen,fyle) + FyleCount(rook,fyle) > 0) {
1228 630073 : dirs[ndirs++] = UP; dirs[ndirs++] = DOWN;
1229 : }
1230 3977359 : if (RankCount(queen,rank) + RankCount(rook,rank) > 0) {
1231 289862 : dirs[ndirs++] = LEFT; dirs[ndirs++] = RIGHT;
1232 : }
1233 5817229 : for (uint i = 0; i < ndirs; i++) {
1234 1839870 : directionT dir = dirs[i];
1235 1839870 : int delta = direction_Delta (dir);
1236 1839870 : squareT dest = target;
1237 1839870 : squareT last = square_Last (target, dir);
1238 :
1239 6481538 : while (dest != last) {
1240 3429168 : dest += delta;
1241 3429168 : pieceT p = Board[dest];
1242 3429168 : if (p == EMPTY) {
1243 : // empty square: keep searching
1244 1108334 : } else if (p == queen || p == rook) {
1245 : // Found an attacking piece
1246 353441 : fromSquares->Add(dest);
1247 353441 : 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 6557057 : if (numQueensBishops > 0) {
1258 4068147 : leftDiagT left = square_LeftDiag (target);
1259 4068147 : rightDiagT right = square_RightDiag (target);
1260 : directionT dirs[4];
1261 4068147 : uint ndirs = 0;
1262 4068147 : if (LeftDiagCount(queen,left) + LeftDiagCount(bishop,left) > 0) {
1263 313027 : dirs[ndirs++] = UP_LEFT; dirs[ndirs++] = DOWN_RIGHT;
1264 : }
1265 4068147 : if (RightDiagCount(queen,right) + RightDiagCount(bishop,right) > 0) {
1266 309122 : dirs[ndirs++] = UP_RIGHT; dirs[ndirs++] = DOWN_LEFT;
1267 : }
1268 5312445 : for (uint i = 0; i < ndirs; i++) {
1269 1244298 : directionT dir = dirs[i];
1270 1244298 : int delta = direction_Delta (dir);
1271 1244298 : squareT dest = target;
1272 1244298 : squareT last = square_Last (target, dir);
1273 :
1274 3666242 : while (dest != last) {
1275 1916903 : dest += delta;
1276 1916903 : pieceT p = Board[dest];
1277 1916903 : if (p == EMPTY) {
1278 : // empty square: keep searching
1279 705931 : } else if (p == queen || p == bishop) {
1280 : // Found an attacking piece
1281 320348 : fromSquares->Add(dest);
1282 320348 : 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 13114114 : if (Material[knight] > 0
1294 6557057 : && SquareColorCount(knight, color_Flip(square_Color(target))) > 0) {
1295 2210771 : const squareT * destPtr = knightAttacks[target];
1296 : while (true) {
1297 13696139 : squareT dest = *destPtr;
1298 13696139 : if (dest == NULL_SQUARE) { break; }
1299 11485368 : if (Board[dest] == knight) {
1300 192016 : fromSquares->Add(dest);
1301 : }
1302 11485368 : destPtr++;
1303 11485368 : }
1304 : }
1305 :
1306 : // Now pawn attacks:
1307 6557057 : if (side == WHITE) {
1308 3275283 : if (square_Rank(target) != RANK_1) { //if (Material[WP] > 0) {
1309 3194631 : sq = square_Move (target, DOWN_LEFT);
1310 3194631 : if (Board[sq] == WP) {
1311 65705 : fromSquares->Add(sq);
1312 : }
1313 3194631 : sq = square_Move (target, DOWN_RIGHT);
1314 3194631 : if (Board[sq] == WP) {
1315 63274 : fromSquares->Add(sq);
1316 : }
1317 : }
1318 : } else {
1319 3281774 : if (square_Rank(target) != RANK_8) { //if (Material[BP] > 0) {
1320 3197382 : sq = square_Move (target, UP_LEFT);
1321 3197382 : if (Board[sq] == BP) {
1322 64740 : fromSquares->Add(sq);
1323 : }
1324 3197382 : sq = square_Move (target, UP_RIGHT);
1325 3197382 : if (Board[sq] == BP) {
1326 66707 : fromSquares->Add(sq);
1327 : }
1328 : }
1329 : }
1330 :
1331 : // King attacks:
1332 6557057 : const squareT *destPtr = kingAttacks[target];
1333 81624557 : do if (Board[*destPtr] == king) fromSquares->Add(*destPtr);
1334 44090807 : while (*++destPtr != NULL_SQUARE);
1335 :
1336 6557057 : 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
1345 0 : Position::IsKingInCheckDir (directionT dir)
1346 : {
1347 0 : ASSERT (dir != NULL_DIR);
1348 0 : squareT kingSq = GetKingSquare(ToMove);
1349 0 : colorT enemy = color_Flip(ToMove);
1350 0 : bool isDiagonal = direction_IsDiagonal(dir);
1351 0 : pieceT queen = piece_Make (enemy, QUEEN);
1352 0 : pieceT slider = piece_Make (enemy, (isDiagonal ? BISHOP : ROOK));
1353 :
1354 : // First, make sure the enemy has sliding pieces that could give check:
1355 0 : uint nSliders = PieceCount(queen) + PieceCount(slider);
1356 0 : 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 0 : fyleT fyle = square_Fyle (kingSq);
1361 0 : rankT rank = square_Rank (kingSq);
1362 0 : leftDiagT ldiag = square_LeftDiag (kingSq);
1363 0 : rightDiagT rdiag = square_RightDiag (kingSq);
1364 :
1365 0 : switch (dir) {
1366 0 : case UP:
1367 : case DOWN:
1368 0 : nSliders = FyleCount(queen,fyle) + FyleCount(slider,fyle);
1369 0 : break;
1370 0 : case LEFT:
1371 : case RIGHT:
1372 0 : nSliders = RankCount(queen,rank) + RankCount(slider,rank);
1373 0 : break;
1374 0 : case UP_LEFT:
1375 : case DOWN_RIGHT:
1376 0 : nSliders = LeftDiagCount(queen,ldiag) + LeftDiagCount(slider,ldiag);
1377 0 : break;
1378 0 : case UP_RIGHT:
1379 : case DOWN_LEFT:
1380 0 : nSliders = RightDiagCount(queen,rdiag) + RightDiagCount(slider,rdiag);
1381 0 : break;
1382 : }
1383 0 : if (nSliders == 0) { return false; }
1384 :
1385 : // Now move along the specified direction looking for a checking piece:
1386 0 : squareT dest = kingSq;
1387 0 : squareT last = square_Last (kingSq, dir);
1388 0 : int delta = direction_Delta (dir);
1389 :
1390 0 : while (dest != last) {
1391 0 : dest += delta;
1392 0 : pieceT p = Board[dest];
1393 0 : if (p == EMPTY) {
1394 : // empty square: keep searching
1395 0 : } else if (p == queen || p == slider) {
1396 : // Found an checking slider piece
1397 0 : return true;
1398 : } else {
1399 : // Found a piece, but not an enemy queen or rook/bishop
1400 : break;
1401 : }
1402 : }
1403 :
1404 0 : 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
1416 0 : Position::IsKingInCheck (simpleMoveT * sm)
1417 : {
1418 0 : if (sm == NULL) { return IsKingInCheck(); }
1419 :
1420 0 : squareT kingSq = GetKingSquare(ToMove);
1421 0 : pieceT p = piece_Type (sm->movingPiece);
1422 0 : if (sm->promote != EMPTY) { p = piece_Type(sm->promote); }
1423 :
1424 : // No optimization of the last move was castling:
1425 0 : if (p == KING && square_Fyle(sm->from) == E_FYLE) {
1426 0 : fyleT toFyle = square_Fyle(sm->to);
1427 0 : if (toFyle == C_FYLE || toFyle == G_FYLE) {
1428 0 : return IsKingInCheck();
1429 : }
1430 : }
1431 : // No optimization for en passant capture:
1432 0 : if (p == PAWN && piece_Type(sm->capturedPiece) == PAWN) {
1433 0 : rankT fromRank = square_Rank(sm->from);
1434 0 : rankT capturedRank = square_Rank(sm->capturedSquare);
1435 0 : if (fromRank == capturedRank) { return IsKingInCheck(); }
1436 : }
1437 :
1438 0 : if (p == PAWN) {
1439 0 : if (ToMove == WHITE) {
1440 0 : if (Material[BP] > 0) {
1441 0 : squareT sq = square_Move (kingSq, UP_LEFT);
1442 0 : if (Board[sq] == BP) { return true; }
1443 0 : sq = square_Move (kingSq, UP_RIGHT);
1444 0 : if (Board[sq] == BP) { return true; }
1445 : }
1446 : } else {
1447 0 : if (Material[WP] > 0) {
1448 0 : squareT sq = square_Move (kingSq, DOWN_LEFT);
1449 0 : if (Board[sq] == WP) { return true; }
1450 0 : sq = square_Move (kingSq, DOWN_RIGHT);
1451 0 : if (Board[sq] == WP) { return true; }
1452 : }
1453 : }
1454 0 : } else if (p == KNIGHT) {
1455 0 : if (square_IsKnightHop (kingSq, sm->to)) { return true; }
1456 0 : } else if (p == KING) {
1457 : // A king cannot directly check its adversary.
1458 : } else {
1459 : // A sliding piece:
1460 0 : directionT toDir = sqDir[kingSq][sm->to];
1461 0 : if (toDir != NULL_DIR && IsKingInCheckDir(toDir)) { return true; }
1462 : }
1463 :
1464 : // Now look for a discovered check from a sliding piece:
1465 0 : directionT dir = sqDir[kingSq][sm->from];
1466 0 : if (dir != NULL_DIR && IsKingInCheckDir(dir)) { return true; }
1467 :
1468 0 : ASSERT (IsKingInCheck() == false);
1469 0 : 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
1477 0 : Position::Mobility (pieceT p, colorT color, squareT from)
1478 : {
1479 0 : ASSERT (p == ROOK || p == BISHOP);
1480 0 : uint mobility = 0;
1481 0 : directionT rookDirs[4] = { UP, DOWN, LEFT, RIGHT };
1482 0 : directionT bishopDirs[4]
1483 : = { UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT };
1484 0 : directionT * dirPtr = (p == ROOK ? rookDirs : bishopDirs);
1485 :
1486 0 : for (uint i=0; i < 4; i++) {
1487 0 : directionT dir = dirPtr[i];
1488 0 : int delta = direction_Delta (dir);
1489 0 : squareT dest = from;
1490 0 : squareT last = square_Last (from, dir);
1491 :
1492 0 : while (dest != last) {
1493 0 : dest += delta;
1494 0 : pieceT p = Board[dest];
1495 0 : if (p == EMPTY) { // Empty square
1496 0 : mobility++;
1497 0 : } else if (piece_Color(p) == color) { // Friendly piece
1498 0 : break; // Finished with this direction.
1499 : } else { // Enemy piece
1500 0 : mobility++;
1501 0 : break; // Finished with this direction.
1502 : }
1503 : }
1504 : }
1505 0 : return mobility;
1506 : }
1507 :
1508 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1509 : // Position::IsKingInMate():
1510 : // Quick check if king is in mate.
1511 : //
1512 : bool
1513 0 : Position::IsKingInMate (void)
1514 : {
1515 0 : SquareList checkSquares;
1516 0 : uint numChecks = CalcNumChecks (GetKingSquare(ToMove), &checkSquares);
1517 0 : if (numChecks == 0) { return false; }
1518 0 : CalcPins ();
1519 0 : MoveList mlist;
1520 0 : GenCheckEvasions (&mlist, EMPTY, GEN_ALL_MOVES, &checkSquares);
1521 0 : if (mlist.Size() == 0) { return true; }
1522 0 : 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
1533 21 : Position::IsLegal (void)
1534 : {
1535 21 : squareT stmKing = GetKingSquare();
1536 21 : squareT enemyKing = GetEnemyKingSquare();
1537 21 : if (square_Adjacent (stmKing, enemyKing)) { return false; }
1538 21 : if (RankCount (WP, RANK_1) != 0) { return false; }
1539 21 : if (RankCount (WP, RANK_8) != 0) { return false; }
1540 21 : if (RankCount (BP, RANK_1) != 0) { return false; }
1541 21 : if (RankCount (BP, RANK_8) != 0) { return false; }
1542 21 : if (CalcAttacks (ToMove, enemyKing, NULL) > 0) {
1543 0 : return false;
1544 : }
1545 21 : 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
1555 0 : Position::IsPromoMove (squareT from, squareT to)
1556 : {
1557 : rankT seventh, eighth;
1558 : pieceT pawn;
1559 0 : if (ToMove == WHITE) { seventh = RANK_7; eighth = RANK_8; pawn = WP; }
1560 0 : else { seventh = RANK_2; eighth = RANK_1; pawn = BP; }
1561 : rankT fromR, toR;
1562 0 : fromR = square_Rank(from);
1563 0 : toR = square_Rank(to);
1564 0 : if ( (fromR == seventh && toR == eighth && Board[from] == pawn) ||
1565 0 : (toR == seventh && fromR == eighth && Board[to] == pawn) ) {
1566 0 : return 1;
1567 : }
1568 0 : 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
1578 8748637 : Position::DoSimpleMove (simpleMoveT * sm)
1579 : {
1580 8748637 : ASSERT (sm != NULL);
1581 8748637 : squareT from = sm->from;
1582 8748637 : squareT to = sm->to;
1583 8748637 : pieceT p = Board[from];
1584 8748637 : pieceT ptype = piece_Type(p);
1585 8748255 : colorT enemy = color_Flip(ToMove);
1586 8748637 : ASSERT (p != EMPTY);
1587 :
1588 : // update move fields that (maybe) have not yet been set:
1589 :
1590 8748637 : sm->pieceNum = ListPos[from];
1591 8748637 : sm->capturedPiece = Board[to];
1592 8748637 : sm->capturedSquare = to;
1593 8748637 : sm->castleFlags = Castling;
1594 8748637 : sm->epSquare = EPTarget;
1595 8748637 : sm->oldHalfMoveClock = HalfMoveClock;
1596 :
1597 8748637 : HalfMoveClock++;
1598 8748637 : PlyCounter++;
1599 :
1600 : // Check for a null (empty) move:
1601 8748637 : if (sm->isNullMove()) {
1602 0 : ToMove = enemy;
1603 0 : EPTarget = NULL_SQUARE;
1604 0 : return;
1605 : }
1606 :
1607 : // Handle en passant capture:
1608 :
1609 9871434 : if (ptype == PAWN && sm->capturedPiece == EMPTY
1610 9674197 : && 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 408 : pieceT enemyPawn = piece_Make(enemy, PAWN);
1616 408 : sm->capturedSquare = (ToMove == WHITE ? (to - 8) : (to + 8));
1617 408 : ASSERT (Board[sm->capturedSquare] == enemyPawn);
1618 408 : sm->capturedPiece = enemyPawn;
1619 : }
1620 :
1621 : // handle captures:
1622 :
1623 8748637 : if (sm->capturedPiece != EMPTY) {
1624 800407 : ASSERT (piece_Type(sm->capturedPiece) != KING);
1625 800407 : sm->capturedNum = ListPos[sm->capturedSquare];
1626 : // update opponents List of pieces
1627 800407 : Count[enemy]--;
1628 800407 : ListPos[List[enemy][Count[enemy]]] = sm->capturedNum;
1629 800407 : List[enemy][sm->capturedNum] = List[enemy][Count[enemy]];
1630 800407 : Material[sm->capturedPiece]--;
1631 800407 : HalfMoveClock = 0;
1632 800407 : RemoveFromBoard (sm->capturedPiece, sm->capturedSquare);
1633 : }
1634 :
1635 : // handle promotion:
1636 :
1637 8748637 : if (sm->promote != EMPTY) {
1638 59760 : ASSERT (p == piece_Make(ToMove, PAWN));
1639 59760 : Material[p]--;
1640 59760 : RemoveFromBoard (p, from);
1641 59760 : p = piece_Make(ToMove, sm->promote);
1642 59760 : Material[p]++;
1643 59760 : AddToBoard (p, from);
1644 : }
1645 :
1646 : // now make the move:
1647 8748637 : List[ToMove][sm->pieceNum] = to;
1648 8748637 : ListPos[to] = sm->pieceNum;
1649 8748637 : RemoveFromBoard (p, from);
1650 8747994 : AddToBoard (p, to);
1651 :
1652 : // handle Castling:
1653 :
1654 9204010 : if (ptype == KING && square_Fyle(from) == E_FYLE &&
1655 876310 : (square_Fyle(to) == C_FYLE || square_Fyle(to) == G_FYLE)) {
1656 : squareT rookfrom, rookto;
1657 17460 : pieceT rook = piece_Make (ToMove, ROOK);
1658 17460 : if (square_Fyle(to) == C_FYLE) {
1659 1220 : rookfrom = to - 2;
1660 1220 : rookto = to + 1;
1661 : } else {
1662 16240 : rookfrom = to + 1;
1663 16240 : rookto = to - 1;
1664 : }
1665 17460 : ListPos[rookto] = ListPos[rookfrom];
1666 17460 : List[ToMove][ListPos[rookto]] = rookto;
1667 17460 : RemoveFromBoard (rook, rookfrom);
1668 17460 : AddToBoard (rook, rookto);
1669 : }
1670 :
1671 : // Handle clearing of castling flags:
1672 :
1673 8748637 : if (Castling) {
1674 1339805 : if (ptype == KING) { // The king moved.
1675 94917 : SetCastling (ToMove, QSIDE, false);
1676 94917 : SetCastling (ToMove, KSIDE, false);
1677 : }
1678 : // See if a rook moved or was captured:
1679 1339805 : if (ToMove == WHITE) {
1680 674686 : if (from == A1) { SetCastling (WHITE, QSIDE, false); }
1681 674686 : if (from == H1) { SetCastling (WHITE, KSIDE, false); }
1682 674686 : if (to == A8) { SetCastling (BLACK, QSIDE, false); }
1683 674686 : if (to == H8) { SetCastling (BLACK, KSIDE, false); }
1684 : } else {
1685 665119 : if (from == A8) { SetCastling (BLACK, QSIDE, false); }
1686 665119 : if (from == H8) { SetCastling (BLACK, KSIDE, false); }
1687 665119 : if (to == A1) { SetCastling (WHITE, QSIDE, false); }
1688 665119 : 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 8748637 : EPTarget = NULL_SQUARE;
1695 8748637 : if (ptype == PAWN) {
1696 1122809 : rankT fromRank = square_Rank(from);
1697 1122809 : rankT toRank = square_Rank(to);
1698 1375242 : if (fromRank == RANK_2 && toRank == RANK_4
1699 1234822 : && (Board[square_Move(to,LEFT)] == BP
1700 102659 : || Board[square_Move(to,RIGHT)] == BP)) {
1701 6130 : EPTarget = square_Move(from, UP);
1702 : }
1703 1383748 : if (fromRank == RANK_7 && toRank == RANK_5
1704 1227453 : && (Board[square_Move(to,LEFT)] == WP
1705 94384 : || Board[square_Move(to,RIGHT)] == WP)) {
1706 6728 : EPTarget = square_Move(from, DOWN);
1707 : }
1708 1122809 : HalfMoveClock = 0; // 50-move clock resets on pawn moves.
1709 : }
1710 :
1711 8748637 : ToMove = enemy;
1712 8748637 : return;
1713 : }
1714 :
1715 :
1716 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1717 : // Position::UndoSimpleMove():
1718 : // Take back a simple move that has been made with DoSimpleMove().
1719 : //
1720 : void
1721 4398509 : Position::UndoSimpleMove (simpleMoveT * m)
1722 : {
1723 4398509 : ASSERT (m != NULL);
1724 4398509 : squareT from = m->from;
1725 4398509 : squareT to = m->to;
1726 4398509 : pieceT p = Board[to];
1727 4398509 : EPTarget = m->epSquare;
1728 4398509 : Castling = m->castleFlags;
1729 4398509 : HalfMoveClock = m->oldHalfMoveClock;
1730 4398509 : PlyCounter--;
1731 4398509 : ToMove = color_Flip(ToMove);
1732 4398509 : m->pieceNum = ListPos[to];
1733 :
1734 : // Check for a null move:
1735 4398509 : if (m->isNullMove()) {
1736 0 : 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 4398509 : if (m->capturedPiece != EMPTY) {
1746 439359 : colorT c = color_Flip(ToMove);
1747 439359 : ListPos[List[c][m->capturedNum]] = Count[c];
1748 439359 : ListPos[m->capturedSquare] = m->capturedNum;
1749 439359 : List[c][Count[c]] = List[c][m->capturedNum];
1750 439359 : List[c][m->capturedNum] = m->capturedSquare;
1751 439359 : Material[m->capturedPiece]++;
1752 439359 : Count[c]++;
1753 : }
1754 :
1755 : // handle promotion:
1756 :
1757 4398509 : if (m->promote != EMPTY) {
1758 28753 : Material[p]--;
1759 28753 : RemoveFromBoard (p, to);
1760 28753 : p = piece_Make(ToMove, PAWN);
1761 28753 : Material[p]++;
1762 28753 : AddToBoard (p, to);
1763 : }
1764 :
1765 : // now make the move:
1766 :
1767 4398509 : List[ToMove][m->pieceNum] = from;
1768 4398509 : ListPos[from] = m->pieceNum;
1769 4398509 : RemoveFromBoard (p, to);
1770 4398509 : AddToBoard (p, from);
1771 4398509 : if (m->capturedPiece != EMPTY) {
1772 439359 : AddToBoard (m->capturedPiece, m->capturedSquare);
1773 : }
1774 :
1775 : // handle Castling:
1776 :
1777 10207502 : if ((piece_Type(p) == KING) && square_Fyle(from) == E_FYLE
1778 4643952 : && (square_Fyle(to) == C_FYLE || square_Fyle(to) == G_FYLE)) {
1779 : squareT rookfrom, rookto;
1780 11581 : pieceT rook = (ToMove == WHITE? WR : BR);
1781 11581 : if (square_Fyle(to) == C_FYLE) {
1782 778 : rookfrom = to - 2; rookto = to + 1;
1783 : } else {
1784 10803 : rookfrom = to + 1; rookto = to - 1;
1785 : }
1786 11581 : ListPos[rookfrom] = ListPos[rookto];
1787 11581 : List[ToMove][ListPos[rookto]] = rookfrom;
1788 11581 : RemoveFromBoard (rook, rookto);
1789 11581 : AddToBoard (rook, rookfrom);
1790 : }
1791 4398509 : 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
1805 0 : Position::RelocatePiece (squareT fromSq, squareT toSq)
1806 : {
1807 : // Must have on-board squares:
1808 0 : if (fromSq == NS || toSq == NS) { return ERROR; }
1809 :
1810 : // If squares are identical, just return success:
1811 0 : if (fromSq == toSq) { return OK; }
1812 :
1813 0 : pieceT piece = Board[fromSq];
1814 0 : pieceT ptype = piece_Type(piece);
1815 0 : colorT pcolor = piece_Color(piece);
1816 :
1817 : // Must be relocating a nonempty piece to an empty square:
1818 0 : if (piece == EMPTY || Board[toSq] != EMPTY) { return ERROR; }
1819 :
1820 : // Pawns cannot relocate to the first or last rank:
1821 0 : if (ptype == PAWN) {
1822 0 : rankT toRank = square_Rank(toSq);
1823 0 : if (toRank == RANK_1 || toRank == RANK_8) { return ERROR; }
1824 : }
1825 :
1826 : // Locate the piece index in the appropriate list of pieces:
1827 0 : uint index = ListPos[fromSq];
1828 0 : ASSERT(List[pcolor][index] == fromSq);
1829 :
1830 : // Relocate the piece:
1831 0 : List[pcolor][index] = toSq;
1832 0 : ListPos[toSq] = index;
1833 0 : RemoveFromBoard (piece, fromSq);
1834 0 : AddToBoard (piece, toSq);
1835 :
1836 : // Check for adjacent kings or side to move giving check:
1837 0 : if (! IsLegal()) {
1838 : // Undo the relocation and return error:
1839 0 : List[pcolor][index] = fromSq;
1840 0 : RemoveFromBoard (piece, toSq);
1841 0 : AddToBoard (piece, fromSq);
1842 0 : return ERROR;
1843 : }
1844 :
1845 : // Relocation successful:
1846 0 : 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
1860 0 : Position::MaterialValue (colorT c)
1861 : {
1862 0 : ASSERT (c == WHITE || c == BLACK);
1863 0 : uint value = 0;
1864 0 : if (c == WHITE) {
1865 0 : value += 9 * PieceCount(WQ);
1866 0 : value += 5 * PieceCount(WR);
1867 0 : value += 3 * PieceCount(WB);
1868 0 : value += 3 * PieceCount(WN);
1869 0 : value += 1 * PieceCount(WP);
1870 : } else {
1871 0 : value += 9 * PieceCount(BQ);
1872 0 : value += 5 * PieceCount(BR);
1873 0 : value += 3 * PieceCount(BB);
1874 0 : value += 3 * PieceCount(BN);
1875 0 : value += 1 * PieceCount(BP);
1876 : }
1877 0 : 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
1888 1564552 : Position::MakeSANString (simpleMoveT * m, char * s, sanFlagT flag)
1889 : {
1890 1564552 : ASSERT (m != NULL && s != NULL);
1891 :
1892 : // Make sure m->pieceNum is updated:
1893 1564552 : m->pieceNum = ListPos[m->from];
1894 1564552 : squareT from = List[ToMove][m->pieceNum];
1895 1564552 : squareT to = m->to;
1896 1564552 : char * c = s;
1897 1564552 : pieceT piece = Board[from];
1898 1564552 : pieceT p = piece_Type(piece);
1899 :
1900 1564552 : if (p == PAWN) {
1901 196972 : if (square_Fyle(from) != square_Fyle(to)) { // pawn capture
1902 32759 : *c++ = square_FyleChar(from);
1903 32759 : *c++ = 'x';
1904 : }
1905 196972 : *c++ = square_FyleChar(to);
1906 196972 : *c++ = square_RankChar(to);
1907 196972 : if ((square_Rank(to)==RANK_1) || (square_Rank(to)==RANK_8)) {
1908 9891 : *c++ = '=';
1909 9891 : *c++ = piece_Char(m->promote);
1910 9891 : p = piece_Type(m->promote);
1911 : }
1912 :
1913 1367580 : } else if (p == KING) {
1914 511887 : if (m->isNullMove()) {
1915 : //*c++ = 'n'; *c++ = 'u'; *c++ = 'l'; *c++ = 'l';
1916 0 : *c++ = '-'; *c++ = '-';
1917 : } else {
1918 511887 : switch (m->isCastle()) {
1919 511203 : case 0:
1920 511203 : *c++ = 'K';
1921 511203 : if (Board[to] != EMPTY)
1922 17890 : *c++ = 'x';
1923 511203 : *c++ = square_FyleChar(to);
1924 511203 : *c++ = square_RankChar(to);
1925 511203 : break;
1926 460 : case 1:
1927 460 : *c++ = 'O';
1928 460 : *c++ = '-';
1929 460 : *c++ = 'O';
1930 460 : break;
1931 224 : case 2:
1932 224 : *c++ = 'O';
1933 224 : *c++ = '-';
1934 224 : *c++ = 'O';
1935 224 : *c++ = '-';
1936 224 : *c++ = 'O';
1937 224 : break;
1938 : }
1939 : }
1940 :
1941 : } else { // Queen/Rook/Bishop/Knight
1942 855693 : *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 855693 : if (Material[piece] > 1) {
1947 371802 : int ambiguity = 0;
1948 4121115 : for (uint i = 1, n = Count[ToMove]; i < n; i++) {
1949 3749313 : squareT sq = List[ToMove][i];
1950 3749313 : if (sq == from || Board[sq] != piece)
1951 7096966 : continue;
1952 :
1953 381215 : if (!movegen::pseudo(sq, to, ToMove, p, Board, EMPTY))
1954 360566 : continue; // Skip illegal move
1955 :
1956 : std::pair<pieceT, squareT> pin =
1957 20649 : movegen::opens_ray(sq, to, GetKingSquare(), Board, EMPTY);
1958 22747 : if (pin.first != INVALID_PIECE &&
1959 2098 : piece_Color_NotEmpty(Board[pin.second]) != ToMove) {
1960 890 : pieceT pt = piece_Type(Board[pin.second]);
1961 890 : if (pt == QUEEN || pt == pin.first)
1962 204 : 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 20445 : ambiguity |= 1;
1971 20445 : if (square_Rank(from) == square_Rank(sq)) {
1972 4828 : ambiguity |= 2; // 0b0010
1973 15617 : } else if (square_Fyle(from) == square_Fyle(sq)) {
1974 2088 : ambiguity |= 4; // 0b0100
1975 : }
1976 : }
1977 371802 : if (ambiguity) {
1978 20373 : if (ambiguity != 5)
1979 18290 : *c++ = square_FyleChar(from); // print from-fyle
1980 20373 : if (ambiguity >= 5)
1981 2088 : *c++ = square_RankChar(from); // print from-rank
1982 : }
1983 : }
1984 855693 : if (Board[to] != EMPTY)
1985 82979 : *c++ = 'x';
1986 855693 : *c++ = square_FyleChar(to);
1987 855693 : *c++ = square_RankChar(to);
1988 : }
1989 :
1990 : bool check;
1991 1564552 : if (flag != SAN_NO_CHECKTEST) {
1992 1564552 : squareT oldTo = Board[to];
1993 1564552 : Board[to] = Board[from];
1994 1564552 : Board[from] = EMPTY;
1995 1564552 : squareT enemyKingSq = GetEnemyKingSquare();
1996 2617217 : check = (p != KING) &&
1997 1052665 : movegen::attack(to, enemyKingSq, ToMove, p, Board, EMPTY);
1998 1564552 : if (!check) {
1999 1640172 : bool enpassant = (p == PAWN && oldTo == EMPTY &&
2000 1640172 : square_Fyle(from) != square_Fyle(to));
2001 2975765 : if (!enpassant && (p != KING || !m->isCastle()) &&
2002 1487509 : !movegen::attack_slider(from, enemyKingSq, QUEEN, Board,
2003 : EMPTY)) {
2004 1285549 : flag = SAN_NO_CHECKTEST;
2005 : }
2006 :
2007 76296 : } else if (flag != SAN_MATETEST) {
2008 60340 : *c++ = '+';
2009 60340 : flag = SAN_NO_CHECKTEST;
2010 : }
2011 1564552 : Board[from] = Board[to];
2012 1564552 : Board[to] = oldTo;
2013 : }
2014 :
2015 : // Now do the check or mate symbol:
2016 1564552 : if (flag != SAN_NO_CHECKTEST) {
2017 : // Now we make the move to test for check:
2018 218663 : DoSimpleMove (m);
2019 218663 : if (check || CalcNumChecks(GetKingSquare()) > 0) {
2020 18077 : char ch = '+';
2021 18077 : if (flag == SAN_MATETEST) {
2022 16377 : MoveList mlist;
2023 16377 : GenerateMoves (&mlist);
2024 16377 : if (mlist.Size() == 0) { ch = '#'; }
2025 : }
2026 18077 : *c++ = ch;
2027 : }
2028 218663 : UndoSimpleMove (m);
2029 : }
2030 1564552 : *c = 0;
2031 1564552 : }
2032 :
2033 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2034 : // Position::MakeUCIString():
2035 : // Make the UCI string for a simpleMove.
2036 : //
2037 : void
2038 0 : Position::MakeUCIString (simpleMoveT * m, char * s)
2039 : {
2040 0 : ASSERT (m != NULL && s != NULL);
2041 :
2042 : // Make sure m->pieceNum is updated:
2043 0 : m->pieceNum = ListPos[m->from];
2044 0 : pieceT p = piece_Type (Board[List[ToMove][m->pieceNum]]);
2045 0 : squareT from = List[ToMove][m->pieceNum];
2046 0 : squareT to = m->to;
2047 :
2048 0 : char * c = s;
2049 :
2050 0 : if (from == to && to != NULL_SQUARE) {
2051 : // UCI standard for null move
2052 0 : c[0] = '0';
2053 0 : c[1] = '0';
2054 0 : c[2] = '0';
2055 0 : c[3] = '0';
2056 0 : c[4] = 0;
2057 0 : return;
2058 : }
2059 :
2060 0 : *c++ = square_FyleChar(from);
2061 0 : *c++ = square_RankChar(from);
2062 0 : *c++ = square_FyleChar(to);
2063 0 : *c++ = square_RankChar(to);
2064 0 : if (p == PAWN) {
2065 0 : if ((square_Rank(to)==RANK_1) || (square_Rank(to)==RANK_8)) {
2066 0 : *c++ = piece_Char(m->promote);
2067 : }
2068 : }
2069 :
2070 0 : *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 : //
2081 34 : errorT Position::ReadCoordMove(simpleMoveT* m, const char* str, int slen,
2082 : bool reverse) {
2083 34 : ASSERT (m != NULL && str != NULL);
2084 : fyleT fromFyle, toFyle;
2085 : rankT fromRank, toRank;
2086 : squareT from, to;
2087 34 : pieceT promo = EMPTY;
2088 :
2089 34 : if (slen == 5) {
2090 5 : promo = piece_FromChar(toupper(str[4]));
2091 29 : } else if (slen != 4) { return ERROR_InvalidMove; }
2092 :
2093 34 : fromFyle = fyle_FromChar (str[0]);
2094 34 : fromRank = rank_FromChar (str[1]);
2095 34 : from = square_Make (fromFyle, fromRank);
2096 34 : if (from == NS) { return ERROR_InvalidMove; }
2097 :
2098 34 : toFyle = fyle_FromChar (str[2]);
2099 34 : toRank = rank_FromChar (str[3]);
2100 34 : to = square_Make (toFyle, toRank);
2101 34 : if (to == NS) { return ERROR_InvalidMove; }
2102 :
2103 34 : MoveList mlist;
2104 34 : GenerateMoves(&mlist);
2105 535 : for (size_t i = 0, n = mlist.Size(); i < n; i++) {
2106 535 : simpleMoveT* sm = mlist.Get(i);
2107 535 : if (sm->promote == promo) {
2108 516 : if (sm->from == from && sm->to == to) {
2109 34 : *m = *sm;
2110 34 : return OK;
2111 : }
2112 482 : if (reverse && sm->to == from && sm->from == to) {
2113 0 : *m = *sm;
2114 0 : return OK;
2115 : }
2116 : }
2117 : }
2118 0 : return ERROR_InvalidMove;
2119 : }
2120 :
2121 1643866 : static int trimCheck(const char* str, int slen) {
2122 1722171 : while (slen > 0) { // trim mate '#' or check '+'
2123 1643866 : --slen;
2124 1643866 : if (str[slen] != '#' && str[slen] != '+') {
2125 1565561 : ++slen;
2126 1565561 : break;
2127 : }
2128 : }
2129 1565561 : return slen;
2130 : }
2131 :
2132 196723 : errorT Position::ReadMovePawn(simpleMoveT* sm, const char* str, int slen,
2133 : fyleT frFyle) {
2134 196723 : ASSERT(sm != NULL && str != NULL && frFyle <= H_FYLE);
2135 :
2136 196723 : slen = trimCheck(str, slen);
2137 196723 : if (slen < 2)
2138 0 : return ERROR_InvalidMove;
2139 :
2140 238280 : auto is_digit = [](char ch) {
2141 238280 : return isdigit(static_cast<unsigned char>(ch));
2142 238280 : };
2143 8902 : auto is_lower = [](char ch) {
2144 8902 : return islower(static_cast<unsigned char>(ch));
2145 8902 : };
2146 :
2147 238280 : if (slen >= 4 && // Check if it is a coordinates-style move ("e2e4")
2148 205625 : is_digit(str[1]) && is_lower(str[2]) && is_digit(str[3])) {
2149 0 : return ReadCoordMove(sm, str, slen, false);
2150 : }
2151 :
2152 196723 : MoveList mlist;
2153 196723 : pieceT promo = EMPTY;
2154 196723 : auto last_ch = static_cast<unsigned char>(str[slen - 1]);
2155 196723 : if (!is_digit(last_ch)) {
2156 : // Promotion, last char must be Q/R/B/N.
2157 9886 : promo = piece_FromChar(toupper(last_ch));
2158 9886 : if (promo != QUEEN && promo != ROOK && promo != KNIGHT &&
2159 : promo != BISHOP) {
2160 0 : return ERROR_InvalidMove;
2161 : }
2162 9886 : slen--;
2163 : // Accept the move even if it is of the form "a8Q" not "a8=Q":
2164 9886 : if (str[slen - 1] == '=') {
2165 9886 : slen--;
2166 : }
2167 : }
2168 196723 : if (slen < 2)
2169 0 : return ERROR_InvalidMove;
2170 :
2171 : // Check for the compact form of capture with no rank,
2172 : // e.g. "ed" or "de=Q":
2173 196723 : if (slen == 2 && (str[1] >= 'a' && str[1] <= 'h')) {
2174 0 : auto toFyle = fyle_FromChar(str[1]);
2175 : // Check each rank in turn, looking for the capture:
2176 0 : for (rankT r = RANK_1; r <= RANK_8; r++) {
2177 0 : auto to = square_Make(toFyle, r);
2178 0 : if (MatchPawnMove(&mlist, frFyle, to, promo) == OK) {
2179 0 : *sm = *(mlist.Get(0));
2180 0 : return OK;
2181 : }
2182 : }
2183 : // It is NOT a valid capture with no rank:
2184 0 : return ERROR_InvalidMove;
2185 : }
2186 :
2187 196723 : auto toFyle = fyle_FromChar(str[slen - 2]);
2188 196723 : auto toRank = rank_FromChar(str[slen - 1]);
2189 196723 : if (toRank == NO_RANK || toFyle == NO_FYLE)
2190 0 : return ERROR_InvalidMove;
2191 :
2192 196723 : auto to = square_Make(toFyle, toRank);
2193 196723 : if (MatchPawnMove(&mlist, frFyle, to, promo) != OK)
2194 0 : return ERROR_InvalidMove;
2195 :
2196 196723 : *sm = *(mlist.Get(0));
2197 196723 : return OK;
2198 : }
2199 :
2200 512360 : errorT Position::ReadMoveKing(simpleMoveT* sm, const char* str, int slen) {
2201 512360 : ASSERT(sm != NULL && str != NULL);
2202 :
2203 512360 : slen = trimCheck(str, slen);
2204 512360 : if (slen < 3 || slen > 6)
2205 0 : return ERROR_InvalidMove;
2206 :
2207 512360 : auto toRank = rank_FromChar(str[slen - 1]);
2208 512360 : auto toFyle = fyle_FromChar(str[slen - 2]);
2209 512360 : if (toRank == NO_RANK || toFyle == NO_FYLE)
2210 0 : return ERROR_InvalidMove;
2211 :
2212 512360 : auto target = square_Make(toFyle, toRank);
2213 512360 : squareT kingSq = GetKingSquare(ToMove);
2214 512360 : if (!movegen::valid_king(kingSq, target))
2215 0 : return ERROR_InvalidMove;
2216 :
2217 512360 : pieceT captured = Board[target];
2218 530221 : if (captured != EMPTY && (piece_Color_NotEmpty(captured) == ToMove ||
2219 17861 : piece_Type(captured) == KING)) {
2220 0 : return ERROR_InvalidMove;
2221 : }
2222 :
2223 : // XXX We should also check for adjacency to enemy King!!
2224 512360 : if (movegen::valid_king(GetKingSquare(color_Flip(ToMove)), target))
2225 0 : 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 512360 : auto movingPiece = piece_Make(ToMove, KING);
2230 512360 : Board[target] = movingPiece;
2231 512360 : Board[kingSq] = EMPTY;
2232 512360 : if (captured != EMPTY) {
2233 17861 : Material[captured]--;
2234 : }
2235 512360 : auto nChecks = CalcNumChecks(target);
2236 512360 : if (captured != EMPTY) {
2237 17861 : Material[captured]++;
2238 : }
2239 512360 : Board[target] = captured;
2240 512360 : Board[kingSq] = movingPiece;
2241 512360 : if (nChecks)
2242 0 : return ERROR_InvalidMove;
2243 :
2244 512360 : sm->from = kingSq;
2245 512360 : sm->to = target;
2246 512360 : sm->promote = EMPTY;
2247 512360 : sm->movingPiece = movingPiece;
2248 512360 : 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 : //
2257 855835 : errorT Position::ReadMove(simpleMoveT* sm, const char* str, int slen,
2258 : pieceT piece) {
2259 855835 : ASSERT(sm != NULL && str != NULL);
2260 855835 : ASSERT(piece == QUEEN || piece == ROOK || piece == BISHOP ||
2261 : piece == KNIGHT);
2262 :
2263 855835 : slen = trimCheck(str, slen);
2264 855835 : if (slen < 3 || slen > 6)
2265 0 : return ERROR_InvalidMove;
2266 :
2267 855835 : auto toRank = rank_FromChar(str[slen - 1]);
2268 855835 : auto toFyle = fyle_FromChar(str[slen - 2]);
2269 855835 : if (toRank == NO_RANK || toFyle == NO_FYLE)
2270 0 : return ERROR_InvalidMove;
2271 855835 : auto to = square_Make(toFyle, toRank);
2272 :
2273 855835 : pieceT captured = Board[to];
2274 938529 : if (captured != EMPTY && (piece_Color_NotEmpty(captured) == ToMove ||
2275 82694 : piece_Type(captured) == KING)) {
2276 0 : return ERROR_InvalidMove;
2277 : }
2278 :
2279 855835 : auto frFyle = NO_FYLE;
2280 855835 : auto frRank = NO_RANK;
2281 855835 : if (slen > 3) { // There is some ambiguity information in the input string.
2282 101493 : frFyle = fyle_FromChar(str[1]);
2283 101493 : frRank = rank_FromChar(str[1]);
2284 101493 : if (frRank == NO_RANK && slen > 4) {
2285 1376 : 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 855835 : int matchCount = 0;
2292 855835 : auto movingPiece = piece_Make(ToMove, piece);
2293 855835 : int nPieces = Material[movingPiece];
2294 855835 : squareT kingSq = GetKingSquare(ToMove);
2295 4250167 : for (unsigned i = 1, n = Count[ToMove]; i < n && nPieces; i++) {
2296 3394332 : auto from = List[ToMove][i];
2297 3394332 : if (Board[from] != movingPiece)
2298 4695707 : continue;
2299 :
2300 1237122 : --nPieces;
2301 1258871 : if ((frFyle != NO_FYLE && frFyle != square_Fyle(from)) ||
2302 4400 : (frRank != NO_RANK && frRank != square_Rank(from)))
2303 21749 : continue;
2304 :
2305 1215373 : if (!movegen::pseudo(from, to, ToMove, piece, Board, EMPTY))
2306 359336 : continue;
2307 :
2308 856037 : auto pin = movegen::opens_ray(from, to, kingSq, Board, EMPTY);
2309 856037 : if (pin.first != INVALID_PIECE) {
2310 86281 : auto p = Board[pin.second];
2311 112903 : if (piece_Color_NotEmpty(p) != ToMove &&
2312 52786 : (piece_Type(p) == QUEEN || piece_Type(p) == pin.first))
2313 202 : continue;
2314 : }
2315 :
2316 855835 : ++matchCount;
2317 855835 : sm->from = from;
2318 855835 : sm->to = to;
2319 855835 : sm->promote = EMPTY;
2320 855835 : sm->movingPiece = movingPiece;
2321 : }
2322 855835 : return (matchCount == 1) ? OK // ok.
2323 855835 : : ERROR_InvalidMove; // No match, or too many
2324 : // (ambiguous) moves match.
2325 : }
2326 :
2327 643 : errorT Position::ReadMoveCastle(simpleMoveT* sm, const char* str, int slen) {
2328 643 : slen = trimCheck(str, slen);
2329 :
2330 1065 : auto str_equal = [&](const char* const_str, const int len) {
2331 1065 : return slen == len && std::equal(str, str + len, const_str);
2332 1708 : };
2333 :
2334 : // short castle
2335 643 : if (str_equal("O-O", 3) || str_equal("OO", 2)) {
2336 432 : squareT kingSq = GetKingSquare(ToMove);
2337 432 : if (kingSq != (ToMove == WHITE ? E1 : E8))
2338 0 : return ERROR_InvalidMove;
2339 :
2340 1728 : if (Board[kingSq + 1] != EMPTY || Board[kingSq + 2] != EMPTY ||
2341 1728 : CalcNumChecks(kingSq) > 0 || CalcNumChecks(kingSq + 1) > 0 ||
2342 432 : CalcNumChecks(kingSq + 2) > 0) {
2343 0 : return ERROR_InvalidMove;
2344 : }
2345 432 : sm->from = kingSq;
2346 432 : sm->to = kingSq + 2;
2347 432 : sm->promote = EMPTY;
2348 432 : sm->movingPiece = KING;
2349 432 : sm->capturedPiece = EMPTY;
2350 432 : return GetCastling(ToMove, KSIDE) ? OK : ERROR_CastlingAvailability;
2351 : }
2352 : // long castle
2353 211 : if (str_equal("O-O-O", 5) || str_equal("OOO", 3)) {
2354 211 : squareT kingSq = GetKingSquare(ToMove);
2355 211 : if (kingSq != (ToMove == WHITE ? E1 : E8))
2356 0 : return ERROR_InvalidMove;
2357 :
2358 844 : if (Board[kingSq - 1] != EMPTY || Board[kingSq - 2] != EMPTY ||
2359 633 : Board[kingSq - 3] != EMPTY || CalcNumChecks(kingSq) > 0 ||
2360 633 : CalcNumChecks(kingSq - 1) > 0 || CalcNumChecks(kingSq - 2) > 0) {
2361 0 : return ERROR_InvalidMove;
2362 : }
2363 211 : sm->from = kingSq;
2364 211 : sm->to = kingSq - 2;
2365 211 : sm->promote = EMPTY;
2366 211 : sm->movingPiece = KING;
2367 211 : sm->capturedPiece = EMPTY;
2368 211 : return GetCastling(ToMove, QSIDE) ? OK : ERROR_CastlingAvailability;
2369 : }
2370 0 : return ERROR_InvalidMove;
2371 : }
2372 :
2373 0 : errorT Position::ParseMove(simpleMoveT* sm, const char* str) {
2374 0 : while (!isalpha(static_cast<unsigned char>(*str)) && *str != '\0') {
2375 0 : str++; // trim left
2376 : }
2377 0 : const char* begin = str;
2378 0 : while (!isspace(static_cast<unsigned char>(*str)) && *str != '\0') {
2379 0 : str++; // trim right
2380 : }
2381 0 : return ParseMove(sm, begin, str);
2382 : }
2383 :
2384 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2385 : // Position::ParseMove():
2386 : // Parse a single move from SAN-style notation.
2387 : //
2388 1565563 : errorT Position::ParseMove(simpleMoveT* sm, const char* str,
2389 : const char* strEnd) {
2390 1565563 : ASSERT(str != NULL);
2391 :
2392 1565563 : int length = static_cast<int>(std::distance(str, strEnd));
2393 1565563 : if (length < 2 || length > 9)
2394 1 : return ERROR_InvalidMove;
2395 :
2396 1565562 : switch (str[0]) {
2397 23979 : case 'a':
2398 23979 : return ReadMovePawn(sm, str, length, A_FYLE);
2399 24805 : case 'b':
2400 24805 : return ReadMovePawn(sm, str, length, B_FYLE);
2401 24610 : case 'c':
2402 24610 : return ReadMovePawn(sm, str, length, C_FYLE);
2403 24242 : case 'd':
2404 24242 : return ReadMovePawn(sm, str, length, D_FYLE);
2405 24776 : case 'e':
2406 24776 : return ReadMovePawn(sm, str, length, E_FYLE);
2407 24506 : case 'f':
2408 24506 : return ReadMovePawn(sm, str, length, F_FYLE);
2409 25468 : case 'g':
2410 25468 : return ReadMovePawn(sm, str, length, G_FYLE);
2411 24337 : case 'h':
2412 24337 : return ReadMovePawn(sm, str, length, H_FYLE);
2413 512360 : case 'K':
2414 512360 : return ReadMoveKing(sm, str, length);
2415 131866 : case 'Q':
2416 131866 : return ReadMove(sm, str, length, QUEEN);
2417 278200 : case 'R':
2418 278200 : return ReadMove(sm, str, length, ROOK);
2419 243845 : case 'B':
2420 243845 : return ReadMove(sm, str, length, BISHOP);
2421 201924 : case 'N':
2422 201924 : return ReadMove(sm, str, length, KNIGHT);
2423 643 : case 'O':
2424 643 : return ReadMoveCastle(sm, str, length);
2425 : }
2426 :
2427 : // Check for a null move:
2428 1 : if ((length == 2 && std::equal(str, str + 2, "--")) ||
2429 1 : (length == 2 && std::equal(str, str + 2, "Z0")) ||
2430 0 : (length == 4 && std::equal(str, str + 4, "null"))) {
2431 0 : sm->pieceNum = 0;
2432 0 : sm->from = GetKingSquare(ToMove);
2433 0 : sm->to = sm->from;
2434 0 : sm->movingPiece = Board[sm->from];
2435 0 : sm->promote = EMPTY;
2436 0 : return OK;
2437 : }
2438 :
2439 : // Invalid move, check for a misspelled first char:
2440 1 : switch (str[0]) {
2441 0 : case 'A':
2442 0 : return ReadMovePawn(sm, str, length, A_FYLE);
2443 0 : case 'C':
2444 0 : return ReadMovePawn(sm, str, length, C_FYLE);
2445 0 : case 'D':
2446 0 : return ReadMovePawn(sm, str, length, D_FYLE);
2447 0 : case 'E':
2448 0 : return ReadMovePawn(sm, str, length, E_FYLE);
2449 0 : case 'F':
2450 0 : return ReadMovePawn(sm, str, length, F_FYLE);
2451 0 : case 'G':
2452 0 : return ReadMovePawn(sm, str, length, G_FYLE);
2453 0 : case 'H':
2454 0 : return ReadMovePawn(sm, str, length, H_FYLE);
2455 0 : case 'P':
2456 0 : return ParseMove(sm, str + 1, strEnd);
2457 0 : case 'k':
2458 0 : return ReadMoveKing(sm, str, length);
2459 0 : case 'q':
2460 0 : return ReadMove(sm, str, length, QUEEN);
2461 0 : case 'r':
2462 0 : return ReadMove(sm, str, length, ROOK);
2463 0 : case 'n':
2464 0 : return ReadMove(sm, str, length, KNIGHT);
2465 : }
2466 1 : 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
2474 0 : Position::CalcSANStrings (sanListT *sanList, sanFlagT flag)
2475 : {
2476 0 : MoveList mlist;
2477 0 : GenerateMoves(&mlist);
2478 0 : for (size_t i = 0, n = mlist.Size(); i < n; i++) {
2479 0 : MakeSANString(mlist.Get(i), sanList->list[i], flag);
2480 : }
2481 0 : sanList->num = mlist.Size();
2482 0 : sanList->current = true;
2483 0 : }
2484 :
2485 : errorT
2486 0 : Position::ReadFromLongStr (const char * str)
2487 : {
2488 0 : pieceT pieceFromByte [256] = {EMPTY};
2489 0 : pieceFromByte [(int) 'K'] = WK; pieceFromByte [(int) 'k'] = BK;
2490 0 : pieceFromByte [(int) 'Q'] = WQ; pieceFromByte [(int) 'q'] = BQ;
2491 0 : pieceFromByte [(int) 'R'] = WR; pieceFromByte [(int) 'r'] = BR;
2492 0 : pieceFromByte [(int) 'B'] = WB; pieceFromByte [(int) 'b'] = BB;
2493 0 : pieceFromByte [(int) 'N'] = WN; pieceFromByte [(int) 'n'] = BN;
2494 0 : pieceFromByte [(int) 'P'] = WP; pieceFromByte [(int) 'p'] = BP;
2495 :
2496 0 : Clear();
2497 0 : for (squareT sq=A1; sq <= H8; sq++) {
2498 0 : if (str[sq] == '.') { continue; }
2499 0 : pieceT p = pieceFromByte [(byte) str[sq]];
2500 0 : if (p == EMPTY) { return ERROR_Corrupt; }
2501 0 : if (AddPiece (p,sq) != OK) { return ERROR_Corrupt; }
2502 : }
2503 0 : switch (str[65]) {
2504 0 : case 'w':
2505 0 : SetToMove (WHITE);
2506 0 : break;
2507 0 : case 'b':
2508 0 : SetToMove (BLACK);
2509 0 : break;
2510 0 : default:
2511 0 : return ERROR_Corrupt;
2512 : }
2513 0 : 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
2526 0 : Position::MakeLongStr (char * str)
2527 : {
2528 0 : ASSERT (str != NULL);
2529 0 : char * s = str;
2530 0 : for (squareT sq = A1; sq <= H8; sq++) {
2531 0 : *s++ = PIECE_CHAR[Board[sq]];
2532 : }
2533 0 : *s++ = ' ';
2534 0 : *s++ = (ToMove == WHITE ? 'w' : 'b');
2535 0 : *s = 0;
2536 0 : }
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
2550 0 : Position::ReadFromCompactStr (const byte * str)
2551 : {
2552 0 : Clear();
2553 0 : for (uint i=0; i < 32; i++) {
2554 0 : pieceT p = str[i] >> 4;
2555 0 : if (p != EMPTY) {
2556 0 : if (AddPiece (p, i * 2) != OK) {
2557 0 : return ERROR_Corrupt;
2558 : }
2559 : }
2560 0 : p = str[i] & 15;
2561 0 : if (p != EMPTY) {
2562 0 : if (AddPiece (p, i * 2 + 1) != OK) {
2563 0 : return ERROR_Corrupt;
2564 : }
2565 : }
2566 : }
2567 0 : colorT toMove = str[32] - 1;
2568 0 : if (toMove != WHITE && toMove != BLACK) {
2569 0 : return ERROR_Corrupt;
2570 : }
2571 0 : ToMove = toMove;
2572 0 : Castling = str[33] - 1;
2573 0 : EPTarget = str[34] - 1;
2574 0 : return OK;
2575 : }
2576 :
2577 : void
2578 0 : Position::PrintCompactStr (char * cboard)
2579 : {
2580 0 : for (uint i=0; i < 32; i++) {
2581 0 : uint i2 = i << 1;
2582 0 : cboard[i] = (byte)(Board[i2] << 4) | Board[i2+1];
2583 : }
2584 0 : cboard[32] = 1 + ToMove;
2585 0 : 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 0 : squareT ep = EPTarget;
2593 0 : if (ToMove == WHITE) {
2594 0 : if (Board[square_Move (ep, DOWN_LEFT)] != WP &&
2595 0 : Board[square_Move (ep, DOWN_RIGHT)] != WP) { ep = NULL_SQUARE; }
2596 :
2597 : } else {
2598 0 : if (Board[square_Move (ep, UP_LEFT)] != BP &&
2599 0 : Board[square_Move (ep, UP_RIGHT)] != BP) { ep = NULL_SQUARE; }
2600 :
2601 : }
2602 0 : cboard[34] = 1 + ep;
2603 0 : cboard[35] = 0;
2604 0 : }
2605 :
2606 : void
2607 0 : Position::PrintCompactStrFlipped (char * cboard)
2608 : {
2609 0 : for (uint i=0; i < 32; i++) {
2610 0 : uint i2 = i << 1;
2611 : // Flip 1st rank to 8th, etc:
2612 0 : i2 = ((7 - (i2)/8) * 8 + ((i2) % 8));
2613 0 : cboard[i] = (byte)(PIECE_FLIP[Board[i2]] << 4) |
2614 0 : (byte)(PIECE_FLIP[Board[i2+1]]);
2615 : }
2616 0 : cboard[32] = 1 + color_Flip(ToMove);
2617 0 : cboard[33] = 1 + Castling;
2618 0 : cboard[34] = 1 + EPTarget;
2619 0 : cboard[35] = 0;
2620 0 : }
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
2638 21 : Position::ReadFromFEN (const char * str)
2639 : {
2640 265 : auto is_space = [](char ch) {
2641 265 : return isspace(static_cast<unsigned char>(ch));
2642 265 : };
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 21 : ASSERT (str != NULL);
2655 21 : const char * s = str;
2656 21 : int count = 0;
2657 :
2658 21 : if (firstCall) {
2659 1 : firstCall = 0;
2660 :
2661 : // Set up pieceFromByte[]:
2662 1 : for (int i=0; i < 256; i++) { pieceFromByte[i] = EMPTY; }
2663 1 : pieceFromByte [(int) 'K'] = WK; pieceFromByte [(int) 'k'] = BK;
2664 1 : pieceFromByte [(int) 'Q'] = WQ; pieceFromByte [(int) 'q'] = BQ;
2665 1 : pieceFromByte [(int) 'R'] = WR; pieceFromByte [(int) 'r'] = BR;
2666 1 : pieceFromByte [(int) 'B'] = WB; pieceFromByte [(int) 'b'] = BB;
2667 1 : pieceFromByte [(int) 'N'] = WN; pieceFromByte [(int) 'n'] = BN;
2668 1 : pieceFromByte [(int) 'P'] = WP; pieceFromByte [(int) 'p'] = BP;
2669 :
2670 : // Set up fenSqToRealSq[]:
2671 65 : for (int sq=0; sq < 64; sq++) {
2672 64 : fenSqToRealSquare [sq] = (squareT)((7 - (sq)/8) * 8 + ((sq) % 8));
2673 : }
2674 : }
2675 :
2676 21 : Clear ();
2677 1645 : while (count < 64) {
2678 812 : 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 147 : if (count % 8) { return ERROR_InvalidFEN; }
2683 :
2684 665 : } else if (*s > '0' && *s < '9') {
2685 303 : count += (*s - '0');
2686 :
2687 : } else {
2688 362 : pieceT p = pieceFromByte [(byte) *s];
2689 362 : if (p == EMPTY) { return ERROR_InvalidFEN; }
2690 362 : if (AddPiece (p, fenSqToRealSquare[count]) != OK) {
2691 0 : return ERROR_InvalidFEN;
2692 : }
2693 362 : count++;
2694 : }
2695 812 : s++;
2696 : }
2697 21 : if (Material[WK] != 1 || Material[BK] != 1) { return ERROR_InvalidFEN; }
2698 :
2699 : // Now the side to move:
2700 42 : while (is_space(*s)) { s++; }
2701 21 : switch (*s) {
2702 11 : case 'w':
2703 11 : SetToMove (WHITE);
2704 11 : break;
2705 10 : case 'b':
2706 10 : SetToMove (BLACK);
2707 10 : break;
2708 0 : default:
2709 0 : return ERROR_InvalidFEN;
2710 : }
2711 21 : s++;
2712 :
2713 21 : if (! IsLegal()) { return ERROR_InvalidFEN; }
2714 :
2715 : // Now the castling flags:
2716 41 : while (is_space(*s)) { s++; }
2717 21 : if (*s == '-') {
2718 15 : s++; // do nothing
2719 6 : } 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 1 : if (Board[E1] == WK) {
2724 1 : if (Board[A1] == WR) { SetCastling (WHITE, QSIDE, true); }
2725 1 : if (Board[H1] == WR) { SetCastling (WHITE, KSIDE, true); }
2726 : }
2727 1 : if (Board[E8] == BK) {
2728 0 : if (Board[A8] == BR) { SetCastling (BLACK, QSIDE, true); }
2729 0 : if (Board[H8] == BR) { SetCastling (BLACK, KSIDE, true); }
2730 : }
2731 : } else {
2732 33 : while (!is_space(*s) && *s != 0) {
2733 14 : switch (*s) {
2734 5 : case 'Q':
2735 5 : SetCastling (WHITE, QSIDE, true);
2736 5 : break;
2737 2 : case 'q':
2738 2 : SetCastling (BLACK, QSIDE, true);
2739 2 : break;
2740 5 : case 'K':
2741 5 : SetCastling (WHITE, KSIDE, true);
2742 5 : break;
2743 2 : case 'k':
2744 2 : SetCastling (BLACK, KSIDE, true);
2745 2 : break;
2746 0 : default:
2747 0 : return ERROR_InvalidFEN;
2748 : }
2749 14 : s++;
2750 : }
2751 : }
2752 :
2753 : // Now the EP target:
2754 41 : while (is_space(*s)) { s++; }
2755 21 : if (*s == 0) {
2756 : // do nothing
2757 20 : } else if (*s == '-') {
2758 19 : EPTarget = NULL_SQUARE;
2759 19 : s++; // No EP target
2760 : } else {
2761 1 : char fylec = *s; s++;
2762 1 : if (fylec < 'a' || fylec > 'h') {
2763 0 : return ERROR_InvalidFEN;
2764 : }
2765 1 : char rankc = *s; s++;
2766 1 : if (rankc != '3' && rankc != '6') {
2767 0 : return ERROR_InvalidFEN;
2768 : }
2769 1 : EPTarget = square_Make(fyle_FromChar(fylec), rank_FromChar(rankc));
2770 : }
2771 :
2772 : // Now the capture/pawn halfmove clock:
2773 40 : while (is_space(*s)) { s++; }
2774 21 : if (*s) {
2775 19 : HalfMoveClock = (ushort) atoi(s);
2776 : }
2777 42 : while (!is_space(*s) && *s != 0) { s++; }
2778 :
2779 : // Finally, the fullmove counter:
2780 40 : while (is_space(*s)) { s++; }
2781 21 : if (*s) {
2782 19 : int i = atoi(s);
2783 19 : if (i >= 1) {
2784 17 : PlyCounter = (i - 1) * 2;
2785 : }
2786 : }
2787 21 : if (ToMove == BLACK) { PlyCounter++; }
2788 21 : 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 : //
2804 1129 : void Position::PrintFEN(char* str, uint flags) const {
2805 1129 : ASSERT (str != NULL);
2806 : uint emptyRun, iRank, iFyle;
2807 10161 : for (iRank = 0; iRank < 8; iRank++) {
2808 9032 : const pieceT* pBoard = &(Board[(7 - iRank) * 8]);
2809 9032 : emptyRun = 0;
2810 9032 : if (iRank > 0 && flags > FEN_COMPACT) { *str++ = '/'; }
2811 81288 : for (iFyle = 0; iFyle < 8; iFyle++, pBoard++) {
2812 72256 : if (*pBoard != EMPTY) {
2813 26512 : if (emptyRun) { *str++ = (byte) emptyRun + '0'; }
2814 26512 : emptyRun = 0;
2815 26512 : *str++ = PIECE_CHAR[*pBoard];
2816 : } else {
2817 45744 : emptyRun++;
2818 : }
2819 : }
2820 9032 : if (emptyRun) { *str++ = (byte) emptyRun + '0'; }
2821 : }
2822 :
2823 1129 : if (flags > FEN_COMPACT) { *str++ = ' '; }
2824 1129 : *str++ = (ToMove == WHITE ? 'w' : 'b');
2825 1129 : *str = 0;
2826 :
2827 1129 : if (flags >= FEN_CASTLING_EP) {
2828 : // Add the castling flags and EP flag as well:
2829 1129 : *str++ = ' ';
2830 1129 : if (Castling == 0) {
2831 158 : *str++ = '-';
2832 : } else {
2833 971 : if (GetCastling (WHITE, KSIDE)) { *str++ = 'K'; }
2834 971 : if (GetCastling (WHITE, QSIDE)) { *str++ = 'Q'; }
2835 971 : if (GetCastling (BLACK, KSIDE)) { *str++ = 'k'; }
2836 971 : if (GetCastling (BLACK, QSIDE)) { *str++ = 'q'; }
2837 : }
2838 1129 : *str++ = ' ';
2839 :
2840 : // Now the EP target square:
2841 1129 : if (EPTarget == NULL_SQUARE) {
2842 1129 : *str++ = '-';
2843 : } else {
2844 0 : *str++ = square_FyleChar (EPTarget);
2845 0 : *str++ = square_RankChar (EPTarget);
2846 : }
2847 1129 : *str = 0;
2848 :
2849 1129 : if (flags >= FEN_ALL_FIELDS) {
2850 : // Also print the Halfmove and ply counters:
2851 1125 : *str++ = ' ';
2852 1125 : sprintf (str, "%d %d", HalfMoveClock, (PlyCounter / 2) + 1);
2853 : }
2854 : }
2855 1129 : 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
2879 0 : Position::DumpHtmlBoard (DString * dstr, uint style, const char * dir, bool flip)
2880 : {
2881 0 : const uint HTML_DIAG_STYLES = 2;
2882 : htmlStyleT hs [HTML_DIAG_STYLES];
2883 0 : hs[0].dir = "bitmaps"; hs[0].width = 40; hs[0].height = 40;
2884 0 : hs[1].dir = "bitmaps2"; hs[1].width = 36; hs[1].height = 35;
2885 0 : if (style >= HTML_DIAG_STYLES) { style = 0; }
2886 :
2887 0 : uint width = hs[style].width;
2888 0 : uint height = hs[style].height;
2889 : uint iRank, iFyle;
2890 : pieceT * pBoard;
2891 0 : if (dir == NULL) { dir = hs[style].dir; }
2892 :
2893 0 : dstr->Append ("<br><br><center>\n");
2894 0 : dstr->Append ("<table Border=1 CellSpacing=0 CellPadding=0>\n");
2895 0 : for (iRank = 0; iRank < 8; iRank++) {
2896 0 : dstr->Append ("<tr>\n");
2897 0 : pBoard = &(Board[(7 - iRank) * 8]);
2898 0 : for (iFyle = 0; iFyle < 8; iFyle++, pBoard++) {
2899 0 : pieceT piece = *pBoard;
2900 0 : if (flip) { piece = Board[iRank * 8 + (7 - iFyle)]; }
2901 0 : dstr->Append (" <td><img border=0 ");
2902 0 : if (width > 0) {
2903 : char temp[40];
2904 0 : sprintf (temp, "width=%u ", width);
2905 0 : dstr->Append (temp);
2906 : }
2907 0 : if (height > 0) {
2908 : char temp[40];
2909 0 : sprintf (temp, "height=%u ", height);
2910 0 : dstr->Append (temp);
2911 : }
2912 0 : dstr->Append ("src=\"");
2913 0 : dstr->Append (dir);
2914 0 : dstr->AddChar ('/');
2915 0 : bool lightSq = ((iRank % 2) == (iFyle % 2));
2916 0 : if (lightSq) {
2917 0 : dstr->AddChar ('w');
2918 : } else {
2919 0 : dstr->AddChar ('b');
2920 : }
2921 0 : if (piece == EMPTY) {
2922 0 : dstr->Append ("sq.gif");
2923 : } else {
2924 0 : colorT c = piece_Color(piece);
2925 0 : dstr->AddChar (c == WHITE ? 'w' : 'b');
2926 0 : dstr->AddChar (tolower (PIECE_CHAR[piece]));
2927 0 : dstr->Append (".gif");
2928 : }
2929 0 : dstr->Append ("\" alt=\"");
2930 0 : if (piece == EMPTY) {
2931 0 : if (! lightSq) { dstr->Append ("::"); }
2932 : } else {
2933 0 : colorT c = piece_Color(piece);
2934 0 : dstr->AddChar (c == WHITE ? 'W' : 'B');
2935 0 : dstr->AddChar (toupper (PIECE_CHAR[piece]));
2936 : }
2937 0 : dstr->Append ("\"></td>\n");
2938 : }
2939 0 : dstr->Append ("</tr>\n");
2940 : }
2941 0 : 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 0 : dstr->Append("</center><br>");
2948 0 : }
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
2955 0 : Position::DumpLatexBoard (DString * dstr, bool flip)
2956 : {
2957 : uint iRank, iFyle;
2958 : pieceT * pBoard;
2959 0 : dstr->Append ("\\board{");
2960 0 : for (iRank = 0; iRank < 8; iRank++) {
2961 0 : pBoard = &(Board[(7 - iRank) * 8]);
2962 0 : for (iFyle = 0; iFyle < 8; iFyle++, pBoard++) {
2963 0 : pieceT piece = *pBoard;
2964 0 : if (flip) { piece = Board[iRank * 8 + (7 - iFyle)]; }
2965 0 : if (piece != EMPTY) {
2966 0 : dstr->AddChar (PIECE_CHAR[piece]);
2967 : } else { // put a space or a '*':
2968 0 : dstr->AddChar (((iRank % 2) == (iFyle % 2)) ? ' ' : '*');
2969 : }
2970 : }
2971 0 : if (iRank < 7) {
2972 0 : dstr->Append ("}\n {");
2973 0 : } else { dstr->AddChar ('}'); }
2974 : }
2975 0 : }
2976 :
2977 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2978 : // Position::Compare():
2979 : // Compare another position with this one.
2980 : //
2981 : sint
2982 0 : Position::Compare (Position * p)
2983 : {
2984 0 : int i = 32;
2985 : byte *p1, *p2;
2986 0 : p1 = Board;
2987 0 : p2 = p->Board;
2988 0 : while (i && *p1 == *p2) {
2989 0 : i--; p1++; p2++;
2990 : }
2991 0 : if (p1 < p2) { return -1; }
2992 0 : if (p1 > p2) { return 1; }
2993 0 : 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
3001 0 : Position::GetSquares (pieceT piece, SquareList * sqlist)
3002 : {
3003 0 : colorT color = piece_Color(piece);
3004 0 : squareT * squares = GetList(color);
3005 0 : uint npieces = GetCount(color);
3006 0 : for (uint i=0; i < npieces; i++) {
3007 0 : squareT sq = squares[i];
3008 0 : pieceT p = Board[sq];
3009 0 : if (p == piece) { sqlist->Add (sq); }
3010 : }
3011 0 : 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
3019 0 : randomSquare (void) { return rand() % 64; }
3020 :
3021 : inline squareT
3022 0 : randomPawnSquare (void) { return (rand() % 48) + A2; }
3023 :
3024 : errorT
3025 0 : Position::Random (const char * material)
3026 : {
3027 : pieceT pieces [32]; // List of pieces excluding kings
3028 0 : uint nPieces[2] = {0, 0}; // Number of pieces per side excluding kings.
3029 0 : uint total = 0; // Total number of pieces excluding kings.
3030 :
3031 0 : colorT side = WHITE;
3032 :
3033 : // The material string must start with a king:
3034 0 : if (toupper(*material) != 'K') { return ERROR_Corrupt; }
3035 0 : material++;
3036 :
3037 : // Read the material string:
3038 : while (1) {
3039 0 : char ch = toupper(*material);
3040 0 : if (ch == 0) { break; }
3041 0 : switch (ch) {
3042 0 : case 'K':
3043 0 : if (side == BLACK) { return ERROR_Corrupt; } // Seen third king!
3044 0 : side = BLACK;
3045 0 : break;
3046 0 : case 'Q': case 'R': case 'B': case 'N': case 'P':
3047 0 : if (nPieces[side] >= 15) { return ERROR_Corrupt; }
3048 0 : nPieces[side]++;
3049 0 : if (ch == 'P') {
3050 0 : pieces[total] = piece_Make (side, PAWN);
3051 : } else {
3052 0 : pieces[total] = piece_Make (side, piece_FromChar(ch));
3053 : }
3054 0 : total++;
3055 0 : break;
3056 0 : case ' ': case '-': case '.': case ',': case ':':
3057 : // Ignore spaces, commas, etc:
3058 0 : break;
3059 0 : default:
3060 0 : return ERROR_Corrupt;
3061 : }
3062 0 : material++;
3063 0 : }
3064 0 : if (side != BLACK) { return ERROR_Corrupt; } // Never saw Black king!
3065 :
3066 : // Generate two non-adjacent king squares:
3067 0 : squareT wk = randomSquare();
3068 0 : squareT bk = randomSquare();
3069 0 : 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 0 : Clear();
3075 0 : ToMove = (rand() % 2) ? WHITE : BLACK;
3076 0 : AddPiece (WK, wk);
3077 0 : AddPiece (BK, bk);
3078 :
3079 0 : for (uint i=0; i < total; i++) {
3080 : squareT sq;
3081 0 : pieceT p = pieces[i];
3082 0 : bool isPawn = (piece_Type(p) == PAWN);
3083 : while (1) {
3084 0 : sq = isPawn ? randomPawnSquare() : randomSquare();
3085 0 : if (Board[sq] == EMPTY) { break; }
3086 : }
3087 : // Add this piece on the random empty square:
3088 0 : 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 0 : squareT enemyKing = (ToMove == WHITE) ? bk : wk;
3094 0 : if (CalcAttacks (ToMove, enemyKing, NULL) == 0) { break; }
3095 0 : }
3096 0 : return OK;
3097 3 : }
3098 :
3099 : //////////////////////////////////////////////////////////////////////
3100 : // EOF: position.cpp
3101 : //////////////////////////////////////////////////////////////////////
|