3 Copyright (c) Scott Gasch
11 This module contains two move generators: a pseudo legal move
12 generator and a pseudo legal escaping move generator. The former
13 is called in positions where side on move is not in check and the
14 latter is called when the side on move is in check. Note: both of
15 these generators produce pseudo-legal moves. The normal move
16 generator is pretty bad: it does not bother to see if moves expose
17 their own king to check or if castles pass through check
18 etc... Instead it relies on MakeMove to throw out any illegal
19 moves it generates. The escape generator is better in that it
20 produces less illegal moves. But it is still possible to generate
21 some with it. An example: it will generate replies to check that
22 may include capturing moves that are illegal because the capturing
23 piece is pinned to the (already in check) king. Both generators
24 produce a set of moves that contains the true legal set of moves in
25 a position as a subset; if a move is legal it will be produced.
27 This module also contains some code to flag moves as checking
28 moves and score moves after they are generated.
36 $Id: generate.c 345 2007-12-02 22:56:42Z scott $
44 // Note: this order is important because of how EvalQueen works
46 const INT g_iQKDeltas[] = {
47 -17, -16, -15, -1, +1, +17, +15, +16, 0 };
50 _FindUnblockedSquares(IN MOVE_STACK *pStack,
56 Given a position, find, count and mark all squares that are
57 unblocked for the king of the side not on move. For example:
59 +---+---+---+---+- - - The squares marked with *'s in this
60 |***| K |***|*Q*| diagram are unblocked for the K. As
61 +---+---+---+---+- - - you can see, there are nine of them.
63 +---+---+---+---+- - - Note the piece at the end of an unblocked
64 | |***| |*P*| ray from the king is on an unblocked
65 +---+---+---+---+- - - square (and its color doesn't matter)
67 +---+---+---+---+- - - This stuff is used to flag checking moves
68 | | | | | as they are generated.
71 Also note: this is one of the most called routines in the engine;
72 speed is of the essence here.
76 MOVE_STACK *pStack : move stack pointer
77 POSITION *pos : position pointer
86 COOR cKing = pos->cNonPawns[FLIP(pos->uToMove)][0];
88 ULONG uPly = pStack->uPly;
90 PIECE pKing = pos->rgSquare[cKing].pPiece;
94 ASSERT(IS_ON_BOARD(cKing));
95 ASSERT(IS_KING(pKing));
96 ASSERT(GET_COLOR(pKing) != pos->uToMove);
100 // Adjust the unblocked key value for this ply...
102 pStack->uUnblockedKeyValue[uPly]++;
106 if (!IS_ON_BOARD(c)) continue;
107 ASSERT(pStack->sUnblocked[uPly][c].uKey !=
108 pStack->uUnblockedKeyValue[uPly]);
113 ASSERT(g_iQKDeltas[u] != 0);
116 c = cKing + g_iQKDeltas[u];
117 while(IS_ON_BOARD(c))
119 pStack->sUnblocked[uPly][c].uKey =
120 pStack->uUnblockedKeyValue[uPly];
121 pStack->sUnblocked[uPly][c].iPointer = -1 * g_iQKDeltas[u];
123 ASSERT(-g_iQKDeltas[u] == DIRECTION_BETWEEN_SQUARES(c,cKing));
124 ASSERT(pStack->sUnblocked[uPly][c].iPointer != 0);
128 cx += pStack->sUnblocked[uPly][c].iPointer;
130 while(IS_ON_BOARD(cx) && (IS_EMPTY(pos->rgSquare[cx].pPiece)));
134 if (!IS_EMPTY(pos->rgSquare[c].pPiece))
142 while(g_iQKDeltas[u] != 0);
147 #define SQUARE_IS_UNBLOCKED(c) (uUnblocked == kp[(c)].uKey)
148 #define DIR_FROM_SQ_TO_KING(c) (kp[(c)].iPointer)
151 WouldGiveCheck(IN SEARCHER_THREAD_CONTEXT *ctx,
157 Determine whether a move is a checking move or not. For this
158 function to work properly _FindUnblockedSquares must have been
159 called at the start of generation.
161 Note: this is one of the most called codepaths in the engine,
162 speed is of the essence here.
166 MOVE_STACK *pStack : the move stack
167 POSITION *pos : the board
168 MOVE mv : the move under consideration
172 FLAG : TRUE if the move is checking, FALSE otherwise
177 // TODO: consider adding a hash table to see if sig+mv would
178 // give check. Is this worth it?
180 MOVE_STACK *pStack = &(ctx->sMoveStack);
181 POSITION *pos = &(ctx->sPosition);
183 COOR cFrom = mv.cFrom;
184 PIECE pPiece = mv.pMoved;
185 COOR xKing = pos->cNonPawns[FLIP(GET_COLOR(pPiece))][0];
186 KEY_POINTER *kp = &(pStack->sUnblocked[ctx->uPly][0]);
187 ULONG uUnblocked = pStack->uUnblockedKeyValue[ctx->uPly];
191 ASSERT(ctx->uPly < MAX_PLY_PER_SEARCH);
192 ASSERT(PositionsAreEquivalent(&(pStack->board[ctx->uPly]), pos));
194 ASSERT(IS_KING(pos->rgSquare[xKing].pPiece));
195 ASSERT(IS_ON_BOARD(cTo));
196 ASSERT(IS_ON_BOARD(cFrom));
200 // Phase 1: Does this move directly attack the king?
204 // You can't attack one king directly with the other except by
205 // castling (in which case the rook is doing the attack).
209 if (!IS_SPECIAL_MOVE(mv))
214 ASSERT((mv.cFrom == E8) || (mv.cFrom == E1));
216 ASSERT(!IS_KING(pPiece));
217 ASSERT(IS_ROOK(pPiece));
218 ASSERT(pPiece == (BLACK_ROOK | GET_COLOR(pPiece)));
224 if (SQUARE_IS_UNBLOCKED(E1) &&
225 (DIR_FROM_SQ_TO_KING(E1) == 1))
227 ASSERT(RANK1(xKing));
228 return(MOVE_FLAG_CHECKING);
233 if (SQUARE_IS_UNBLOCKED(E1) &&
234 (DIR_FROM_SQ_TO_KING(E1) == -1))
236 ASSERT(RANK1(xKing));
237 return(MOVE_FLAG_CHECKING);
242 if (SQUARE_IS_UNBLOCKED(E8) &&
243 (DIR_FROM_SQ_TO_KING(E8) == 1))
245 ASSERT(RANK8(xKing));
246 return(MOVE_FLAG_CHECKING);
251 if (SQUARE_IS_UNBLOCKED(E8) &&
252 (DIR_FROM_SQ_TO_KING(E8) == -1))
254 ASSERT(RANK8(xKing));
255 return(MOVE_FLAG_CHECKING);
260 UtilPanic(SHOULD_NOT_GET_HERE,
261 NULL, NULL, NULL, NULL,
269 // Ok, either its not a king or its a castle move and we are thinking
270 // about the rook now.
274 // Check the attack vector table
276 ASSERT(!IS_KING(pPiece));
280 ASSERT(IS_PAWN(pPiece));
281 ASSERT(GET_COLOR(pPiece) == GET_COLOR(mv.pPromoted));
282 pPiece = mv.pPromoted;
283 ASSERT(!IS_PAWN(pPiece));
284 iDelta = cFrom - cTo;
289 // Does pPiece attack xKing directly if there are not pieces in
292 if (0 != (CHECK_VECTOR_WITH_INDEX((int)cTo - (int)xKing,
294 (1 << PIECE_TYPE(pPiece))))
297 // We don't toggle knight squares as unblocked... so if the sq
298 // is unblocked then piece must not be a knight. Note: this
299 // logical OR replaced with bitwise equivalent for speed.
301 if ((SQUARE_IS_UNBLOCKED(cTo)) | (IS_KNIGHT(pPiece)))
304 if (SQUARE_IS_UNBLOCKED(cTo))
306 ASSERT(!IS_KNIGHT(pPiece));
310 ASSERT(IS_KNIGHT(pPiece));
313 return(MOVE_FLAG_CHECKING);
315 ASSERT(!IS_PAWN(pPiece));
318 // Special case: if this is a pawn that just promoted, and the
319 // from square was unblocked, this pawn in its new state can
322 if ((DIR_FROM_SQ_TO_KING(cFrom) == iDelta) &&
323 (SQUARE_IS_UNBLOCKED(cFrom)))
325 return(MOVE_FLAG_CHECKING);
331 // We now know that the move itself does not directly attack the
332 // enemy king. We will now see if that move exposes check to the
337 // A move cannot expose check directly if its from square is not
338 // an unblocked square. But if it is unblocked, we will have to
339 // scan behind the piece to see if there is some attacker.
341 if (SQUARE_IS_UNBLOCKED(cFrom))
344 // Piece moves towards the king on the same ray? Cannot be
345 // check. Piece moves away from the king on the same ray?
348 if (DIRECTION_BETWEEN_SQUARES(cTo, xKing) !=
349 DIR_FROM_SQ_TO_KING(cFrom))
351 if (IS_ON_BOARD(FasterExposesCheck(pos, cFrom, xKing)))
353 ASSERT(IS_ON_BOARD(ExposesCheck(pos, cFrom, xKing)));
354 return(MOVE_FLAG_CHECKING);
360 // There is one last special case. If this move was enpassant
361 // it's possible that the removal of the enemy pawn has exposed
362 // the enemy king to check.
364 // rnb2bnr/ppp3pp/4k3/1B1qPpB1/4p1Q1/8/PPP2PPP/RN2K1NR w - f6 0 0
365 // rn5r/pp4pp/8/5qk1/1b2pP2/4K3/PPP3PP/RN4NR b - f3 0 0
367 if (IS_ENPASSANT(mv))
369 ASSERT(IS_PAWN(pPiece));
370 ASSERT(VALID_EP_SQUARE(cTo));
371 ASSERT(cTo == pos->cEpSquare);
372 cEpSquare = cTo + 16 * g_iBehind[(GET_COLOR(pPiece))];
374 if (GET_COLOR(pPiece))
376 ASSERT(cEpSquare == (cTo + 16));
380 ASSERT(cEpSquare == (cTo - 16));
383 ASSERT(IS_PAWN(pos->rgSquare[cEpSquare].pPiece));
384 ASSERT(OPPOSITE_COLORS(pPiece, pos->rgSquare[cEpSquare].pPiece));
387 // Logical OR replaced with bitwise for speed.
389 if (((SQUARE_IS_UNBLOCKED(cEpSquare)) |
390 (SQUARE_IS_UNBLOCKED(cFrom))) &&
391 (IS_ON_BOARD(ExposesCheckEp(pos, cEpSquare, cFrom, cTo, xKing))))
393 return(MOVE_FLAG_CHECKING);
398 // No direct check + no exposed check = no check.
404 static void FORCEINLINE
405 _AddNormalMove(IN MOVE_STACK *pStack,
414 Adds a move from the generator functions to the stack of generated
419 MOVE_STACK *pStack : the move stack
420 POSITION *pos : the board position
421 COOR cFrom : the move's from square
422 COOR cTo : the move's to square
423 PIECE pCap : what was captured (if anything)
427 static void FORCEINLINE
431 PIECE pMoved = pos->rgSquare[cFrom].pPiece;
432 ULONG uPly = pStack->uPly;
433 MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
436 ASSERT(GET_COLOR(pMoved) == pos->uToMove);
437 ASSERT(IS_ON_BOARD(cFrom));
438 ASSERT(IS_ON_BOARD(cTo));
440 pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
441 pMvf->mv.uMove = MAKE_MOVE_WITH_NO_PROM_OR_FLAGS(cFrom, cTo, pMoved, pCap);
442 pStack->uEnd[uPly]++;
444 ASSERT(pMvf->mv.uMove == MAKE_MOVE(cFrom, cTo, pMoved, pCap, 0, 0));
445 ASSERT(SanityCheckMove(pos, pMvf->mv));
449 static void FORCEINLINE
450 _AddEnPassant(IN MOVE_STACK *pStack,
458 Add an en passant pawn capture to the move stack.
462 MOVE_STACK *pStack : the move stack
463 POSITION *pos : the board position
464 COOR cFrom : the from square
465 COOR cTo : the to square (and en passant square)
473 PIECE pMoved = BLACK_PAWN | pos->uToMove;
474 PIECE pCaptured = FLIP(pMoved);
475 ULONG uPly = pStack->uPly;
476 MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
478 ASSERT(pCaptured == (BLACK_PAWN | (FLIP(pos->uToMove))));
479 ASSERT(IS_ON_BOARD(cFrom));
480 ASSERT(IS_ON_BOARD(cTo));
481 ASSERT(RANK4(cFrom) || RANK5(cFrom));
482 ASSERT(VALID_EP_SQUARE(cTo));
483 ASSERT(IS_PAWN(pos->rgSquare[cFrom].pPiece));
484 ASSERT(GET_COLOR(pos->rgSquare[cFrom].pPiece) == pos->uToMove);
486 pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
488 MAKE_MOVE(cFrom, cTo, pMoved, pCaptured, 0, MOVE_FLAG_SPECIAL);
489 pStack->uEnd[uPly]++;
491 ASSERT(SanityCheckMove(pos, pMvf->mv));
495 static void FORCEINLINE
496 _AddCastle(IN MOVE_STACK *pStack,
504 Add a castle move to the move stack.
515 static void FORCEINLINE
519 PIECE pMoved = BLACK_KING | pos->uToMove;
520 ULONG uPly = pStack->uPly;
521 MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
523 ASSERT(IS_ON_BOARD(cFrom));
524 ASSERT(IS_ON_BOARD(cTo));
525 ASSERT((E1 == cFrom) || (E8 == cFrom));
526 ASSERT(IS_KING(pos->rgSquare[cFrom].pPiece));
527 ASSERT(GET_COLOR(pos->rgSquare[cFrom].pPiece) == pos->uToMove);
529 pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
530 pMvf->mv.uMove = MAKE_MOVE(cFrom, cTo, pMoved, 0, 0, MOVE_FLAG_SPECIAL);
531 pStack->uEnd[uPly]++;
533 ASSERT(SanityCheckMove(pos, pMvf->mv));
537 static void FORCEINLINE
538 _AddPromote(IN MOVE_STACK *pStack,
546 Adds a pawn promotion (set of) moves to the move stack. This can
547 be a capture-promotion or non-capture promotion. This function
548 pushes several moves onto the move stack, one for each possible
553 MOVE_STACK *pStack : the move stack
554 POSITION *pos : the board position
555 COOR cFrom : the move's from square
556 COOR cTo : the move's to square
564 PIECE pMoved = BLACK_PAWN | pos->uToMove;
565 PIECE pCaptured = pos->rgSquare[cTo].pPiece;
566 ULONG uPly = pStack->uPly;
567 MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
570 #define ARRAY_LENGTH_TARG (4)
571 const static PIECE pTarg[ARRAY_LENGTH_TARG] = { BLACK_QUEEN,
576 ASSERT(IS_ON_BOARD(cFrom));
577 ASSERT(IS_ON_BOARD(cTo));
578 ASSERT(RANK1(cTo) || RANK8(cTo));
579 ASSERT(RANK7(cFrom) || RANK2(cFrom));
580 ASSERT(ARRAY_LENGTH(pTarg) == ARRAY_LENGTH_TARG);
581 ASSERT(IS_PAWN(pos->rgSquare[cFrom].pPiece));
583 for (u = 0; u < ARRAY_LENGTH_TARG; u++)
585 pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
586 pMvf->mv.uMove = MAKE_MOVE(cFrom, cTo, pMoved, pCaptured,
587 pTarg[u] | pos->uToMove, MOVE_FLAG_SPECIAL);
588 pStack->uEnd[uPly]++;
589 ASSERT(SanityCheckMove(pos, pMvf->mv));
591 #undef ARRAY_LENGTH_TARG
595 static void FORCEINLINE
596 _AddDoubleJump(IN MOVE_STACK *pStack,
604 This function adds a pawn double jump to the move stack.
608 MOVE_STACK *pStack : the move stack
609 POSITION *pos : the board position
610 COOR cFrom : the move's from square
611 COOR cTo : the move's to square
619 PIECE pMoved = BLACK_PAWN | pos->uToMove;
620 ULONG uPly = pStack->uPly;
621 MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
623 ASSERT(IS_ON_BOARD(cFrom));
624 ASSERT(IS_ON_BOARD(cTo));
625 ASSERT(RANK2(cFrom) || RANK7(cFrom));
626 ASSERT(RANK4(cTo) || RANK5(cTo));
627 ASSERT(IS_PAWN(pos->rgSquare[cFrom].pPiece));
629 pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
630 pMvf->mv.uMove = MAKE_MOVE(cFrom, cTo, pMoved, 0, 0, MOVE_FLAG_SPECIAL);
631 pStack->uEnd[uPly]++;
633 ASSERT(SanityCheckMove(pos, pMvf->mv));
637 const INT g_iNDeltas[] = {
638 -33, -31, -18, -14, +14, +18, +31, +33, 0 };
641 GenerateKnight(IN MOVE_STACK *pStack,
648 This function is called by GenerateMoves' JumpTable. Its job
649 is to generate and push pseudo-legal knight moves for the
650 knight sitting on square cKnight.
654 MOVE_STACK *pStack : the move stack
655 POSITION *pos : the board position
656 COOR cKnight : the knight's location
668 ASSERT(IS_KNIGHT(pos->rgSquare[cKnight].pPiece));
669 ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == pos->uToMove);
670 ASSERT(g_iNDeltas[u] != 0);
674 c = cKnight + g_iNDeltas[u];
678 p = pos->rgSquare[c].pPiece;
681 // This logical OR replaced with bitwise OR; the effect is
682 // the same the the bitwise is marginally faster.
684 if (IS_EMPTY(p) | (OPPOSITE_COLORS(p, pos->uToMove)))
686 _AddNormalMove(pStack, pos, cKnight, c, p);
690 while(0 != g_iNDeltas[u]);
694 GenerateWhiteKnight(IN MOVE_STACK *pStack,
702 ASSERT(IS_KNIGHT(pos->rgSquare[cKnight].pPiece));
703 ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == pos->uToMove);
704 ASSERT(g_iNDeltas[u] != 0);
708 c = cKnight + g_iNDeltas[u];
712 p = pos->rgSquare[c].pPiece;
715 // Note: this test covers empty squares also -- it is just
716 // testing that the low order bit (color bit) of p is zero
717 // which is true for black pieces and empty squares. This
718 // is done for speed over readability.
720 if (GET_COLOR(p) == BLACK)
722 ASSERT(IS_EMPTY(p) || (GET_COLOR(p) == BLACK));
723 _AddNormalMove(pStack, pos, cKnight, c, p);
727 while(0 != g_iNDeltas[u]);
731 // These logical AND/ORs replaced with bitwise AND/OR; the effect is
732 // the same the the bitwise is marginally faster.
734 #define BLOCKS_THE_CHECK(sq) \
735 ((iAttackDelta != 0) & \
736 (iAttackDelta == DIRECTION_BETWEEN_SQUARES(cAttacker, (sq))) & \
737 (((cAttacker > cKing) & ((sq) > cKing)) | \
738 ((cAttacker < cKing) & ((sq) < cKing))))
741 SaveMeKnight(IN MOVE_STACK *pStack,
750 This routine is called by GenerateEscapes' JumpTable. Its job is
751 to generate moves by the knight on square cKnight that alleviate
752 check to the king on square cKing. The (lone) piece attacking the
753 king is on square cAttacker.
757 MOVE_STACK *pStack : the move stack
758 POSITION *pos : the board position
759 COOR cKnight : the location of the knight
760 COOR cKing : the location of the friendly king
761 COOR cAttacker : the location of the checking piece
770 int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
774 ASSERT(InCheck(pos, pos->uToMove));
775 ASSERT(IS_KNIGHT(pos->rgSquare[cKnight].pPiece));
776 ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == pos->uToMove);
777 ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) ==
778 GET_COLOR(pos->rgSquare[cKing].pPiece));
779 ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
780 pos->rgSquare[cKing].pPiece));
781 ASSERT(g_iNDeltas[u] != 0);
785 c = cKnight + g_iNDeltas[u];
789 if ((c == cAttacker) || BLOCKS_THE_CHECK(c))
791 cExposed = ExposesCheck(pos, cKnight, cKing);
792 if (!IS_ON_BOARD(cExposed) || (cExposed == c))
794 p = pos->rgSquare[c].pPiece;
796 OPPOSITE_COLORS(p, pos->rgSquare[cKnight].pPiece));
797 ASSERT(!p || (c == cAttacker));
798 _AddNormalMove(pStack, pos, cKnight, c, p);
803 while(0 != g_iNDeltas[u]);
807 const INT g_iBDeltas[] = {
808 -17, -15, +15, +17, 0 };
811 GenerateBishop(IN MOVE_STACK *pStack,
818 This routine is called by GenerateMoves' JumpTable to generate
819 pseudo-legal bishop moves by the piece at cBishop.
823 MOVE_STACK *pStack : the move stack
824 POSITION *pos : the board position
825 COOR cBishop : the bishop location
836 ASSERT(IS_BISHOP(pos->rgSquare[cBishop].pPiece));
837 ASSERT(GET_COLOR(pos->rgSquare[cBishop].pPiece) == pos->uToMove);
840 // Note: manually unrolled loop
843 while(IS_ON_BOARD(c))
845 p = pos->rgSquare[c].pPiece;
848 _AddNormalMove(pStack, pos, cBishop, c, 0);
852 if (OPPOSITE_COLORS(p, pos->uToMove))
854 _AddNormalMove(pStack, pos, cBishop, c, p);
862 while(IS_ON_BOARD(c))
864 p = pos->rgSquare[c].pPiece;
867 _AddNormalMove(pStack, pos, cBishop, c, 0);
871 if (OPPOSITE_COLORS(p, pos->uToMove))
873 _AddNormalMove(pStack, pos, cBishop, c, p);
881 while(IS_ON_BOARD(c))
883 p = pos->rgSquare[c].pPiece;
886 _AddNormalMove(pStack, pos, cBishop, c, 0);
890 if (OPPOSITE_COLORS(p, pos->uToMove))
892 _AddNormalMove(pStack, pos, cBishop, c, p);
900 while(IS_ON_BOARD(c))
902 p = pos->rgSquare[c].pPiece;
905 _AddNormalMove(pStack, pos, cBishop, c, 0);
909 if (OPPOSITE_COLORS(p, pos->uToMove))
911 _AddNormalMove(pStack, pos, cBishop, c, p);
921 SaveMeBishop(IN MOVE_STACK *pStack,
930 This routine is called by GenerateEscapes' JumpTable in order to
931 generate moves by the bishop at cBishop to alleviate check on its
932 friendly king (at cKing) which is being checked by a piece at
937 MOVE_STACK *pStack : the move stack
938 POSITION *pos : the board position
939 COOR cBishop : the bishop's location
940 COOR cKing : the friendly king's location
941 COOR cAttacker : the checking piece's location
949 int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
955 ASSERT(InCheck(pos, pos->uToMove));
956 ASSERT(IS_BISHOP(pos->rgSquare[cBishop].pPiece));
957 ASSERT(GET_COLOR(pos->rgSquare[cBishop].pPiece) == pos->uToMove);
958 ASSERT(GET_COLOR(pos->rgSquare[cBishop].pPiece) ==
959 GET_COLOR(pos->rgSquare[cKing].pPiece));
960 ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
961 pos->rgSquare[cKing].pPiece));
962 ASSERT(g_iBDeltas[u] != 0);
966 c = cBishop + g_iBDeltas[u];
967 while(IS_ON_BOARD(c))
969 p = pos->rgSquare[c].pPiece;
970 if ((c == cAttacker) || BLOCKS_THE_CHECK(c))
973 OPPOSITE_COLORS(p, pos->rgSquare[cBishop].pPiece));
974 ASSERT(!p || (c == cAttacker));
975 cExposed = ExposesCheck(pos, cBishop, cKing);
976 if (!IS_ON_BOARD(cExposed) || (cExposed == c))
978 _AddNormalMove(pStack, pos, cBishop, c, p);
982 else if (!IS_EMPTY(p))
990 while(0 != g_iBDeltas[u]);
994 const INT g_iRDeltas[] = {
995 -1, +1, +16, -16, 0 };
999 GenerateRook(IN MOVE_STACK *pStack,
1004 Routine description:
1006 This routine is called by GenerateMoves' JumpTable in order to
1007 generate pseudo-legal moves for the rook at position cRook.
1011 MOVE_STACK *pStack : the move stack
1012 POSITION *pos : the board position
1013 COOR cRook : the rook's location
1024 ASSERT(IS_ROOK(pos->rgSquare[cRook].pPiece));
1025 ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == pos->uToMove);
1028 // Note: manually unrolled loop
1031 while(IS_ON_BOARD(c))
1033 p = pos->rgSquare[c].pPiece;
1036 _AddNormalMove(pStack, pos, cRook, c, 0);
1040 if (OPPOSITE_COLORS(p, pos->uToMove))
1042 _AddNormalMove(pStack, pos, cRook, c, p);
1050 while(IS_ON_BOARD(c))
1052 p = pos->rgSquare[c].pPiece;
1055 _AddNormalMove(pStack, pos, cRook, c, 0);
1059 if (OPPOSITE_COLORS(p, pos->uToMove))
1061 _AddNormalMove(pStack, pos, cRook, c, p);
1069 while(IS_ON_BOARD(c))
1071 p = pos->rgSquare[c].pPiece;
1074 _AddNormalMove(pStack, pos, cRook, c, 0);
1078 if (OPPOSITE_COLORS(p, pos->uToMove))
1080 _AddNormalMove(pStack, pos, cRook, c, p);
1088 while(IS_ON_BOARD(c))
1090 p = pos->rgSquare[c].pPiece;
1093 _AddNormalMove(pStack, pos, cRook, c, 0);
1097 if (OPPOSITE_COLORS(p, pos->uToMove))
1099 _AddNormalMove(pStack, pos, cRook, c, p);
1108 SaveMeRook(IN MOVE_STACK *pStack,
1115 Routine description:
1117 This routine is called by GenerateEscapes' JumpTable in order to
1118 generate moves by the rook at location cRook that alleviate check
1119 to the king at cKing. The lone checking piece is at cAttacker.
1123 MOVE_STACK *pStack : the move stack
1124 POSITION *pos : the board position
1125 COOR cRook : the rook's location
1126 COOR cKing : the king's location
1127 COOR cAttacker : the checker's location
1135 int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
1141 ASSERT(InCheck(pos, pos->uToMove));
1142 ASSERT(IS_ROOK(pos->rgSquare[cRook].pPiece));
1143 ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == pos->uToMove);
1144 ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == pos->uToMove);
1145 ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) ==
1146 GET_COLOR(pos->rgSquare[cKing].pPiece));
1147 ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
1148 pos->rgSquare[cKing].pPiece));
1149 ASSERT(g_iRDeltas[u] != 0);
1153 c = cRook + g_iRDeltas[u];
1154 while(IS_ON_BOARD(c))
1156 p = pos->rgSquare[c].pPiece;
1157 if ((c == cAttacker) || BLOCKS_THE_CHECK(c))
1160 OPPOSITE_COLORS(p, pos->rgSquare[cRook].pPiece));
1161 ASSERT(!p || (c == cAttacker));
1162 cExposed = ExposesCheck(pos, cRook, cKing);
1163 if (!IS_ON_BOARD(cExposed) || (cExposed == c))
1165 _AddNormalMove(pStack, pos, cRook, c, p);
1169 else if (!IS_EMPTY(p))
1177 while(0 != g_iRDeltas[u]);
1182 GenerateQueen(IN MOVE_STACK *pStack,
1187 Routine description:
1189 This routine is called by GenerateMoves' JumpTable in order to
1190 generate pseudo-legal moves for the queen at position cQueen.
1194 MOVE_STACK *pStack : the move stack
1195 POSITION *pos : the board position
1196 COOR cQueen : the queen's location
1208 ASSERT(IS_QUEEN(pos->rgSquare[cQueen].pPiece));
1209 ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == pos->uToMove);
1210 ASSERT(g_iQKDeltas[u] != 0);
1214 c = cQueen + g_iQKDeltas[u];
1215 while(IS_ON_BOARD(c))
1217 p = pos->rgSquare[c].pPiece;
1220 _AddNormalMove(pStack, pos, cQueen, c, 0);
1224 if (OPPOSITE_COLORS(p, pos->uToMove))
1226 _AddNormalMove(pStack, pos, cQueen, c, p);
1230 c += g_iQKDeltas[u];
1234 while(0 != g_iQKDeltas[u]);
1239 SaveMeQueen(IN MOVE_STACK *pStack,
1246 Routine description:
1248 This routine is called by code in GenerateEscapes in order to
1249 generate moves by the queen at square cQueen that alleviate check
1250 on the friendly king at square cKing. The lone enemy piece that
1251 is checking the king is sitting on square cAttacker.
1255 MOVE_STACK *pStack : the move stack
1256 POSITION *pos : the board position
1257 COOR cQueen : the queen's location
1258 COOR cKing : the friendly king's location
1259 COOR cAttacker : the attacker's location
1269 int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
1273 ASSERT(InCheck(pos, pos->uToMove));
1274 ASSERT(IS_QUEEN(pos->rgSquare[cQueen].pPiece));
1275 ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == pos->uToMove);
1276 ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == pos->uToMove);
1277 ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) ==
1278 GET_COLOR(pos->rgSquare[cKing].pPiece));
1279 ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
1280 pos->rgSquare[cKing].pPiece));
1281 ASSERT(g_iQKDeltas[u] != 0);
1285 c = cQueen + g_iQKDeltas[u];
1286 while(IS_ON_BOARD(c))
1288 p = pos->rgSquare[c].pPiece;
1289 if ((c == cAttacker) || BLOCKS_THE_CHECK(c))
1292 OPPOSITE_COLORS(p, pos->rgSquare[cQueen].pPiece));
1293 ASSERT(!p || (c == cAttacker));
1294 cExposed = ExposesCheck(pos, cQueen, cKing);
1295 if (!IS_ON_BOARD(cExposed) || (cExposed == c))
1297 _AddNormalMove(pStack, pos, cQueen, c, p);
1301 else if (!IS_EMPTY(p))
1305 c += g_iQKDeltas[u];
1309 while(0 != g_iQKDeltas[u]);
1313 GenerateBlackKing(IN MOVE_STACK *pStack,
1318 Routine description:
1320 This function is called by GenerateMoves in order to generate
1321 pseudo-legal moves for the black king at square cKing. Note: this
1322 function often generates illegal castling moves and relies on the
1323 MakeMove code to decide about the legality of the castle.
1327 MOVE_STACK *pStack : the move stack
1328 POSITION *pos : the board position
1329 COOR cKing : the king location
1341 ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
1342 ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == pos->uToMove);
1343 ASSERT(g_iQKDeltas[u] != 0);
1347 c = cKing + g_iQKDeltas[u];
1351 p = pos->rgSquare[c].pPiece;
1354 // This logical OR replaced with bitwise OR; the effect is
1355 // the same the the bitwise is marginally faster.
1357 if (IS_EMPTY(p) | (GET_COLOR(p)))
1359 _AddNormalMove(pStack, pos, cKing, c, p);
1363 while(0 != g_iQKDeltas[u]);
1365 if ((pos->bvCastleInfo & BLACK_CAN_CASTLE) == 0) return;
1367 p = pos->rgSquare[E8].pPiece;
1369 ASSERT(cKing == E8);
1373 // Note: no castling through check is enforced at the time the
1374 // move is played. This is a "pseudo-legal" move when generated.
1376 if ((pos->bvCastleInfo & CASTLE_BLACK_SHORT) &&
1377 (IS_EMPTY(pos->rgSquare[G8].pPiece)) &&
1378 (IS_EMPTY(pos->rgSquare[F8].pPiece)))
1380 ASSERT(pos->rgSquare[H8].pPiece == BLACK_ROOK);
1381 _AddCastle(pStack, pos, E8, G8);
1384 if ((pos->bvCastleInfo & CASTLE_BLACK_LONG) &&
1385 (IS_EMPTY(pos->rgSquare[C8].pPiece)) &&
1386 (IS_EMPTY(pos->rgSquare[D8].pPiece)) &&
1387 (IS_EMPTY(pos->rgSquare[B8].pPiece)))
1389 ASSERT(pos->rgSquare[A8].pPiece == BLACK_ROOK);
1390 _AddCastle(pStack, pos, E8, C8);
1395 // N.B. There is no SaveYourselfBlackKing or SaveYourselfWhiteKing
1396 // because this functionality is built into phase 1 of
1401 GenerateWhiteKing(IN MOVE_STACK *pStack,
1406 Routine description:
1408 This function is called by GenerateMoves in order to generate
1409 pseudo-legal king moves by the white king at square cKing. Note:
1410 it often generates illegal castling moves and relies on the code
1411 in MakeMove to determine the legality of castles.
1415 MOVE_STACK *pStack : the move stack
1416 POSITION *pos : the board position
1417 COOR cKing : the king location
1429 ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
1430 ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == pos->uToMove);
1431 ASSERT(g_iQKDeltas[u] != 0);
1435 c = cKing + g_iQKDeltas[u];
1439 p = pos->rgSquare[c].pPiece;
1442 // Note: this test covers empty squares also -- it is just
1443 // testing that the low order bit (color bit) of p is zero
1444 // which is true for black pieces and empty squares. This
1445 // is done for speed over readability.
1447 if (GET_COLOR(p) == BLACK)
1449 ASSERT(IS_EMPTY(p) || (GET_COLOR(p) == BLACK));
1450 _AddNormalMove(pStack, pos, cKing, c, p);
1454 while(0 != g_iQKDeltas[u]);
1456 if ((pos->bvCastleInfo & WHITE_CAN_CASTLE) == 0) return;
1458 p = pos->rgSquare[E1].pPiece;
1460 ASSERT(cKing == E1);
1464 // Note: no castling through check is enforced at the time the
1465 // move is played. This is a "pseudo-legal" move when generated.
1467 if ((pos->bvCastleInfo & CASTLE_WHITE_SHORT) &&
1468 (IS_EMPTY(pos->rgSquare[G1].pPiece)) &&
1469 (IS_EMPTY(pos->rgSquare[F1].pPiece)))
1471 ASSERT(pos->rgSquare[H1].pPiece == WHITE_ROOK);
1472 _AddCastle(pStack, pos, E1, G1);
1475 if ((pos->bvCastleInfo & CASTLE_WHITE_LONG) &&
1476 (IS_EMPTY(pos->rgSquare[C1].pPiece)) &&
1477 (IS_EMPTY(pos->rgSquare[B1].pPiece)) &&
1478 (IS_EMPTY(pos->rgSquare[D1].pPiece)))
1480 ASSERT(pos->rgSquare[A1].pPiece == WHITE_ROOK);
1481 _AddCastle(pStack, pos, E1, C1);
1487 GenerateWhitePawn(IN MOVE_STACK *pStack,
1492 Routine description:
1494 This function is called by GenerateMoves in order to generate
1495 pseudo-legal pawn moves by the pawn at cPawn. It handles
1496 en passant captures, double jumps, promotions, etc...
1500 MOVE_STACK *pStack : the move stack
1501 POSITION *pos : the board position
1502 COOR cPawn : the pawn's location
1510 ULONG uRank = RANK(cPawn);
1514 ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece));
1515 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
1516 ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7));
1521 if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
1523 _AddNormalMove(pStack, pos, cPawn, cTo, 0);
1527 if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
1529 _AddDoubleJump(pStack, pos, cPawn, cTo);
1534 if (IS_ON_BOARD(cTo))
1536 if (cTo == pos->cEpSquare)
1538 _AddEnPassant(pStack, pos, cPawn, cTo);
1540 p = pos->rgSquare[cTo].pPiece;
1543 // This logical AND replaced with bitwise AND; the effect
1544 // is the same the the bitwise is marginally faster.
1546 if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK))
1548 _AddNormalMove(pStack, pos, cPawn, cTo, p);
1552 if (IS_ON_BOARD(cTo))
1554 if (cTo == pos->cEpSquare)
1556 _AddEnPassant(pStack, pos, cPawn, cTo);
1558 p = pos->rgSquare[cTo].pPiece;
1561 // This logical AND replaced with bitwise AND; the effect
1562 // is the same the the bitwise is marginally faster.
1564 if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK))
1566 _AddNormalMove(pStack, pos, cPawn, cTo, p);
1572 ASSERT(RANK7(cPawn));
1574 if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
1576 _AddPromote(pStack, pos, cPawn, cTo);
1579 if (IS_ON_BOARD(cTo))
1581 p = pos->rgSquare[cTo].pPiece;
1584 // This logical AND replaced with bitwise AND; the effect
1585 // is the same the the bitwise is marginally faster.
1587 if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK))
1589 _AddPromote(pStack, pos, cPawn, cTo);
1593 if (IS_ON_BOARD(cTo))
1595 p = pos->rgSquare[cTo].pPiece;
1598 // This logical AND replaced with bitwise AND; the effect
1599 // is the same the the bitwise is marginally faster.
1601 if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK))
1603 _AddPromote(pStack, pos, cPawn, cTo);
1611 SaveMeWhitePawn(IN MOVE_STACK *pStack,
1618 Routine description:
1620 This function is called by the GenerateEscapes code in order to
1621 ask for a pawn's help in alleviating check on the king at square
1622 cKing. The lone attacker to said king is on square cAttacker.
1626 MOVE_STACK *pStack : the move stack
1627 POSITION *pos : the board position
1628 COOR cPawn : the pawn's location
1629 COOR cKing : the friendly king's location
1630 COOR cAttacker : the location of the lone checker to the king
1638 ULONG uRank = RANK(cPawn);
1642 int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
1644 ASSERT(InCheck(pos, pos->uToMove));
1645 ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece));
1646 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == WHITE);
1647 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
1648 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
1649 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) ==
1650 GET_COLOR(pos->rgSquare[cKing].pPiece));
1651 ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
1652 pos->rgSquare[cKing].pPiece));
1653 ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7));
1658 if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
1660 if (BLOCKS_THE_CHECK(cTo) &&
1661 !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing)))
1663 _AddNormalMove(pStack, pos, cPawn, cTo, 0);
1669 if (IS_EMPTY(pos->rgSquare[cTo].pPiece) &&
1670 BLOCKS_THE_CHECK(cTo) &&
1671 !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing)))
1673 _AddDoubleJump(pStack, pos, cPawn, cTo);
1679 if (cTo == cAttacker)
1681 cExposed = ExposesCheck(pos, cPawn, cKing);
1682 if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
1684 p = pos->rgSquare[cTo].pPiece;
1685 ASSERT(!IS_EMPTY(p));
1686 ASSERT(GET_COLOR(p) == BLACK);
1687 ASSERT(IS_ON_BOARD(cTo));
1688 _AddNormalMove(pStack, pos, cPawn, cTo, p);
1693 // N.B. There is no possible way to block check with an
1694 // en-passant capture because by definition the last move made
1695 // by the other side was the pawn double jump. So only handle
1696 // if the double jumping enemy pawn is the attacker and we can
1697 // kill it en passant.
1699 if ((cTo == pos->cEpSquare) && (cAttacker == cTo + 16))
1701 _AddEnPassant(pStack, pos, cPawn, cTo);
1705 if (cTo == cAttacker)
1707 cExposed = ExposesCheck(pos, cPawn, cKing);
1708 if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
1710 p = pos->rgSquare[cTo].pPiece;
1711 ASSERT(!IS_EMPTY(p));
1712 ASSERT(GET_COLOR(p) == BLACK);
1713 ASSERT(IS_ON_BOARD(cTo));
1714 _AddNormalMove(pStack, pos, cPawn, cTo, p);
1717 if ((cTo == pos->cEpSquare) && (cAttacker == cTo + 16))
1719 _AddEnPassant(pStack, pos, cPawn, cTo);
1724 ASSERT(RANK7(cPawn));
1726 if (BLOCKS_THE_CHECK(cTo) && IS_EMPTY(pos->rgSquare[cTo].pPiece))
1728 _AddPromote(pStack, pos, cPawn, cTo);
1732 if (cTo == cAttacker)
1734 cExposed = ExposesCheck(pos, cPawn, cKing);
1735 if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
1737 p = pos->rgSquare[cTo].pPiece;
1738 ASSERT(!IS_EMPTY(p));
1739 ASSERT(GET_COLOR(p) == BLACK);
1740 ASSERT(IS_ON_BOARD(cTo));
1741 _AddPromote(pStack, pos, cPawn, cTo);
1746 if (cTo == cAttacker)
1748 cExposed = ExposesCheck(pos, cPawn, cKing);
1749 if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
1751 p = pos->rgSquare[cTo].pPiece;
1752 ASSERT(!IS_EMPTY(p));
1753 ASSERT(GET_COLOR(p) == BLACK);
1754 ASSERT(IS_ON_BOARD(cTo));
1755 _AddPromote(pStack, pos, cPawn, cTo);
1763 GenerateBlackPawn(IN MOVE_STACK *pStack,
1768 Routine description:
1770 This code is called by GenerateMoves in order to handle move
1771 generation for the black pawn on square cPawn. It handles
1772 en passant captures, double jumps, and promotion.
1776 MOVE_STACK *pStack : the move stack
1777 POSITION *pos : the position
1778 COOR cPawn : the pawn location
1786 ULONG uRank = RANK(cPawn);
1790 ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece));
1791 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
1792 ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7));
1797 if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
1799 _AddNormalMove(pStack, pos, cPawn, cTo, 0);
1803 if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
1805 _AddDoubleJump(pStack, pos, cPawn, cTo);
1810 if (IS_ON_BOARD(cTo))
1812 if (cTo == pos->cEpSquare)
1814 _AddEnPassant(pStack, pos, cPawn, cTo);
1816 p = pos->rgSquare[cTo].pPiece;
1819 // This logical AND replaced with bitwise AND; the effect
1820 // is the same the the bitwise is marginally faster.
1822 if (!IS_EMPTY(p) & (GET_COLOR(p)))
1824 _AddNormalMove(pStack, pos, cPawn, cTo, p);
1828 if (IS_ON_BOARD(cTo))
1830 if (cTo == pos->cEpSquare)
1832 _AddEnPassant(pStack, pos, cPawn, cTo);
1834 p = pos->rgSquare[cTo].pPiece;
1837 // This logical AND replaced with bitwise AND; the effect
1838 // is the same the the bitwise is marginally faster.
1840 if (!IS_EMPTY(p) & (GET_COLOR(p)))
1842 _AddNormalMove(pStack, pos, cPawn, cTo, p);
1848 ASSERT(RANK2(cPawn));
1850 if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
1852 _AddPromote(pStack, pos, cPawn, cTo);
1855 if (IS_ON_BOARD(cTo))
1857 p = pos->rgSquare[cTo].pPiece;
1860 // This logical AND replaced with bitwise AND; the effect
1861 // is the same the the bitwise is marginally faster.
1863 if (!IS_EMPTY(p) & (GET_COLOR(p)))
1865 _AddPromote(pStack, pos, cPawn, cTo);
1869 if (IS_ON_BOARD(cTo))
1871 p = pos->rgSquare[cTo].pPiece;
1874 // This logical AND replaced with bitwise AND; the effect
1875 // is the same the the bitwise is marginally faster.
1877 if (!IS_EMPTY(p) & (GET_COLOR(p)))
1879 _AddPromote(pStack, pos, cPawn, cTo);
1887 SaveMeBlackPawn(IN MOVE_STACK *pStack,
1894 Routine description:
1896 This code is called to ask for a black pawn's help in alleviating
1897 check to its king. The pawn is on square cPawn. The friendly
1898 king is on square cKing. The lone enemy piece attacking it is on
1903 MOVE_STACK *pStack : the move stack
1904 POSITION *pos : the board position
1905 COOR cPawn : the pawn location
1906 COOR cKing : the friendly king location
1907 COOR cAttacker : the lone checker location
1915 ULONG uRank = RANK(cPawn);
1919 int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
1921 ASSERT(InCheck(pos, pos->uToMove));
1922 ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece));
1923 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == BLACK);
1924 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
1925 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
1926 ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) ==
1927 GET_COLOR(pos->rgSquare[cKing].pPiece));
1928 ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
1929 pos->rgSquare[cKing].pPiece));
1930 ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7));
1935 if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
1937 if (BLOCKS_THE_CHECK(cTo) &&
1938 !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing)))
1940 _AddNormalMove(pStack, pos, cPawn, cTo, 0);
1946 if (IS_EMPTY(pos->rgSquare[cTo].pPiece) &&
1947 BLOCKS_THE_CHECK(cTo) &&
1948 !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing)))
1950 _AddDoubleJump(pStack, pos, cPawn, cTo);
1956 if (cTo == cAttacker)
1958 cExposed = ExposesCheck(pos, cPawn, cKing);
1959 if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
1961 p = pos->rgSquare[cTo].pPiece;
1962 ASSERT(!IS_EMPTY(p));
1963 ASSERT(GET_COLOR(p) == WHITE);
1964 ASSERT(IS_ON_BOARD(cTo));
1965 _AddNormalMove(pStack, pos, cPawn, cTo, p);
1970 // N.B. There is no possible way to block check with an
1971 // en-passant capture because by definition the last move made
1972 // by the other side was the pawn double jump. So only handle
1973 // if the double jumping enemy pawn is the attacker and we can
1974 // kill it en passant.
1976 if ((cTo == pos->cEpSquare) &&
1977 (cAttacker == cTo - 16))
1979 _AddEnPassant(pStack, pos, cPawn, cTo);
1983 if (cTo == cAttacker)
1985 cExposed = ExposesCheck(pos, cPawn, cKing);
1986 if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
1988 p = pos->rgSquare[cTo].pPiece;
1989 ASSERT(!IS_EMPTY(p));
1990 ASSERT(GET_COLOR(p) == WHITE);
1991 ASSERT(IS_ON_BOARD(cTo));
1992 _AddNormalMove(pStack, pos, cPawn, cTo, p);
1995 if ((cTo == pos->cEpSquare) &&
1996 (cAttacker == cTo - 16))
1998 _AddEnPassant(pStack, pos, cPawn, cTo);
2003 ASSERT(RANK2(cPawn));
2005 if (BLOCKS_THE_CHECK(cTo) &&
2006 IS_EMPTY(pos->rgSquare[cTo].pPiece))
2008 _AddPromote(pStack, pos, cPawn, cTo);
2012 if (cTo == cAttacker)
2014 cExposed = ExposesCheck(pos, cPawn, cKing);
2015 if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
2017 p = pos->rgSquare[cTo].pPiece;
2018 ASSERT(!IS_EMPTY(p));
2019 ASSERT(GET_COLOR(p) == WHITE);
2020 ASSERT(IS_ON_BOARD(cTo));
2021 _AddPromote(pStack, pos, cPawn, cTo);
2026 if (cTo == cAttacker)
2028 cExposed = ExposesCheck(pos, cPawn, cKing);
2029 if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
2031 p = pos->rgSquare[cTo].pPiece;
2032 ASSERT(!IS_EMPTY(p));
2033 ASSERT(GET_COLOR(p) == WHITE);
2034 ASSERT(IS_ON_BOARD(cTo));
2035 _AddPromote(pStack, pos, cPawn, cTo);
2043 InvalidGenerator(IN UNUSED MOVE_STACK *pStack,
2044 IN UNUSED POSITION *pos,
2045 IN UNUSED COOR cPawn)
2048 Routine description:
2050 If you'd like to make a call, please hang up and try your call
2055 IN UNUSED MOVE_STACK *pStack,
2056 IN UNUSED POSITION *pos,
2057 IN UNUSED COOR cPawn
2065 UtilPanic(SHOULD_NOT_GET_HERE,
2066 NULL, NULL, NULL, NULL,
2067 __FILE__, __LINE__);
2072 InvalidSaveMe(IN UNUSED MOVE_STACK *pStack,
2073 IN UNUSED POSITION *pos,
2074 IN UNUSED COOR cPawn,
2075 IN UNUSED COOR cKing,
2076 IN UNUSED COOR cAttacker)
2079 Routine description:
2081 ...if you need help, please dial the operator.
2085 IN UNUSED MOVE_STACK *pStack,
2086 IN UNUSED POSITION *pos,
2087 IN UNUSED COOR cPawn,
2088 IN UNUSED COOR cKing,
2089 IN UNUSED COOR cAttacker
2097 UtilPanic(SHOULD_NOT_GET_HERE,
2098 NULL, NULL, NULL, NULL,
2099 __FILE__, __LINE__);
2104 _GenerateAllMoves(IN MOVE_STACK *pStack,
2108 Routine description:
2110 This code generates all pseudo-legal moves in a position. Please
2111 see notes at the top of this module for details.
2115 MOVE_STACK *pStack : the move stack
2116 POSITION *pos : the board position
2125 static void (*JumpTable[])(MOVE_STACK *, POSITION *, COOR) =
2127 InvalidGenerator, // EMPTY
2128 InvalidGenerator, // EMPTY | WHITE
2129 InvalidGenerator, // 2 (BLACK_PAWN)
2130 InvalidGenerator, // 3 (WHITE_PAWN)
2131 GenerateKnight, // 4 (BLACK_KNIGHT)
2132 GenerateWhiteKnight, // 5 (WHITE_KNIGHT)
2133 GenerateBishop, // 6 (BLACK_BISHOP)
2134 GenerateBishop, // 7 (WHITE_BISHOP)
2135 GenerateRook, // 8 (BLACK_ROOK)
2136 GenerateRook, // 9 (WHITE_ROOK)
2137 GenerateQueen, // 10 (BLACK_QUEEN)
2138 GenerateQueen, // 11 (WHITE_QUEEN)
2139 GenerateBlackKing, // 12 (BLACK_KING)
2140 GenerateWhiteKing // 13 (WHITE_KING)
2147 for(u = pos->uNonPawnCount[pos->uToMove][0] - 1;
2151 c = pos->cNonPawns[pos->uToMove][u];
2153 ASSERT(IS_ON_BOARD(c));
2154 p = pos->rgSquare[c].pPiece;
2155 ASSERT(!IS_EMPTY(p));
2156 ASSERT(!IS_PAWN(p));
2157 ASSERT(GET_COLOR(p) == pos->uToMove);
2159 (JumpTable[pos->rgSquare[c].pPiece])(pStack, pos, c);
2162 if (pos->uToMove == BLACK)
2164 for(u = 0; u < pos->uPawnCount[BLACK]; u++)
2166 c = pos->cPawns[BLACK][u];
2168 ASSERT(IS_ON_BOARD(c));
2169 p = pos->rgSquare[c].pPiece;
2170 ASSERT(!IS_EMPTY(p));
2172 ASSERT(GET_COLOR(p) == pos->uToMove);
2174 GenerateBlackPawn(pStack, pos, c);
2177 ASSERT(pos->uToMove == WHITE);
2178 for(u = 0; u < pos->uPawnCount[WHITE]; u++)
2180 c = pos->cPawns[WHITE][u];
2182 ASSERT(IS_ON_BOARD(c));
2183 p = pos->rgSquare[c].pPiece;
2184 ASSERT(!IS_EMPTY(p));
2186 ASSERT(GET_COLOR(p) == pos->uToMove);
2188 GenerateWhitePawn(pStack, pos, c);
2196 _GenerateEscapes(IN MOVE_STACK *pStack,
2200 Routine description:
2202 This function is called when side to move is in check in order to
2203 generate pseudo-legal escapes from check. All legal escapes from
2204 check are a subset of the moves returned by this function. In
2205 general it's pretty good; the only bug I know about is that it
2206 will generate a capture that alleviates check where the capturing
2207 piece is pinned to the king (and thus the capture is illegal).
2208 The purpose of this function is to allow the search to detect when
2209 there is one legal reply to check and possibly extend.
2213 MOVE_STACK *pStack : the move stack
2214 POSITION *pos : the board
2218 ULONG : the number of pieces checking the king
2228 COOR cKing = pos->cNonPawns[pos->uToMove][0];
2230 SEE_LIST rgCheckers;
2233 static void (*JumpTable[])
2234 (MOVE_STACK *, POSITION *, COOR, COOR, COOR) =
2236 InvalidSaveMe, // EMPTY
2237 InvalidSaveMe, // EMPTY | WHITE
2238 InvalidSaveMe, // 2 (BLACK_PAWN)
2239 InvalidSaveMe, // 3 (WHITE_PAWN)
2240 SaveMeKnight, // 4 (BLACK_KNIGHT)
2241 SaveMeKnight, // 5 (WHITE_KNIGHT)
2242 SaveMeBishop, // 6 (BLACK_BISHOP)
2243 SaveMeBishop, // 7 (WHITE_BISHOP)
2244 SaveMeRook, // 8 (BLACK_ROOK)
2245 SaveMeRook, // 9 (WHITE_ROOK)
2246 SaveMeQueen, // 10 (BLACK_QUEEN)
2247 SaveMeQueen, // 11 (WHITE_QUEEN)
2248 InvalidSaveMe, // kings already considered
2249 InvalidSaveMe // kings already considered
2252 ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
2253 ASSERT(TRUE == InCheck(pos, pos->uToMove));
2256 // Use the SEE function GetAttacks to find out how many pieces are
2257 // giving check and from whence. Note: we don't sort rgCheckers
2258 // here; maybe it would be worth trying. In any event it
2259 // shouldn't be affected by treating the list as a minheap
2260 // vs. as a sorted list.
2262 GetAttacks(&rgCheckers, pos, cKing, FLIP(pos->uToMove));
2263 ASSERT(rgCheckers.uCount > 0);
2264 ASSERT(rgCheckers.uCount < 17);
2265 uReturn = rgCheckers.uCount;
2268 // Phase I: See if the king can flee or take the checking piece.
2270 pKing = pos->rgSquare[cKing].pPiece;
2271 ASSERT(GET_COLOR(pKing) == pos->uToMove);
2272 ASSERT(IS_KING(pKing));
2273 ASSERT(OPPOSITE_COLORS(pKing, rgCheckers.data[0].pPiece));
2275 while(0 != g_iQKDeltas[u])
2277 c = cKing + g_iQKDeltas[u];
2281 p = pos->rgSquare[c].pPiece;
2284 // This logical OR replaced with bitwise OR; the effect is
2285 // the same the the bitwise is marginally faster.
2287 if (IS_EMPTY(p) | (OPPOSITE_COLORS(p, pKing)))
2289 if (FALSE == IsAttacked(c, pos, FLIP(pos->uToMove)))
2292 // So we know the destination square is not under
2293 // attack right now... but that could be because
2294 // the present (pre-move) king position "blocks"
2295 // the attack. Detect this.
2298 v < rgCheckers.uCount;
2301 ASSERT(OPPOSITE_COLORS(pKing,
2302 rgCheckers.data[v].pPiece));
2303 if (!IS_PAWN(rgCheckers.data[v].pPiece) &&
2304 !IS_KNIGHT(rgCheckers.data[v].pPiece))
2306 iIndex = (int)rgCheckers.data[v].cLoc -
2308 iDelta = CHECK_DELTA_WITH_INDEX(iIndex);
2311 iIndex = (int)rgCheckers.data[v].cLoc -
2313 if (iDelta == CHECK_DELTA_WITH_INDEX(iIndex))
2319 ASSERT(v == rgCheckers.uCount);
2322 // If we get here the square is not under attack
2323 // and it does not contain our own piece. It's
2324 // either empty or contains an enemy piece.
2326 ASSERT(!p || OPPOSITE_COLORS(pKing, p));
2327 _AddNormalMove(pStack, pos, cKing, c, p);
2328 uReturn += 0x00010000;
2337 // N.B. If there is more than one piece checking the king then
2338 // there is no way to escape check by blocking check with another
2339 // piece or capturing the offending piece. The two attackers
2340 // cannot be on the same rank, file or diagonal because this would
2341 // depend on the previous position being illegal.
2343 if (rgCheckers.uCount > 1)
2347 // Note: the above comment is correct but this assertion fails
2348 // on positions that we made up to run TestSearch. Disabled
2352 iDelta = DIRECTION_BETWEEN_SQUARES(rgCheckers.data[0].cLoc, cKing);
2353 for (v = 1; v < rgCheckers.uCount; v++)
2355 if (DIRECTION_BETWEEN_SQUARES(rgCheckers.data[v].cLoc, cKing) !=
2361 ASSERT(v != rgCheckers.uCount);
2368 // Phase 2: If we get here there is a lone attacker causing check.
2369 // See if the other pieces can block the check or take the
2372 c = rgCheckers.data[0].cLoc;
2373 for (u = 1; // don't consider the king
2374 u < pos->uNonPawnCount[pos->uToMove][0];
2377 cDefender = pos->cNonPawns[pos->uToMove][u];
2378 ASSERT(IS_ON_BOARD(cDefender));
2379 p = pos->rgSquare[cDefender].pPiece;
2380 ASSERT(p && !IS_PAWN(p));
2381 ASSERT(GET_COLOR(p) == pos->uToMove);
2383 (JumpTable[p])(pStack, pos, cDefender, cKing, c);
2386 // Consider all pawns too
2387 if (pos->uToMove == BLACK)
2389 for (u = 0; u < pos->uPawnCount[BLACK]; u++)
2391 cDefender = pos->cPawns[BLACK][u];
2393 ASSERT(IS_ON_BOARD(cDefender));
2394 p = pos->rgSquare[cDefender].pPiece;
2395 ASSERT(p && IS_PAWN(p));
2396 ASSERT(GET_COLOR(p) == pos->uToMove);
2398 SaveMeBlackPawn(pStack, pos, cDefender, cKing, c);
2401 ASSERT(pos->uToMove == WHITE);
2402 for (u = 0; u < pos->uPawnCount[WHITE]; u++)
2404 cDefender = pos->cPawns[WHITE][u];
2406 ASSERT(IS_ON_BOARD(cDefender));
2407 p = pos->rgSquare[cDefender].pPiece;
2408 ASSERT(p && IS_PAWN(p));
2409 ASSERT(GET_COLOR(p) == pos->uToMove);
2411 SaveMeWhitePawn(pStack, pos, cDefender, cKing, c);
2418 typedef struct _PRECOMP_KILLERS
2426 _ScoreAllMoves(IN MOVE_STACK *pStack,
2427 IN SEARCHER_THREAD_CONTEXT *ctx,
2431 Routine description:
2433 We have just generated all the moves, now score them. See comments
2434 inline below about order.
2438 IN MOVE_STACK *pStack,
2439 IN SEARCHER_THREAD_CONTEXT *ctx,
2448 ULONG uPly = ctx->uPly;
2449 POSITION *pos = &ctx->sPosition;
2453 MOVE_STACK_MOVE_VALUE_FLAGS mvf;
2454 ULONG uHashMoveLoc = (ULONG)-1;
2455 ULONG uColor = pos->uToMove;
2456 PRECOMP_KILLERS sKillers[4];
2457 COOR cEnprise = GetEnprisePiece(pos, uColor);
2460 // We have generated all moves here. We also know that we are not
2461 // escaping from check. There can be a hash move -- if there is
2462 // then the search has already tried it and we should not generate
2465 // White has 218 legal moves in this position:
2466 // 3Q4/1Q4Q1/4Q3/2Q4R/Q4Q2/3Q4/1Q4Rp/1K1BBNNk w - - 0 1
2470 // Pre-populate killer/bonuses
2472 sKillers[0].mv = ctx->mvKiller[uPly][0];
2473 sKillers[0].uBonus = FIRST_KILLER;
2474 sKillers[1].mv.uMove = sKillers[3].mv.uMove = 0;
2475 sKillers[2].mv = ctx->mvKiller[uPly][1];
2476 sKillers[2].uBonus = THIRD_KILLER;
2479 sKillers[1].mv = ctx->mvKiller[uPly - 2][0];
2480 sKillers[1].uBonus = SECOND_KILLER;
2481 sKillers[3].mv = ctx->mvKiller[uPly - 2][1];
2482 sKillers[3].uBonus = FOURTH_KILLER;
2484 sKillers[0].uBonus |=
2485 (SORT_THESE_FIRST * (IS_KILLERMATE_MOVE(sKillers[0].mv) != 0));
2486 sKillers[1].uBonus |=
2487 (SORT_THESE_FIRST * (IS_KILLERMATE_MOVE(sKillers[1].mv) != 0));
2488 sKillers[2].uBonus |=
2489 (SORT_THESE_FIRST * (IS_KILLERMATE_MOVE(sKillers[2].mv) != 0));
2490 sKillers[3].uBonus |=
2491 (SORT_THESE_FIRST * (IS_KILLERMATE_MOVE(sKillers[3].mv) != 0));
2496 ASSERT(MOVE_COUNT(ctx, uPly) <= MAX_MOVES_PER_PLY);
2497 for (u = pStack->uBegin[uPly];
2498 u < pStack->uEnd[uPly];
2501 mv = pStack->mvf[u].mv;
2503 ASSERT(GET_COLOR(mv.pMoved) == uColor);
2504 if (!IS_SAME_MOVE(mv, mvHash))
2507 pStack->mvf[u].iValue = -MAX_INT;
2508 pStack->mvf[u].bvFlags = 0;
2511 // 1. Hash move (already handled by search, all we have to
2512 // do here is not generate it)
2513 // 2. Killer mate move, if one exists
2514 // 3. Winning captures & promotions (MVV/LVA to tiebreak)
2515 // 4. Even captures & promotions -- >= SORT_THESE_FIRST
2516 // 5. Killer move 1-4
2517 // 6. "good" moves (moves away from danger etc...)
2518 // 7. Other non capture moves (using dynamic move ordering scheme)
2519 // 8. Losing captures -- <0
2523 // Captures and promotions, use SEE
2525 if (IS_CAPTURE_OR_PROMOTION(mv))
2527 ASSERT((mv.pCaptured) || (mv.pPromoted));
2528 s = PIECE_VALUE(mv.pCaptured) -
2529 PIECE_VALUE(mv.pMoved);
2530 if ((s <= 0) || mv.pPromoted)
2537 s += PIECE_VALUE_OVER_100(mv.pCaptured) + 120;
2538 s += PIECE_VALUE_OVER_100(mv.pPromoted);
2539 s -= PIECE_VALUE_OVER_100(mv.pMoved);
2540 s += SORT_THESE_FIRST;
2543 // IDEA: bonus for capturing opponent's last moved
2544 // piece to encourage quicker fail highs?
2546 ASSERT(s >= SORT_THESE_FIRST);
2548 ASSERT((s & STRIP_OFF_FLAGS) < VALUE_KING);
2553 ASSERT((s & STRIP_OFF_FLAGS) > -VALUE_KING);
2561 // Non-captures/promotes use history/killer
2565 ASSERT((!mv.pCaptured) && (!mv.pPromoted));
2566 s = g_iPSQT[mv.pMoved][mv.cTo]; // 0..1000
2567 s |= (IS_SAME_MOVE(sKillers[0].mv, mv) * sKillers[0].uBonus);
2568 s |= (IS_SAME_MOVE(sKillers[1].mv, mv) * sKillers[1].uBonus);
2569 s |= (IS_SAME_MOVE(sKillers[2].mv, mv) * sKillers[2].uBonus);
2570 s |= (IS_SAME_MOVE(sKillers[3].mv, mv) * sKillers[3].uBonus);
2571 s |= ((GOOD_MOVE + PIECE_VALUE(mv.pMoved) / 2) *
2572 (mv.cFrom == cEnprise));
2575 pStack->mvf[u].iValue = s;
2576 ASSERT(pStack->mvf[u].iValue != -MAX_INT);
2580 ASSERT(uHashMoveLoc == (ULONG)-1);
2583 ASSERT((mv.cFrom == mvHash.cFrom) && (mv.cTo == mvHash.cTo));
2584 pStack->mvf[u].bvFlags |= MVF_MOVE_SEARCHED;
2591 // If we generated the hash move, stick it at the end and pretend
2592 // we didn't... it was already considered by the search and we
2593 // should not generate it again.
2595 if (uHashMoveLoc != (ULONG)-1)
2597 ASSERT(MOVE_COUNT(ctx, uPly) >= 1);
2598 ASSERT(uHashMoveLoc >= pStack->uBegin[uPly]);
2599 ASSERT(uHashMoveLoc < pStack->uEnd[uPly]);
2600 mvf = pStack->mvf[uHashMoveLoc];
2601 pStack->mvf[uHashMoveLoc] = pStack->mvf[pStack->uEnd[uPly] - 1];
2603 pStack->mvf[pStack->uEnd[uPly] - 1] = mvf;
2605 pStack->uEnd[uPly]--;
2612 _ScoreAllEscapes(IN MOVE_STACK *pStack,
2613 IN SEARCHER_THREAD_CONTEXT *ctx,
2617 Routine description:
2619 We have just generated all the moves, now score them. See comments
2620 inline below about order.
2624 IN MOVE_STACK *pStack,
2625 IN SEARCHER_THREAD_CONTEXT *ctx,
2634 ULONG uPly = ctx->uPly;
2635 POSITION *pos = &ctx->sPosition;
2639 MOVE_STACK_MOVE_VALUE_FLAGS mvf;
2640 ULONG uHashMoveLoc = (ULONG)-1;
2644 static SCORE _bonus[2][7] = {
2645 { 0, 2000, 1250, 1500, 1100, 850, 0 }, // friend
2646 { 0, 1600, 100, 100, 50, -1, -1 }, // foe
2650 // We have generated legal escapes from check here. There can be
2651 // a hash move -- if there is then the search has already tried it
2652 // and we should not generate it again.
2655 ASSERT(MOVE_COUNT(ctx, uPly) <= MAX_MOVES_PER_PLY);
2656 for (u = pStack->uBegin[uPly];
2657 u < pStack->uEnd[uPly];
2660 mv = pStack->mvf[u].mv;
2662 ASSERT(GET_COLOR(mv.pMoved) == pos->uToMove);
2663 if (!IS_SAME_MOVE(mv, mvHash))
2666 pStack->mvf[u].iValue = -MAX_INT;
2667 pStack->mvf[u].bvFlags = 0;
2669 pStack->mvf[u].mv.bvFlags |= MOVE_FLAG_ESCAPING_CHECK;
2672 // 1. Hash move (already handled by search, all we have to
2673 // do here is not generate it)
2674 // 2. Winning captures by pieces other than the checked king
2675 // 3. Winning captures by the king
2676 // 4. Even captures by pieces other than the king
2677 // 5. Killer escapes
2678 // 6. Moves that block the check and are even
2680 // 8. Losing captures / blocks
2684 // Captures and promotions, use SEE
2686 if (IS_CAPTURE_OR_PROMOTION(mv))
2688 ASSERT((mv.pCaptured) || (mv.pPromoted));
2689 s = PIECE_VALUE(mv.pCaptured) -
2690 PIECE_VALUE(mv.pMoved);
2691 if ((s <= 0) || mv.pPromoted)
2698 // Winning captures: take with pieces before with king
2699 s += PIECE_VALUE_OVER_100(mv.pPromoted);
2700 s -= PIECE_VALUE_OVER_100(mv.pMoved);
2701 s >>= IS_KING(mv.pMoved);
2702 s |= SORT_THESE_FIRST;
2703 ASSERT(s >= SORT_THESE_FIRST);
2705 ASSERT((s & STRIP_OFF_FLAGS) < VALUE_KING);
2709 // Even capture -- can't be capturing w/ king
2710 ASSERT(!IS_KING(mv.pMoved));
2711 s += PIECE_VALUE_OVER_100(mv.pPromoted) + 20;
2712 s -= PIECE_VALUE_OVER_100(mv.pMoved);
2720 ASSERT(!IS_KING(mv.pMoved));
2721 ASSERT((s & STRIP_OFF_FLAGS) > -VALUE_KING);
2729 // Non-captures/promotes
2733 ASSERT((!mv.pCaptured) && (!mv.pPromoted));
2734 if (IS_SAME_MOVE(mv, ctx->mvKillerEscapes[uPly][0]) ||
2735 IS_SAME_MOVE(mv, ctx->mvKillerEscapes[uPly][1]))
2742 s = g_iPSQT[mv.pMoved][mv.cTo]; // 0..1000
2743 if (!IS_KING(mv.pMoved))
2746 s += (VALUE_QUEEN - PIECE_VALUE(mv.pMoved)) * 4;
2747 if (SEE(pos, mv) >= 0)
2749 // Non-losing block of check
2750 ASSERT(SEE(pos, mv) == 0);
2756 // Losing block of check
2763 // If the king is fleeing, encourage a spot next
2764 // to friendly pieces.
2767 c = mv.cTo + g_iQKDeltas[v++];
2770 p = pos->rgSquare[c].pPiece;
2771 s += _bonus[OPPOSITE_COLORS(pos->uToMove, p)]
2773 ASSERT(_bonus[OPPOSITE_COLORS(pos->uToMove, p)]
2774 [PIECE_TYPE(p)] >= 0);
2777 while(g_iQKDeltas[v] != 0);
2781 pStack->mvf[u].iValue = s;
2782 ASSERT(pStack->mvf[u].iValue != -MAX_INT);
2786 ASSERT(uHashMoveLoc == (ULONG)-1);
2789 ASSERT((mv.cFrom == mvHash.cFrom) && (mv.cTo == mvHash.cTo));
2790 pStack->mvf[u].bvFlags |= MVF_MOVE_SEARCHED;
2796 // If we generated the hash move, stick it at the end and pretend
2797 // we didn't... it was already considered by the search and we
2798 // should not generate it again.
2800 if (uHashMoveLoc != (ULONG)-1)
2802 ASSERT(MOVE_COUNT(ctx, uPly) >= 1);
2803 ASSERT(uHashMoveLoc >= pStack->uBegin[uPly]);
2804 ASSERT(uHashMoveLoc < pStack->uEnd[uPly]);
2805 mvf = pStack->mvf[uHashMoveLoc];
2806 pStack->mvf[uHashMoveLoc] = pStack->mvf[pStack->uEnd[uPly] - 1];
2808 pStack->mvf[pStack->uEnd[uPly] - 1] = mvf;
2810 pStack->uEnd[uPly]--;
2817 _ScoreQSearchMovesInclChecks(IN MOVE_STACK *pStack,
2818 IN SEARCHER_THREAD_CONTEXT *ctx)
2821 Routine description:
2823 We have just generated all the moves. We were called from the
2826 1. Unlike _ScoreAllMoves we don't have to think about a hash move
2827 2. QSearch only cares about winning/even captures/promotes and
2830 Like _ScoreSearchMoves, this function takes "hints" from the
2831 searcher context about what squares are in danger and tries to
2832 order moves away from those squares sooner. Unlike
2833 _ScoreSearchMoves this function also attempts to capture enemies
2834 that are in danger sooner.
2838 IN MOVE_STACK *pStack,
2839 IN SEARCHER_THREAD_CONTEXT *ctx
2847 register POSITION *pos = &ctx->sPosition;
2848 PLY_INFO *pi = &ctx->sPlyInfo[ctx->uPly];
2849 ULONG uPly = ctx->uPly;
2850 ULONG u, uColor = pos->uToMove;
2852 MOVE mvLast = (pi-1)->mv;
2854 COOR cEnprise = GetEnprisePiece(pos, uColor);
2857 // We have generate all moves or just legal escapes from check
2858 // here. There can be a hash move -- if there is then the search
2859 // has already tried it and we should not generate it again.
2861 // White has 218 legal moves in this position:
2862 // 3Q4/1Q4Q1/4Q3/2Q4R/Q4Q2/3Q4/1Q4Rp/1K1BBNNk w - - 0 1
2864 ASSERT(MOVE_COUNT(ctx, uPly) <= MAX_MOVES_PER_PLY);
2867 for (u = pStack->uBegin[uPly];
2868 u < pStack->uEnd[uPly];
2871 mv = pStack->mvf[u].mv;
2872 ASSERT(GET_COLOR(mv.pMoved) == uColor);
2874 pStack->mvf[u].iValue = -MAX_INT;
2875 pStack->mvf[u].bvFlags = 0;
2877 pStack->mvf[u].mv.bvFlags |= WouldGiveCheck(ctx, mv);
2880 // 1. There can't be a hash move so don't worry about it
2881 // 2. Winning captures & promotions
2882 // 3. Even captures & promotions -- >= SORT_THESE_FIRST
2883 // 4. Checking non-capture moves
2884 // 5. Losing captures that check -- > 0
2885 // 6. Everything else is ignored -- <= 0
2889 // Captures and promotions, use SEE
2891 if (IS_CAPTURE_OR_PROMOTION(mv))
2893 ASSERT((mv.pCaptured) || (mv.pPromoted));
2894 s = PIECE_VALUE(mv.pCaptured) -
2895 PIECE_VALUE(mv.pMoved);
2896 if ((s <= 0) || mv.pPromoted)
2904 // Winning / even captures / promotes should be >=
2905 // SORT_THESE_FIRST.
2907 s += PIECE_VALUE_OVER_100(mv.pCaptured) + 120;
2908 s += PIECE_VALUE_OVER_100(mv.pPromoted);
2909 s -= PIECE_VALUE_OVER_100(mv.pMoved);
2910 s += SORT_THESE_FIRST;
2913 // Bonus if it checks too
2915 s += (pStack->mvf[u].mv.bvFlags & MOVE_FLAG_CHECKING) * 4;
2918 // Bonus for capturing last moved enemy piece.
2920 s += (mv.cTo == mvLast.cTo) * 64;
2921 ASSERT(s >= SORT_THESE_FIRST);
2927 // Make losing captures into either zero scores or
2928 // slightly positive if they check. Qsearch is going
2929 // to bail as soon as it sees the first zero so no
2930 // need to keep real SEE scores on these.
2932 s = (pStack->mvf[u].mv.bvFlags & MOVE_FLAG_CHECKING);
2938 // Non-captures/promotes -- we don't care unless they check
2942 s = (pStack->mvf[u].mv.bvFlags & MOVE_FLAG_CHECKING) * 2;
2944 ASSERT(s < SORT_THESE_FIRST);
2948 // If this move moves a piece that was in danger, consider it
2953 ASSERT(IS_CAPTURE_OR_PROMOTION(pStack->mvf[u].mv) ||
2954 IS_CHECKING_MOVE(pStack->mvf[u].mv));
2955 s += (mv.cFrom == cEnprise) * (PIECE_VALUE(mv.pMoved) / 4);
2958 pStack->mvf[u].iValue = s;
2959 ASSERT(pStack->mvf[u].iValue != -MAX_INT);
2966 _ScoreQSearchMovesNoChecks(IN MOVE_STACK *pStack,
2967 IN SEARCHER_THREAD_CONTEXT *ctx)
2970 Routine description:
2972 We have just generated all the moves. We were called from the
2975 1. Unlike _ScoreAllMoves we don't have to think about a hash move
2976 2. QSearch only cares about winning/even captures/promotes. At
2977 this point it is so deep that it doesn't even care about
2980 Like _ScoreSearchMoves, this function takes "hints" from the
2981 searcher context about what squares are in danger and tries to
2982 order moves away from those squares sooner. Unlike
2983 _ScoreSearchMoves this function also attempts to capture enemies
2984 that are in danger sooner.
2988 IN MOVE_STACK *pStack,
2989 IN SEARCHER_THREAD_CONTEXT *ctx
2997 register POSITION *pos = &(ctx->sPosition);
2998 ULONG uPly = ctx->uPly;
3004 // We have generate all moves or just legal escapes from check
3005 // here. There can be a hash move -- if there is then the search
3006 // has already tried it and we should not generate it again.
3008 // White has 218 legal moves in this position:
3009 // 3Q4/1Q4Q1/4Q3/2Q4R/Q4Q2/3Q4/1Q4Rp/1K1BBNNk w - - 0 1
3011 ASSERT(MOVE_COUNT(ctx, uPly) <= MAX_MOVES_PER_PLY);
3013 uColor = pos->uToMove;
3014 for (u = pStack->uBegin[uPly];
3015 u < pStack->uEnd[uPly];
3018 mv = pStack->mvf[u].mv;
3019 ASSERT(GET_COLOR(mv.pMoved) == uColor);
3021 pStack->mvf[u].iValue = -MAX_INT;
3022 pStack->mvf[u].bvFlags = 0;
3025 // 1. There can't be a hash move so don't worry about it
3026 // 2. Winning captures & promotions
3027 // 3. Even captures & promotions -- >= SORT_THESE_FIRST
3028 // 4. Everything else is ignored -- <= 0
3032 // Captures and promotions, use SEE
3035 if (IS_CAPTURE_OR_PROMOTION(mv))
3037 ASSERT((mv.pCaptured) || (mv.pPromoted));
3038 s = PIECE_VALUE(mv.pCaptured) -
3039 PIECE_VALUE(mv.pMoved);
3041 // TODO: how does this handle positions like K *p *k ?
3051 // Winning / even captures / promotes should be >=
3052 // SORT_THESE_FIRST.
3054 s += PIECE_VALUE_OVER_100(mv.pCaptured) + 120;
3055 s += PIECE_VALUE_OVER_100(mv.pPromoted);
3056 s -= PIECE_VALUE_OVER_100(mv.pMoved);
3057 s += SORT_THESE_FIRST;
3060 // IDEA: bonus for capturing an enprise enemy piece?
3062 ASSERT(s >= SORT_THESE_FIRST);
3066 pStack->mvf[u].iValue = s;
3067 ASSERT(pStack->mvf[u].iValue != -MAX_INT);
3075 GenerateMoves(IN SEARCHER_THREAD_CONTEXT *ctx,
3080 Routine description:
3082 This is the entrypoint to the move generator. It invokes the
3083 requested generation code which push moves onto the move stack in
3084 ctx. Once the moves are generated this code optionally will score
3089 SEARCHER_THREAD_CONTEXT *ctx : a searcher thread's context
3091 IN: ctx->sPosition.uDangerCount and ctx->sPosition.cDanger
3092 data is used to tweak move scoring. This data MUST be
3093 consistent with the state of the current board.
3095 OUT: ctx->sMoveStack.uBegin[ctx->uPly] is set,
3096 ctx->sMoveStack.uEnd[ctx->uPly] is set,
3097 ctx->sMoveStack.mvf[begin..end] are populated
3099 MOVE mvHash : the hash move to not generate
3101 ULONG uType : what kind of moves to generate
3103 GENERATE_ALL_MOVES : pseudo-legal generation of all moves;
3104 scored by _ScoreAllMoves. Return value is count of moves
3107 GENERATE_ESCAPE: called when stm in check, generate evasions
3108 scored by _ScoreAllMoves. Return value is special code;
3109 see inline comment below.
3111 GENERATE_CAPTURES_PROMS_CHECKS: caps, promotions, and checks
3112 scored by _ScoreQSearchMoves. Return value is count of
3115 GENERATE_CAPTURES_PROMS: caps, promotions. Skip the checks.
3117 GENERATE_DONT_SCORE: pseudo-legal generation of all moves;
3118 not scored to save time. Return value always zero.
3126 register ULONG uPly = ctx->uPly;
3127 MOVE_STACK *pStack = &ctx->sMoveStack;
3128 POSITION *pos = &ctx->sPosition;
3136 ASSERT((uPly >= 0) && (uPly < MAX_PLY_PER_SEARCH));
3137 memcpy(&board, pos, sizeof(POSITION));
3138 memcpy(&(pStack->board[uPly]), pos, sizeof(POSITION));
3141 // Make sure the hash move makes sense (if provided)
3145 p = pos->rgSquare[mvHash.cFrom].pPiece;
3147 ASSERT(GET_COLOR(p) == pos->uToMove);
3148 p = pos->rgSquare[mvHash.cTo].pPiece;
3149 ASSERT(!p || OPPOSITE_COLORS(p, pos->uToMove));
3152 pStack->uPly = uPly;
3153 pStack->uEnd[uPly] = pStack->uBegin[uPly];
3154 pStack->sGenFlags[uPly].uAllGenFlags = 0;
3158 case GENERATE_CAPTURES_PROMS_CHECKS:
3159 ASSERT(!InCheck(pos, pos->uToMove));
3160 ASSERT(mvHash.uMove == 0);
3161 _FindUnblockedSquares(pStack, pos);
3162 _GenerateAllMoves(pStack, pos);
3163 _ScoreQSearchMovesInclChecks(pStack, ctx);
3165 case GENERATE_CAPTURES_PROMS:
3166 ASSERT(!InCheck(pos, pos->uToMove));
3167 ASSERT(mvHash.uMove == 0);
3168 _FindUnblockedSquares(pStack, pos);
3169 _GenerateAllMoves(pStack, pos);
3170 _ScoreQSearchMovesNoChecks(pStack, ctx);
3172 case GENERATE_ALL_MOVES:
3173 ASSERT(!InCheck(pos, pos->uToMove));
3174 _FindUnblockedSquares(pStack, pos);
3175 _GenerateAllMoves(pStack, pos);
3176 _ScoreAllMoves(pStack, ctx, mvHash);
3177 uReturn = MOVE_COUNT(ctx, uPly);
3179 case GENERATE_ESCAPES:
3180 ASSERT(InCheck(pos, pos->uToMove));
3181 _FindUnblockedSquares(pStack, pos);
3182 uReturn = _GenerateEscapes(pStack, pos);
3184 pStack->sGenFlags[uPly].uKingMoveCount = uReturn >> 16;
3185 pStack->sGenFlags[uPly].uCheckingPieces = uReturn & 0xFF;
3186 _ScoreAllEscapes(pStack, ctx, mvHash);
3189 // Note: this is just for plytest / seetest. Just generate
3190 // moves, do not score them.
3191 case GENERATE_DONT_SCORE:
3192 if (InCheck(pos, pos->uToMove))
3194 (void)_GenerateEscapes(pStack, pos);
3195 for (uReturn = pStack->uBegin[uPly];
3196 uReturn < pStack->uEnd[uPly];
3199 pStack->mvf[uReturn].mv.bvFlags |=
3200 MOVE_FLAG_ESCAPING_CHECK;
3205 (void)_GenerateAllMoves(pStack, pos);
3217 // Sanity check the move list we just generated...
3219 ASSERT(MOVE_COUNT(ctx, uPly) >= 0);
3220 for (u = pStack->uBegin[uPly];
3221 u < pStack->uEnd[uPly];
3224 mv = pStack->mvf[u].mv;
3225 ASSERT(!IS_SAME_MOVE(mv, mvHash));
3226 SanityCheckMove(pos, mv);
3228 if (WouldGiveCheck(ctx, mv))
3230 if (MakeMove(ctx, mv))
3232 ASSERT(ctx->uPly == (uPly + 1));
3233 ASSERT(InCheck(pos, pos->uToMove));
3234 UnmakeMove(ctx, mv);
3237 // Note: this is so that DEBUG and RELEASE builds search
3238 // the same trees. Without this the MakeMove/UnmakeMove
3239 // pair here can affect the order of the pieces in the
3240 // POSITION piece lists which in turn affects the order
3241 // in which moves are generated down the road.
3243 memcpy(&ctx->sPosition, &board, sizeof(POSITION));
3248 if (MakeMove(ctx, mv))
3250 ASSERT(ctx->uPly == (uPly + 1));
3251 ASSERT(!InCheck(pos, pos->uToMove));
3252 UnmakeMove(ctx, mv);
3255 // Note: this is so that DEBUG and RELEASE builds search
3256 // the same trees. Without this the MakeMove/UnmakeMove
3257 // pair here can affect the order of the pieces in the
3258 // POSITION piece lists which in turn affects the order
3259 // in which moves are generated down the road.
3261 memcpy(&ctx->sPosition, &board, sizeof(POSITION));
3265 ASSERT(PositionsAreEquivalent(&board, pos));
3268 pStack->sGenFlags[uPly].uMoveCount = MOVE_COUNT(ctx, uPly);
3269 pStack->uBegin[uPly + 1] = pStack->uEnd[uPly];