3 Copyright (c) Scott Gasch
11 Functions to make and unmake moves.
19 $Id: move.c 345 2007-12-02 22:56:42Z scott $
26 SlidePiece(POSITION *pos, COOR cFrom, COOR cTo)
31 Slide a piece (i.e. not a pawn) from cFrom to cTo. This is much
32 faster than LiftPiece / PlacePiece.
36 POSITION *pos : the board
37 COOR cFrom : square moving from
38 COOR cTo : square moving to
51 VerifyPositionConsistency(pos, FALSE);
52 ASSERT(IS_ON_BOARD(cFrom));
53 ASSERT(IS_ON_BOARD(cTo));
54 ASSERT(IS_EMPTY(pos->rgSquare[cTo].pPiece));
56 p = pos->rgSquare[cFrom].pPiece;
57 pos->rgSquare[cFrom].pPiece = EMPTY;
59 ASSERT(IS_VALID_PIECE(p));
60 uIndex = pos->rgSquare[cFrom].uIndex;
62 ASSERT(IS_VALID_COLOR(c));
63 ASSERT(pos->cNonPawns[c][uIndex] == cFrom);
64 pos->cNonPawns[c][uIndex] = cTo;
65 pos->u64NonPawnSig ^= g_u64SigSeeds[cFrom][PIECE_TYPE(p)][c];
66 pos->u64NonPawnSig ^= g_u64SigSeeds[cTo][PIECE_TYPE(p)][c];
68 if (IS_BISHOP(p) && (IS_SQUARE_WHITE(cFrom)))
70 ASSERT(IS_SQUARE_WHITE(cTo));
73 pos->rgSquare[cTo].pPiece = p;
74 pos->rgSquare[cTo].uIndex = uIndex;
76 pos->rgSquare[cFrom].uIndex = INVALID_PIECE_INDEX;
77 VerifyPositionConsistency(pos, FALSE);
83 SlidePawn(POSITION *pos, COOR cFrom, COOR cTo)
88 Slide a pawn (i.e. not a piece) from cFrom to cTo. This is much
89 faster than LiftPiece / PlacePiece.
93 POSITION *pos : the board
94 COOR cFrom : square moving from
95 COOR cTo : square moving to
108 VerifyPositionConsistency(pos, FALSE);
109 ASSERT(IS_ON_BOARD(cFrom));
110 ASSERT(IS_ON_BOARD(cTo));
111 ASSERT(IS_EMPTY(pos->rgSquare[cTo].pPiece));
113 p = pos->rgSquare[cFrom].pPiece;
114 pos->rgSquare[cFrom].pPiece = EMPTY;
116 ASSERT(IS_VALID_PIECE(p));
117 uIndex = pos->rgSquare[cFrom].uIndex;
119 ASSERT(IS_VALID_COLOR(c));
120 ASSERT(pos->cPawns[c][uIndex] == cFrom);
121 pos->cPawns[c][uIndex] = cTo;
122 pos->u64PawnSig ^= g_u64PawnSigSeeds[cFrom][c];
123 pos->u64PawnSig ^= g_u64PawnSigSeeds[cTo][c];
124 pos->rgSquare[cTo].pPiece = p;
125 pos->rgSquare[cTo].uIndex = uIndex;
127 pos->rgSquare[cFrom].uIndex = INVALID_PIECE_INDEX;
128 VerifyPositionConsistency(pos, FALSE);
134 SlidePieceWithoutSigs(POSITION *pos, COOR cFrom, COOR cTo)
139 Slide a piece (i.e. not a pawn) from cFrom to cTo. This is way
140 faster than LiftPiece / PlacePiece. Also note that this version
141 of the routine doesn't maintain the consistency of the position
142 signatures and should therefore only be called from UnmakeMove.
146 POSITION *pos : the board
147 COOR cFrom : square moving from
148 COOR cTo : square moving to
161 ASSERT(IS_ON_BOARD(cFrom));
162 ASSERT(IS_ON_BOARD(cTo));
163 ASSERT(IS_EMPTY(pos->rgSquare[cTo].pPiece));
165 p = pos->rgSquare[cFrom].pPiece;
166 pos->rgSquare[cFrom].pPiece = EMPTY;
167 ASSERT(IS_VALID_PIECE(p));
169 uIndex = pos->rgSquare[cFrom].uIndex;
171 ASSERT(IS_VALID_COLOR(c));
172 ASSERT(pos->cNonPawns[c][uIndex] == cFrom);
173 pos->cNonPawns[c][uIndex] = cTo;
174 pos->rgSquare[cTo].pPiece = p;
175 pos->rgSquare[cTo].uIndex = uIndex;
177 pos->rgSquare[cFrom].uIndex = INVALID_PIECE_INDEX;
183 SlidePawnWithoutSigs(POSITION *pos, COOR cFrom, COOR cTo)
188 Slide a pawn (i.e. not a piece) from cFrom to cTo. This is way
189 faster than LiftPiece / PlacePiece. Also note that this version
190 of the routine doesn't maintain the consistency of the position
191 signatures and should therefore only be called from UnmakeMove.
195 POSITION *pos : the board
196 COOR cFrom : square moving from
197 COOR cTo : square moving to
210 ASSERT(IS_ON_BOARD(cFrom));
211 ASSERT(IS_ON_BOARD(cTo));
212 ASSERT(IS_EMPTY(pos->rgSquare[cTo].pPiece));
214 p = pos->rgSquare[cFrom].pPiece;
215 pos->rgSquare[cFrom].pPiece = EMPTY;
216 ASSERT(IS_VALID_PIECE(p));
218 uIndex = pos->rgSquare[cFrom].uIndex;
220 ASSERT(IS_VALID_COLOR(c));
221 ASSERT(pos->cPawns[c][uIndex] == cFrom);
222 pos->cPawns[c][uIndex] = cTo;
223 pos->rgSquare[cTo].pPiece = p;
224 pos->rgSquare[cTo].uIndex = uIndex;
226 pos->rgSquare[cFrom].uIndex = INVALID_PIECE_INDEX;
232 LiftPiece(POSITION *pos, COOR cSquare)
237 Remove the piece at square cSquare from the board. Update POSITION
238 (piece lists, counts, material, etc...) accordingly.
251 register PIECE pLifted;
259 VerifyPositionConsistency(pos, FALSE);
260 ASSERT(IS_ON_BOARD(cSquare));
262 pLifted = pos->rgSquare[cSquare].pPiece;
263 if (IS_EMPTY(pLifted))
265 return(pLifted); // ok to return an empty square
267 pos->rgSquare[cSquare].pPiece = EMPTY;
268 ASSERT(IS_VALID_PIECE(pLifted));
269 ASSERT(!IS_KING(pLifted));
270 uIndex = pos->rgSquare[cSquare].uIndex;
272 ASSERT(IS_VALID_PIECE_INDEX(uIndex));
273 pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX;
275 color = GET_COLOR(pLifted);
276 ASSERT(IS_VALID_COLOR(color));
277 pv = PIECE_VALUE(pLifted);
279 pos->iMaterialBalance[color] -= pv;
280 pos->iMaterialBalance[FLIP(color)] += pv;
281 ASSERT(pos->iMaterialBalance[WHITE] * -1 == pos->iMaterialBalance[BLACK]);
283 if (IS_PAWN(pLifted))
285 ASSERT(pos->cPawns[color][uIndex] == cSquare);
286 ASSERT(pv == VALUE_PAWN);
288 pos->uPawnMaterial[color] -= pv;
289 ASSERT(pos->uPawnMaterial[color] <= (7 * VALUE_PAWN));
291 pos->u64PawnSig ^= g_u64PawnSigSeeds[cSquare][color];
294 // Remove this pawn from the pawn list.
296 pos->uPawnCount[color]--;
297 ASSERT(pos->uPawnCount[color] < 8);
298 uLastIndex = pos->uPawnCount[color];
301 // Assume that the pawn being lifted is not the last pawn on
302 // the list. We can't have a hole in the list so swap the
303 // last pawn on the list into this guy's spot.
305 c = pos->cPawns[color][uLastIndex];
306 pos->rgSquare[c].uIndex = uIndex;
307 pos->cPawns[color][uIndex] = c;
309 pos->cPawns[color][uLastIndex] = ILLEGAL_COOR;
315 // Piece is not a pawn
317 ASSERT(pos->cNonPawns[color][uIndex] == cSquare);
318 ASSERT(pos->uNonPawnMaterial[color] >= VALUE_KING);
319 ASSERT(pv > VALUE_PAWN);
320 pos->uNonPawnMaterial[color] -= pv;
321 ASSERT(pos->uNonPawnMaterial[color] <= VALUE_MAX_ARMY);
324 // Remove this piece from the list.
326 pos->uNonPawnCount[color][0]--;
327 ASSERT(pos->uNonPawnCount[color] > 0);
328 uLastIndex = pos->uNonPawnCount[color][0]; // optimized...
330 // Assume that it is NOT the last piece in the list ... swap
331 // the last piece into this one's spot.
333 c = pos->cNonPawns[color][uLastIndex];
334 pos->rgSquare[c].uIndex = uIndex;
335 pos->cNonPawns[color][uIndex] = c;
337 pos->cNonPawns[color][uLastIndex] = ILLEGAL_COOR;
341 // Change per-piece counters.
343 u = PIECE_TYPE(pLifted);
344 ASSERT((u >= KNIGHT) && (u < KING));
345 pos->u64NonPawnSig ^= g_u64SigSeeds[cSquare][u][color];
346 pos->uNonPawnCount[color][u]--;
347 ASSERT(pos->uNonPawnCount[color][u] <= 9);
348 pos->uWhiteSqBishopCount[color] -= (IS_BISHOP(pLifted) &
349 IS_SQUARE_WHITE(cSquare));
350 ASSERT(pos->uWhiteSqBishopCount[color] <= 9);
354 VerifyPositionConsistency(pos, FALSE);
361 LiftPieceWithoutSigs(POSITION *pos, COOR cSquare)
366 Remove the piece at square cSquare from the board. Update POSITION
367 (piece lists, counts, material, etc...) accordingly.
369 Note: LiftPieceWithoutSigs can _only_ be called from UnmakeMove.
370 It is used to unmake a previously made move and therefore does
371 not worry about updating position signatures. UnmakeMove has
372 the pre-move signature in a history list and will klobber the
373 position signature soon anyway.
386 register PIECE pLifted;
394 ASSERT(IS_ON_BOARD(cSquare));
395 pLifted = pos->rgSquare[cSquare].pPiece;
396 if (IS_EMPTY(pLifted))
398 return(pLifted); // ok to return an empty square
400 pos->rgSquare[cSquare].pPiece = EMPTY;
401 ASSERT(IS_VALID_PIECE(pLifted));
402 ASSERT(!IS_KING(pLifted));
404 uIndex = pos->rgSquare[cSquare].uIndex;
406 ASSERT(IS_VALID_PIECE_INDEX(uIndex));
407 pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX;
409 color = GET_COLOR(pLifted);
410 ASSERT(IS_VALID_COLOR(color));
411 pv = PIECE_VALUE(pLifted);
413 pos->iMaterialBalance[color] -= pv;
414 pos->iMaterialBalance[FLIP(color)] += pv;
415 ASSERT(pos->iMaterialBalance[WHITE] * -1 == pos->iMaterialBalance[BLACK]);
417 if (IS_PAWN(pLifted))
419 ASSERT(pos->cPawns[color][uIndex] == cSquare);
420 ASSERT(pv == VALUE_PAWN);
422 pos->uPawnMaterial[color] -= pv;
423 ASSERT(pos->uPawnMaterial[color] <= (7 * VALUE_PAWN));
426 // Remove this pawn from the pawn list.
428 pos->uPawnCount[color]--;
429 ASSERT(pos->uPawnCount[color] < 8);
430 uLastIndex = pos->uPawnCount[color]; // optimized...
433 // Assume that the pawn being lifted is not the last pawn on
434 // the list. We can't have a hole in the list so swap the
435 // last pawn on the list into this guy's spot.
437 c = pos->cPawns[color][uLastIndex];
438 pos->rgSquare[c].uIndex = uIndex;
439 pos->cPawns[color][uIndex] = c;
441 pos->cPawns[color][uLastIndex] = ILLEGAL_COOR;
447 // Piece is not a pawn
449 ASSERT(pos->cNonPawns[color][uIndex] == cSquare);
450 ASSERT(pos->uNonPawnMaterial[color] >= VALUE_KING);
451 ASSERT(pv > VALUE_PAWN);
452 pos->uNonPawnMaterial[color] -= pv;
453 ASSERT(pos->uNonPawnMaterial[color] <= VALUE_MAX_ARMY);
456 // Remove this piece from the list.
458 pos->uNonPawnCount[color][0]--;
459 ASSERT(pos->uNonPawnCount[color] > 0);
460 uLastIndex = pos->uNonPawnCount[color][0]; // optimized...
463 // Assume that it is NOT the last piece in the list ... swap
464 // the last piece into this one's spot.
466 c = pos->cNonPawns[color][uLastIndex];
467 pos->rgSquare[c].uIndex = uIndex;
468 pos->cNonPawns[color][uIndex] = c;
470 pos->cNonPawns[color][uLastIndex] = ILLEGAL_COOR;
474 // Change per-piece counters.
476 u = PIECE_TYPE(pLifted);
477 ASSERT((u >= KNIGHT) && (u < KING));
478 pos->uNonPawnCount[color][u]--;
479 ASSERT(pos->uNonPawnCount[color][u] <= 9);
480 pos->uWhiteSqBishopCount[color] -= (IS_BISHOP(pLifted) &
481 IS_SQUARE_WHITE(cSquare));
482 ASSERT(pos->uWhiteSqBishopCount[color] <= 9);
490 PlacePiece(POSITION *pos, COOR cSquare, PIECE pPiece)
495 Place a piece back on the board. Link it into the right piece list.
509 register ULONG color = GET_COLOR(pPiece);
510 register ULONG uIndex;
515 VerifyPositionConsistency(pos, FALSE);
516 ASSERT(IS_EMPTY(pos->rgSquare[cSquare].pPiece));
517 ASSERT(!IS_KING(pPiece));
520 pv = PIECE_VALUE(pPiece);
521 pos->iMaterialBalance[color] += pv;
522 pos->iMaterialBalance[FLIP(color)] -= pv;
523 ASSERT(pos->iMaterialBalance[WHITE] * -1 == pos->iMaterialBalance[BLACK]);
530 ASSERT(pv == VALUE_PAWN);
531 pos->uPawnMaterial[color] += pv;
532 ASSERT(pos->uPawnMaterial[color] <= (8 * VALUE_PAWN));
534 uIndex = pos->uPawnCount[color];
535 ASSERT((uIndex >= 0) && (uIndex <= 7));
536 pos->uPawnCount[color]++;
537 ASSERT(pos->uPawnCount[color] <= 8);
538 pos->cPawns[color][uIndex] = cSquare;
539 pos->u64PawnSig ^= g_u64PawnSigSeeds[cSquare][color];
544 // Piece is not a pawn
546 ASSERT(pv > VALUE_PAWN);
547 pos->uNonPawnMaterial[color] += pv;
548 ASSERT(pos->uNonPawnMaterial[color] <= VALUE_MAX_ARMY);
549 uIndex = pos->uNonPawnCount[color][0];
550 pos->uNonPawnCount[color][0]++;
551 ASSERT(pos->uNonPawnCount[color][0] <= 16);
552 pos->cNonPawns[color][uIndex] = cSquare;
554 u = PIECE_TYPE(pPiece);
555 ASSERT((u >= KNIGHT) && (u < KING));
556 pos->u64NonPawnSig ^= g_u64SigSeeds[cSquare][u][color];
557 pos->uNonPawnCount[color][u]++;
558 ASSERT(pos->uNonPawnCount[color][u] <= 10);
560 pos->uWhiteSqBishopCount[color] += (IS_BISHOP(pPiece) &
561 IS_SQUARE_WHITE(cSquare));
562 ASSERT(pos->uWhiteSqBishopCount[color] <= 10);
566 // Place the piece on the board
568 pos->rgSquare[cSquare].pPiece = pPiece;
569 pos->rgSquare[cSquare].uIndex = uIndex;
572 VerifyPositionConsistency(pos, FALSE);
578 PlacePieceWithoutSigs(POSITION *pos, COOR cSquare, PIECE pPiece)
583 Place a piece back on the board. Link it into the right piece list.
585 Note: PlacePieceWithoutSigs can _only_ be called from UnmakeMove.
586 It is used to unmake a previously made move and therefore does
587 not worry about updating position signatures. UnmakeMove has
588 the pre-move signature in a history list and will klobber the
589 position signature soon anyway.
603 register ULONG color = GET_COLOR(pPiece);
604 register ULONG uIndex;
608 ASSERT(IS_EMPTY(pos->rgSquare[cSquare].pPiece));
609 ASSERT(!IS_KING(pPiece));
611 pv = PIECE_VALUE(pPiece);
612 pos->iMaterialBalance[color] += pv;
613 pos->iMaterialBalance[FLIP(color)] -= pv;
614 ASSERT(pos->iMaterialBalance[WHITE] * -1 == pos->iMaterialBalance[BLACK]);
621 ASSERT(pv == VALUE_PAWN);
622 pos->uPawnMaterial[color] += pv;
623 ASSERT(pos->uPawnMaterial[color] <= (8 * VALUE_PAWN));
625 uIndex = pos->uPawnCount[color];
626 ASSERT((uIndex >= 0) && (uIndex <= 7));
627 pos->uPawnCount[color]++;
628 ASSERT(pos->uPawnCount[color] <= 8);
629 pos->cPawns[color][uIndex] = cSquare;
634 // Piece is not a pawn
636 ASSERT(pv > VALUE_PAWN);
637 pos->uNonPawnMaterial[color] += pv;
638 ASSERT(pos->uNonPawnMaterial[color] <= VALUE_MAX_ARMY);
639 uIndex = pos->uNonPawnCount[color][0];
640 pos->uNonPawnCount[color][0]++;
641 ASSERT(pos->uNonPawnCount[color][0] <= 16);
642 pos->cNonPawns[color][uIndex] = cSquare;
644 u = PIECE_TYPE(pPiece);
645 ASSERT((u >= KNIGHT) && (u < KING));
646 pos->uNonPawnCount[color][u]++;
647 ASSERT(pos->uNonPawnCount[color][u] <= 10);
649 pos->uWhiteSqBishopCount[color] += (IS_BISHOP(pPiece) &
650 IS_SQUARE_WHITE(cSquare));
651 ASSERT(pos->uWhiteSqBishopCount[color] <= 10);
655 // Place the piece on the board
657 pos->rgSquare[cSquare].pPiece = pPiece;
658 pos->rgSquare[cSquare].uIndex = uIndex;
663 SaveCurrentPositionHistoryInContext(SEARCHER_THREAD_CONTEXT *ctx,
669 This routine is called just before MakeMove to record some
670 information about the current board position to help unmake the
671 move later. It's also used to detect draws in the search.
673 Note: when this function is called, ply has not been incremented.
677 SEARCHER_THREAD_CONTEXT *ctx
685 ULONG uPly = ctx->uPly;
686 POSITION *pos = &(ctx->sPosition);
687 PLY_INFO *pi = &ctx->sPlyInfo[uPly];
690 // Save data about the position we are leaving.
692 ASSERT(ctx->uPly < MAX_PLY_PER_SEARCH);
693 ASSERT((pos->u64NonPawnSig & 1) == pos->uToMove);
694 pi->u64NonPawnSig = pos->u64NonPawnSig;
695 pi->u64PawnSig = pos->u64PawnSig;
696 pi->u64Sig = (pos->u64NonPawnSig ^ pos->u64PawnSig);
697 pi->uFifty = pos->uFifty;
698 pi->fCastled[BLACK] = pos->fCastled[BLACK];
699 pi->fCastled[WHITE] = pos->fCastled[WHITE];
700 pi->uTotalNonPawns = (pos->uNonPawnCount[BLACK][0] +
701 pos->uNonPawnCount[WHITE][0]);
702 ASSERT(IS_VALID_FLAG(pos->fCastled[WHITE]) &&
703 IS_VALID_FLAG(pos->fCastled[BLACK]));
704 ASSERT((pos->bvCastleInfo & ~CASTLE_ALL_POSSIBLE) == 0);
705 pi->bvCastleInfo = pos->bvCastleInfo;
706 ASSERT(VALID_EP_SQUARE(pos->cEpSquare));
707 pi->cEpSquare = pos->cEpSquare;
708 (pi+1)->fInQsearch = pi->fInQsearch;
712 static ULONG g_uCastleMask[128] =
714 0x7, 0xF, 0xF, 0xF, 0x3, 0xF, 0xF, 0xB, 0,0,0,0,0,0,0,0,
715 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0,0,0,0,0,0,0,0,
716 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0,0,0,0,0,0,0,0,
717 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0,0,0,0,0,0,0,0,
718 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0,0,0,0,0,0,0,0,
719 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0,0,0,0,0,0,0,0,
720 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0,0,0,0,0,0,0,0,
721 0xD, 0xF, 0xF, 0xF, 0xC, 0xF, 0xF, 0xE, 0,0,0,0,0,0,0,0
726 MakeMove(SEARCHER_THREAD_CONTEXT *ctx,
732 Attempts to make MOVE mv on the POSITION in ctx. Returns TRUE if
733 the move was successful, FALSE if it was illegal. If the move was
734 successful, this function affects the POSITION (piece locations,
735 piece lists, castling perms, en-passant square, signatures etc...)
736 and also increments the ply in ctx. These changes can be reverted
737 at a later point by using UnmakeMove.
739 Note: This function only does a very limited amount of legality
740 checking in order to save time because it is designed to take
741 moves from the move generator (which will only make illegal moves
742 in certain, known ways). If the move is coming from the user
743 (and is therefore of dubious legality)) it should be passed to
744 MakeUserMove, not MakeMove. MakeUserMove verifies that it is a
745 legal chess move in every way.
749 SEARCHER_THREAD_CONTEXT *ctx
758 POSITION *pos = &ctx->sPosition;
767 FLAG fMoveIsLegal = FALSE;
768 static int iSign[2] = { -1, +1 };
771 cRookFrom = cRookTo = ILLEGAL_COOR; // Satisfy overzealous compiler
772 VerifyPositionConsistency(pos, FALSE);
773 SanityCheckMove(pos, mv);
774 ASSERT(!InCheck(pos, FLIP(pos->uToMove)));
775 if (InCheck(pos, pos->uToMove))
777 ASSERT(IS_ESCAPING_CHECK(mv));
780 SaveCurrentPositionHistoryInContext(ctx, mv);
788 pCaptured = mv.pCaptured;
789 ASSERT(!IS_EMPTY(pPiece));
790 ASSERT(pPiece == pos->rgSquare[mv.cFrom].pPiece);
791 ASSERT(GET_COLOR(pPiece) == pos->uToMove);
794 // Is this a special move? Special moves are: promotions, enpassant,
795 // castling and double pawn moves.
797 if (IS_SPECIAL_MOVE(mv))
801 pPromoted = mv.pPromoted;
804 ASSERT(!IS_PAWN(pPromoted));
805 ASSERT(GET_COLOR(pPromoted) == GET_COLOR(pPiece));
806 pPiece = LiftPiece(pos, mv.cFrom);
807 pCaptured = LiftPiece(pos, mv.cTo);
808 ASSERT(!pCaptured || (OPPOSITE_COLORS(pCaptured, pPiece)));
809 PlacePiece(pos, mv.cTo, pPromoted);
811 goto update_castle_and_clear_ep;
815 // Is this an enpassant capture?
819 ASSERT(mv.cTo == pos->cEpSquare);
820 pCaptured = LiftPiece(pos, mv.cTo +
821 iSign[GET_COLOR(pPiece)] * 16);
822 ASSERT(IS_PAWN(pCaptured));
823 ASSERT(OPPOSITE_COLORS(pPiece, pCaptured));
824 ASSERT(IS_EMPTY(pos->rgSquare[mv.cTo].pPiece));
825 ASSERT(IS_PAWN(pPiece));
826 SlidePawn(pos, mv.cFrom, mv.cTo);
832 // A special pawn move other than promotion and enpassant
833 // capture must be a double initial jump.
838 c += (iSign[GET_COLOR(pPiece)] * -16);
839 ASSERT(RANK3(c) || RANK6(c));
840 ASSERT(c + (iSign[GET_COLOR(pPiece)] * -16) == mv.cTo);
841 ASSERT(IS_EMPTY(pos->rgSquare[c].pPiece));
842 ASSERT(IS_EMPTY(pos->rgSquare[mv.cTo].pPiece));
843 ASSERT(IS_PAWN(pPiece));
844 SlidePawn(pos, mv.cFrom, mv.cTo);
848 // Only change the position signature to reflect the
849 // possible en-passant if there's an enemy who can
850 // take advantage of it.
852 xPawn = BLACK_PAWN | FLIP(GET_COLOR(pPiece));
853 if (((!IS_ON_BOARD(mv.cTo - 1)) ||
854 (pos->rgSquare[mv.cTo - 1].pPiece != xPawn)) &&
855 ((!IS_ON_BOARD(mv.cTo + 1)) ||
856 (pos->rgSquare[mv.cTo + 1].pPiece != xPawn)))
860 pos->u64NonPawnSig ^= g_u64EpSigSeeds[FILE(pos->cEpSquare)];
861 ASSERT(VALID_EP_SQUARE(c));
863 pos->u64NonPawnSig ^= g_u64EpSigSeeds[FILE(pos->cEpSquare)];
867 else if (IS_KING(pPiece))
869 ASSERT(!IS_ESCAPING_CHECK(mv));
872 // The only special king move is a castle.
877 ASSERT(pos->bvCastleInfo & CASTLE_WHITE_LONG);
878 ASSERT(IS_EMPTY(pos->rgSquare[B1].pPiece) &&
879 IS_EMPTY(pos->rgSquare[C1].pPiece) &&
880 IS_EMPTY(pos->rgSquare[D1].pPiece));
881 if ((IsAttacked(D1, pos, BLACK)) ||
882 (IsAttacked(C1, pos, BLACK)))
888 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
889 pos->bvCastleInfo &= ~(WHITE_CAN_CASTLE);
890 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
891 pos->fCastled[WHITE] = TRUE;
894 ASSERT(pos->bvCastleInfo & CASTLE_WHITE_SHORT);
895 ASSERT(IS_EMPTY(pos->rgSquare[F1].pPiece) &&
896 IS_EMPTY(pos->rgSquare[G1].pPiece));
897 if ((IsAttacked(F1, pos, BLACK)) ||
898 (IsAttacked(G1, pos, BLACK)))
904 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
905 pos->bvCastleInfo &= ~(WHITE_CAN_CASTLE);
906 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
907 pos->fCastled[WHITE] = TRUE;
910 ASSERT(pos->bvCastleInfo & CASTLE_BLACK_LONG);
911 ASSERT(IS_EMPTY(pos->rgSquare[B8].pPiece) &&
912 IS_EMPTY(pos->rgSquare[C8].pPiece) &&
913 IS_EMPTY(pos->rgSquare[D8].pPiece));
914 if ((IsAttacked(D8, pos, WHITE)) ||
915 (IsAttacked(C8, pos, WHITE)))
921 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
922 pos->bvCastleInfo &= ~(BLACK_CAN_CASTLE);
923 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
924 pos->fCastled[BLACK] = TRUE;
927 ASSERT(pos->bvCastleInfo & CASTLE_BLACK_SHORT);
928 ASSERT(IS_EMPTY(pos->rgSquare[F8].pPiece) &&
929 IS_EMPTY(pos->rgSquare[G8].pPiece));
930 if ((IsAttacked(F8, pos, WHITE)) ||
931 (IsAttacked(G8, pos, WHITE)))
937 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
938 pos->bvCastleInfo &= ~(BLACK_CAN_CASTLE);
939 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
940 pos->fCastled[BLACK] = TRUE;
944 cRookFrom = cRookTo = ILLEGAL_COOR;
949 ASSERT(IS_EMPTY(pos->rgSquare[mv.cTo].pPiece));
950 ASSERT(IS_ROOK(pos->rgSquare[cRookFrom].pPiece));
951 ASSERT(IS_EMPTY(pos->rgSquare[cRookTo].pPiece));
952 SlidePiece(pos, mv.cFrom, mv.cTo); // king
953 SlidePiece(pos, cRookFrom, cRookTo); // rook
967 // Normal move or normal capture
973 pCaptured = LiftPiece(pos, mv.cTo);
974 ASSERT(!IS_KING(pCaptured));
975 ASSERT(!IS_EMPTY(pCaptured));
976 ASSERT(OPPOSITE_COLORS(pCaptured, pPiece));
981 SlidePawn(pos, mv.cFrom, mv.cTo);
985 SlidePiece(pos, mv.cFrom, mv.cTo);
988 update_castle_and_clear_ep:
989 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
990 pos->bvCastleInfo &= g_uCastleMask[mv.cFrom];
991 pos->bvCastleInfo &= g_uCastleMask[mv.cTo];
992 pos->u64NonPawnSig ^= g_u64CastleSigSeeds[pos->bvCastleInfo];
995 if (pos->cEpSquare != ILLEGAL_COOR)
997 pos->u64NonPawnSig ^= g_u64EpSigSeeds[FILE(pos->cEpSquare)];
998 pos->cEpSquare = ILLEGAL_COOR;
999 pos->u64NonPawnSig ^= g_u64EpSigSeeds[FILE(pos->cEpSquare)];
1004 // At this point we have made the move on the board (moved pieces,
1005 // updated castling permissions, updated 50 move count, updated
1006 // signature with respect to piece location etc...)
1008 // Now we switch the side on move and flip a bit in the signature
1009 // to indicate that it's the other side's turn now. We also
1010 // record this position in the movelist (for later detection of
1011 // repetition of position draws) and increment either the ply or
1014 pos->u64NonPawnSig ^= 1;
1015 pos->uToMove = FLIP(pos->uToMove);
1016 ASSERT((pos->u64NonPawnSig & 1) == pos->uToMove);
1017 ASSERT(ctx->uPly < MAX_PLY_PER_SEARCH - 1);
1018 ctx->sPlyInfo[ctx->uPly].mv = mv;
1020 ASSERT(ctx->uPly < MAX_PLY_PER_SEARCH);
1023 // Make sure the move doesn't leave his own king in check.
1027 cKing = pos->cNonPawns[FLIP(pos->uToMove)][0];
1028 ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
1030 if (IS_KING(mv.pMoved) || IS_ESCAPING_CHECK(mv))
1033 // If this guy just moved his king OR he was in check
1034 // before making this move we need to do a full check
1035 // check now... this involves using the attack vector
1036 // table to test ALL pieces in the enemy army and see if
1037 // they are attacking the just-moved king.
1039 ASSERT(GET_COLOR(mv.pMoved) == FLIP(pos->uToMove));
1040 if (InCheck(pos, GET_COLOR(mv.pMoved)))
1047 // else he did not move the king and he was not in check
1048 // before he made this move. To determine if he is in check
1049 // now should be cheaper because all we have to consider is
1050 // whether the move he just made exposed his king to check.
1055 // This call will also catch positions like:
1057 // 3K4/8/8/8/1k1Pp1R1/8/8/8 w - d3 0 1
1059 // ... where the black pawn capturing on d3 exposes his
1060 // own king to the white rook's horizontal attack.
1061 // Because the pawn captured en passant has already been
1064 if (IS_ON_BOARD(ExposesCheck(pos, mv.cFrom, cKing)))
1066 ASSERT(InCheck(pos, GET_COLOR(mv.pMoved)));
1071 // This is a special case -- if the move was enpassant the
1072 // guy might have exposed his king to check by removing
1073 // the enemy pawn (i.e. exposes a diagonal attack on his
1076 if (IS_ENPASSANT(mv))
1078 ASSERT(IS_SPECIAL_MOVE(mv));
1079 ASSERT(!IS_PROMOTION(mv));
1080 ASSERT(mv.pCaptured);
1081 ASSERT(IS_PAWN(mv.pMoved));
1082 ASSERT(IS_PAWN(mv.pCaptured));
1083 if (IS_ON_BOARD(ExposesCheck(pos,
1084 ((pos->uToMove == WHITE) ?
1089 ASSERT(InCheck(pos, GET_COLOR(mv.pMoved)));
1094 ASSERT(IS_EMPTY(pos->rgSquare[mv.cFrom].pPiece));
1095 ASSERT(!IS_EMPTY(pos->rgSquare[mv.cTo].pPiece));
1096 ASSERT((pos->rgSquare[mv.cTo].pPiece == mv.pMoved) ||
1097 (pos->rgSquare[mv.cTo].pPiece == mv.pPromoted));
1099 ASSERT(!InCheck(pos, GET_COLOR(mv.pMoved)));
1100 fMoveIsLegal = TRUE;
1102 VerifyPositionConsistency(pos, FALSE);
1105 // Fall into illegal... but don't worry: we're legal!
1109 return(fMoveIsLegal);
1113 // If we goto here it means that the guy is trying to move into
1114 // check. We didn't determine this until after we already made
1115 // the whole move so we can't just return false (illegal move)
1116 // from there. First we have to unmake the move we just made.
1118 ASSERT(InCheck(pos, FLIP(pos->uToMove)));
1119 UnmakeMove(ctx, mv);
1121 VerifyPositionConsistency(pos, FALSE);
1128 MakeUserMove(SEARCHER_THREAD_CONTEXT *ctx,
1132 Routine description:
1134 This function should be called to make a move of dubious quality.
1135 The engine calls MakeMove directly for moves it generated itself
1136 because these are _mostly_ legal. The only exceptions being:
1138 1. moves that expose the friendly king to check
1139 2. castles through check
1141 This function is intended for moves from a user or ICS. It
1142 screens these moves by first generating available moves at this
1143 ply and making sure it looks like one of them. It returns
1144 TRUE if the move is legal and was played, FALSE otherwise.
1146 Both moves made with MakeMove and MakeUserMove can be unmade
1151 SEARCHER_THREAD_CONTEXT *ctx,
1160 POSITION *pos = &ctx->sPosition;
1161 FLAG fInCheck = InCheck(pos, pos->uToMove);
1167 // Ask the generator to give us a list of moves here
1170 GenerateMoves(ctx, mv, (fInCheck ? GENERATE_ESCAPES : GENERATE_ALL_MOVES));
1171 for (x = ctx->sMoveStack.uBegin[ctx->uPly];
1172 x < ctx->sMoveStack.uEnd[ctx->uPly];
1175 mv = ctx->sMoveStack.mvf[x].mv;
1176 if (IS_SAME_MOVE(mvUser, mv))
1178 if (TRUE == MakeMove(ctx, mv))
1192 UnmakeMove(SEARCHER_THREAD_CONTEXT *ctx,
1196 Routine description:
1198 Reverses a move made by MakeMove.
1202 SEARCHER_THREAD_CONTEXT *ctx,
1214 POSITION *pos = &(ctx->sPosition);
1215 static int iSign[2] = { -1, +1 };
1218 VerifyPositionConsistency(pos, FALSE);
1219 ASSERT(IS_ON_BOARD(mv.cTo));
1220 ASSERT(IS_ON_BOARD(mv.cFrom));
1224 goto restore_saved_position_data;
1228 pCaptured = mv.pCaptured;
1231 ASSERT(IS_KING(pPiece));
1232 ASSERT(IS_SPECIAL_MOVE(mv));
1238 // No need to call SlidePiece here, the signatures will
1239 // get reset to their pre-move state below
1241 SlidePieceWithoutSigs(pos, D1, A1); // rook
1242 SlidePieceWithoutSigs(pos, C1, E1); // king
1243 ASSERT(IS_KING(pos->rgSquare[E1].pPiece));
1244 ASSERT(IS_ROOK(pos->rgSquare[A1].pPiece));
1247 SlidePieceWithoutSigs(pos, F1, H1); // rook
1248 SlidePieceWithoutSigs(pos, G1, E1); // king
1249 ASSERT(IS_KING(pos->rgSquare[E1].pPiece));
1250 ASSERT(IS_ROOK(pos->rgSquare[H1].pPiece));
1253 SlidePieceWithoutSigs(pos, D8, A8); // rook
1254 SlidePieceWithoutSigs(pos, C8, E8); // king
1255 ASSERT(IS_KING(pos->rgSquare[E8].pPiece));
1256 ASSERT(IS_ROOK(pos->rgSquare[A8].pPiece));
1259 SlidePieceWithoutSigs(pos, F8, H8); // rook
1260 SlidePieceWithoutSigs(pos, G8, E8); // king
1261 ASSERT(IS_KING(pos->rgSquare[E8].pPiece));
1262 ASSERT(IS_ROOK(pos->rgSquare[H8].pPiece));
1273 if (IS_PROMOTION(mv))
1275 ASSERT(IS_SPECIAL_MOVE(mv));
1276 ASSERT(mv.pPromoted);
1279 // LiftPiece/PlacePiece must be done here to maintain
1280 // piece list integrity.
1282 (void)LiftPieceWithoutSigs(pos, mv.cTo);
1285 ASSERT(pPiece == mv.pMoved);
1286 PlacePieceWithoutSigs(pos, mv.cFrom, pPiece);
1290 if (IS_PAWN(pPiece))
1292 SlidePawnWithoutSigs(pos, mv.cTo, mv.cFrom);
1296 SlidePieceWithoutSigs(pos, mv.cTo, mv.cFrom);
1303 // If this was an enpassant capture, put the pawn back to the
1306 if (IS_ENPASSANT(mv))
1308 ASSERT(IS_SPECIAL_MOVE(mv));
1309 ASSERT(IS_PAWN(mv.pMoved));
1310 ASSERT(IS_PAWN(pCaptured));
1311 ASSERT(IS_ON_BOARD(mv.cTo) + 16);
1312 ASSERT(IS_ON_BOARD(mv.cTo) - 16);
1315 // Resurrect the dead piece in the enpassant location
1316 // where it died (not the same as where the killer moved).
1318 ASSERT(IS_EMPTY(pos->rgSquare[mv.cTo + 16 *
1319 iSign[GET_COLOR(mv.pMoved)]].pPiece));
1320 PlacePieceWithoutSigs(pos,
1321 mv.cTo + 16 * iSign[GET_COLOR(mv.pMoved)],
1327 // Normal capture... resurrect the dead piece where the
1330 ASSERT(IS_EMPTY(pos->rgSquare[mv.cTo].pPiece));
1331 PlacePieceWithoutSigs(pos, mv.cTo, pCaptured);
1336 restore_saved_position_data:
1338 ASSERT(ctx->uPly < MAX_PLY_PER_SEARCH);
1340 pi = &ctx->sPlyInfo[ctx->uPly];
1341 pos->u64NonPawnSig = pi->u64NonPawnSig;
1342 pos->u64PawnSig = pi->u64PawnSig;
1343 ASSERT((pos->u64NonPawnSig & WHITE) == FLIP(pos->uToMove));
1344 pos->uFifty = pi->uFifty;
1345 pos->fCastled[WHITE] = pi->fCastled[WHITE];
1346 pos->fCastled[BLACK] = pi->fCastled[BLACK];
1347 ASSERT((pi->bvCastleInfo & ~CASTLE_ALL_POSSIBLE) == 0);
1348 pos->bvCastleInfo = pi->bvCastleInfo;
1349 ASSERT(VALID_EP_SQUARE(pi->cEpSquare));
1350 ASSERT(VALID_EP_SQUARE(pos->cEpSquare));
1351 pos->cEpSquare = pi->cEpSquare;
1352 pos->uToMove = FLIP(pos->uToMove);
1354 VerifyPositionConsistency(pos, FALSE);
1360 _MoveFlagsToString(BITV bv)
1363 Routine description:
1365 Support routing for DumpMove. Translates bvFlags component of a
1366 MOVE into a nice string. Not thread safe.
1381 if (bv & MOVE_FLAG_SPECIAL)
1385 if (bv & MOVE_FLAG_ESCAPING_CHECK)
1389 if (bv & MOVE_FLAG_CHECKING)
1393 if (bv & MOVE_FLAG_KILLERMATE)
1406 Routine description:
1408 Dump a move on stdout. This function takes a ULONG instead of a
1409 MOVE becasue both gdb and msdev do not like to pass unions to
1410 functions invoked at debugtime.
1425 Trace("--------------------------------------------------------------------------------\n"
1427 " +0x000 uMove : 0x%x\n"
1428 " +0x000 cFrom <pos 0, 8 bits>: 0x%x (%s)\n",
1430 mv.cFrom, CoorToString(mv.cFrom));
1431 Trace(" +0x001 cTo <pos 0, 8 bits>: 0x%x (%s)\n"
1432 " +0x002 pMoved <pos 0, 4 Bits>: 0x%x (%s)\n",
1433 mv.cTo, CoorToString(mv.cTo),
1434 mv.pMoved, PieceAbbrev(mv.pMoved));
1435 Trace(" +0x002 pCaptured <pos 4, 4 Bits>: 0x%x (%s)\n",
1436 mv.pCaptured, PieceAbbrev(mv.pCaptured));
1437 Trace(" +0x003 pPromoted <pos 0, 4 Bits>: 0x%x (%s)\n",
1438 mv.pPromoted, PieceAbbrev(mv.pPromoted));
1439 Trace(" +0x003 bvFlags <pos 4, 4 Bits>: 0x%x (%s)\n",
1440 mv.bvFlags, _MoveFlagsToString(mv.bvFlags));