summaryrefslogtreecommitdiff
path: root/src/generate.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2016-06-01 18:58:58 -0700
committerScott Gasch <[email protected]>2016-06-01 18:58:58 -0700
commit3fd43cd5fcb22bb65bf2a92a25d95d801b11c9e0 (patch)
tree9b6443235d16ba17f094a1a1c7ae53d2bcb9267b /src/generate.c
Initial checkin for typhoon chess engine.
Diffstat (limited to 'src/generate.c')
-rwxr-xr-xsrc/generate.c3270
1 files changed, 3270 insertions, 0 deletions
diff --git a/src/generate.c b/src/generate.c
new file mode 100755
index 0000000..f1ebab8
--- /dev/null
+++ b/src/generate.c
@@ -0,0 +1,3270 @@
+/**
+
+Copyright (c) Scott Gasch
+
+Module Name:
+
+ generate.c
+
+Abstract:
+
+ This module contains two move generators: a pseudo legal move
+ generator and a pseudo legal escaping move generator. The former
+ is called in positions where side on move is not in check and the
+ latter is called when the side on move is in check. Note: both of
+ these generators produce pseudo-legal moves. The normal move
+ generator is pretty bad: it does not bother to see if moves expose
+ their own king to check or if castles pass through check
+ etc... Instead it relies on MakeMove to throw out any illegal
+ moves it generates. The escape generator is better in that it
+ produces less illegal moves. But it is still possible to generate
+ some with it. An example: it will generate replies to check that
+ may include capturing moves that are illegal because the capturing
+ piece is pinned to the (already in check) king. Both generators
+ produce a set of moves that contains the true legal set of moves in
+ a position as a subset; if a move is legal it will be produced.
+
+ This module also contains some code to flag moves as checking
+ moves and score moves after they are generated.
+
+Author:
+
+ Scott Gasch ([email protected]) 11 May 2004
+
+Revision History:
+
+ $Id: generate.c 345 2007-12-02 22:56:42Z scott $
+
+**/
+
+#include "chess.h"
+#include "psqt.h"
+
+//
+// Note: this order is important because of how EvalQueen works
+//
+const INT g_iQKDeltas[] = {
+ -17, -16, -15, -1, +1, +17, +15, +16, 0 };
+
+static void
+_FindUnblockedSquares(IN MOVE_STACK *pStack,
+ IN POSITION *pos)
+/**
+
+Routine description:
+
+ Given a position, find, count and mark all squares that are
+ unblocked for the king of the side not on move. For example:
+
+ +---+---+---+---+- - - The squares marked with *'s in this
+ |***| K |***|*Q*| diagram are unblocked for the K. As
+ +---+---+---+---+- - - you can see, there are nine of them.
+ |*P*|***|***| |
+ +---+---+---+---+- - - Note the piece at the end of an unblocked
+ | |***| |*P*| ray from the king is on an unblocked
+ +---+---+---+---+- - - square (and its color doesn't matter)
+ | |*R*| | |
+ +---+---+---+---+- - - This stuff is used to flag checking moves
+ | | | | | as they are generated.
+ . . . . .
+
+ Also note: this is one of the most called routines in the engine;
+ speed is of the essence here.
+
+Parameters:
+
+ MOVE_STACK *pStack : move stack pointer
+ POSITION *pos : position pointer
+
+Return value:
+
+ void
+
+**/
+{
+ register ULONG u;
+ COOR cKing = pos->cNonPawns[FLIP(pos->uToMove)][0];
+ COOR c;
+ ULONG uPly = pStack->uPly;
+#ifdef DEBUG
+ PIECE pKing = pos->rgSquare[cKing].pPiece;
+ ULONG uCount = 0;
+ COOR cx;
+
+ ASSERT(IS_ON_BOARD(cKing));
+ ASSERT(IS_KING(pKing));
+ ASSERT(GET_COLOR(pKing) != pos->uToMove);
+#endif
+
+ //
+ // Adjust the unblocked key value for this ply...
+ //
+ pStack->uUnblockedKeyValue[uPly]++;
+#ifdef DEBUG
+ FOREACH_SQUARE(c)
+ {
+ if (!IS_ON_BOARD(c)) continue;
+ ASSERT(pStack->sUnblocked[uPly][c].uKey !=
+ pStack->uUnblockedKeyValue[uPly]);
+ }
+#endif
+
+ u = 0;
+ ASSERT(g_iQKDeltas[u] != 0);
+ do
+ {
+ c = cKing + g_iQKDeltas[u];
+ while(IS_ON_BOARD(c))
+ {
+ pStack->sUnblocked[uPly][c].uKey =
+ pStack->uUnblockedKeyValue[uPly];
+ pStack->sUnblocked[uPly][c].iPointer = -1 * g_iQKDeltas[u];
+#ifdef DEBUG
+ ASSERT(-g_iQKDeltas[u] == DIRECTION_BETWEEN_SQUARES(c,cKing));
+ ASSERT(pStack->sUnblocked[uPly][c].iPointer != 0);
+ cx = c;
+ do
+ {
+ cx += pStack->sUnblocked[uPly][c].iPointer;
+ }
+ while(IS_ON_BOARD(cx) && (IS_EMPTY(pos->rgSquare[cx].pPiece)));
+ ASSERT(cx == cKing);
+ uCount++;
+#endif
+ if (!IS_EMPTY(pos->rgSquare[c].pPiece))
+ {
+ break;
+ }
+ c += g_iQKDeltas[u];
+ }
+ u++;
+ }
+ while(g_iQKDeltas[u] != 0);
+ ASSERT(uCount > 2);
+ ASSERT(uCount < 28);
+}
+
+#define SQUARE_IS_UNBLOCKED(c) (uUnblocked == kp[(c)].uKey)
+#define DIR_FROM_SQ_TO_KING(c) (kp[(c)].iPointer)
+
+FLAG
+WouldGiveCheck(IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN MOVE mv)
+/**
+
+Routine description:
+
+ Determine whether a move is a checking move or not. For this
+ function to work properly _FindUnblockedSquares must have been
+ called at the start of generation.
+
+ Note: this is one of the most called codepaths in the engine,
+ speed is of the essence here.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board
+ MOVE mv : the move under consideration
+
+Return value:
+
+ FLAG : TRUE if the move is checking, FALSE otherwise
+
+**/
+{
+ //
+ // TODO: consider adding a hash table to see if sig+mv would
+ // give check. Is this worth it?
+ //
+ MOVE_STACK *pStack = &(ctx->sMoveStack);
+ POSITION *pos = &(ctx->sPosition);
+ COOR cTo = mv.cTo;
+ COOR cFrom = mv.cFrom;
+ PIECE pPiece = mv.pMoved;
+ COOR xKing = pos->cNonPawns[FLIP(GET_COLOR(pPiece))][0];
+ KEY_POINTER *kp = &(pStack->sUnblocked[ctx->uPly][0]);
+ ULONG uUnblocked = pStack->uUnblockedKeyValue[ctx->uPly];
+ COOR cEpSquare;
+ int iDelta;
+
+ ASSERT(ctx->uPly < MAX_PLY_PER_SEARCH);
+ ASSERT(PositionsAreEquivalent(&(pStack->board[ctx->uPly]), pos));
+ ASSERT(mv.uMove);
+ ASSERT(IS_KING(pos->rgSquare[xKing].pPiece));
+ ASSERT(IS_ON_BOARD(cTo));
+ ASSERT(IS_ON_BOARD(cFrom));
+ ASSERT(pPiece);
+
+ //
+ // Phase 1: Does this move directly attack the king?
+ //
+
+ //
+ // You can't attack one king directly with the other except by
+ // castling (in which case the rook is doing the attack).
+ //
+ if (IS_KING(pPiece))
+ {
+ if (!IS_SPECIAL_MOVE(mv))
+ {
+ goto exposes;
+ }
+
+ ASSERT((mv.cFrom == E8) || (mv.cFrom == E1));
+ pPiece &= ~4;
+ ASSERT(!IS_KING(pPiece));
+ ASSERT(IS_ROOK(pPiece));
+ ASSERT(pPiece == (BLACK_ROOK | GET_COLOR(pPiece)));
+
+ switch(cTo)
+ {
+ case C1:
+ cTo = D1;
+ if (SQUARE_IS_UNBLOCKED(E1) &&
+ (DIR_FROM_SQ_TO_KING(E1) == 1))
+ {
+ ASSERT(RANK1(xKing));
+ return(MOVE_FLAG_CHECKING);
+ }
+ break;
+ case G1:
+ cTo = F1;
+ if (SQUARE_IS_UNBLOCKED(E1) &&
+ (DIR_FROM_SQ_TO_KING(E1) == -1))
+ {
+ ASSERT(RANK1(xKing));
+ return(MOVE_FLAG_CHECKING);
+ }
+ break;
+ case C8:
+ cTo = D8;
+ if (SQUARE_IS_UNBLOCKED(E8) &&
+ (DIR_FROM_SQ_TO_KING(E8) == 1))
+ {
+ ASSERT(RANK8(xKing));
+ return(MOVE_FLAG_CHECKING);
+ }
+ break;
+ case G8:
+ cTo = F8;
+ if (SQUARE_IS_UNBLOCKED(E8) &&
+ (DIR_FROM_SQ_TO_KING(E8) == -1))
+ {
+ ASSERT(RANK8(xKing));
+ return(MOVE_FLAG_CHECKING);
+ }
+ break;
+#ifdef DEBUG
+ default:
+ UtilPanic(SHOULD_NOT_GET_HERE,
+ NULL, NULL, NULL, NULL,
+ __FILE__, __LINE__);
+ break;
+#endif
+ }
+ }
+
+ //
+ // Ok, either its not a king or its a castle move and we are thinking
+ // about the rook now.
+ //
+
+ //
+ // Check the attack vector table
+ //
+ ASSERT(!IS_KING(pPiece));
+ iDelta = 0;
+ if (mv.pPromoted)
+ {
+ ASSERT(IS_PAWN(pPiece));
+ ASSERT(GET_COLOR(pPiece) == GET_COLOR(mv.pPromoted));
+ pPiece = mv.pPromoted;
+ ASSERT(!IS_PAWN(pPiece));
+ iDelta = cFrom - cTo;
+ ASSERT(iDelta != 0);
+ }
+
+ //
+ // Does pPiece attack xKing directly if there are not pieces in
+ // the way?
+ //
+ if (0 != (CHECK_VECTOR_WITH_INDEX((int)cTo - (int)xKing,
+ GET_COLOR(pPiece)) &
+ (1 << PIECE_TYPE(pPiece))))
+ {
+ //
+ // We don't toggle knight squares as unblocked... so if the sq
+ // is unblocked then piece must not be a knight. Note: this
+ // logical OR replaced with bitwise equivalent for speed.
+ //
+ if ((SQUARE_IS_UNBLOCKED(cTo)) | (IS_KNIGHT(pPiece)))
+ {
+#ifdef DEBUG
+ if (SQUARE_IS_UNBLOCKED(cTo))
+ {
+ ASSERT(!IS_KNIGHT(pPiece));
+ }
+ else
+ {
+ ASSERT(IS_KNIGHT(pPiece));
+ }
+#endif
+ return(MOVE_FLAG_CHECKING);
+ }
+ ASSERT(!IS_PAWN(pPiece));
+
+ //
+ // Special case: if this is a pawn that just promoted, and the
+ // from square was unblocked, this pawn in its new state can
+ // check the king.
+ //
+ if ((DIR_FROM_SQ_TO_KING(cFrom) == iDelta) &&
+ (SQUARE_IS_UNBLOCKED(cFrom)))
+ {
+ return(MOVE_FLAG_CHECKING);
+ }
+ }
+
+ exposes:
+ //
+ // We now know that the move itself does not directly attack the
+ // enemy king. We will now see if that move exposes check to the
+ // enemy king.
+ //
+
+ //
+ // A move cannot expose check directly if its from square is not
+ // an unblocked square. But if it is unblocked, we will have to
+ // scan behind the piece to see if there is some attacker.
+ //
+ if (SQUARE_IS_UNBLOCKED(cFrom))
+ {
+ //
+ // Piece moves towards the king on the same ray? Cannot be
+ // check. Piece moves away from the king on the same ray?
+ // Cannot be check.
+ //
+ if (DIRECTION_BETWEEN_SQUARES(cTo, xKing) !=
+ DIR_FROM_SQ_TO_KING(cFrom))
+ {
+ if (IS_ON_BOARD(FasterExposesCheck(pos, cFrom, xKing)))
+ {
+ ASSERT(IS_ON_BOARD(ExposesCheck(pos, cFrom, xKing)));
+ return(MOVE_FLAG_CHECKING);
+ }
+ }
+ }
+
+ //
+ // There is one last special case. If this move was enpassant
+ // it's possible that the removal of the enemy pawn has exposed
+ // the enemy king to check.
+ //
+ // rnb2bnr/ppp3pp/4k3/1B1qPpB1/4p1Q1/8/PPP2PPP/RN2K1NR w - f6 0 0
+ // rn5r/pp4pp/8/5qk1/1b2pP2/4K3/PPP3PP/RN4NR b - f3 0 0
+ //
+ if (IS_ENPASSANT(mv))
+ {
+ ASSERT(IS_PAWN(pPiece));
+ ASSERT(VALID_EP_SQUARE(cTo));
+ ASSERT(cTo == pos->cEpSquare);
+ cEpSquare = cTo + 16 * g_iBehind[(GET_COLOR(pPiece))];
+#ifdef DEBUG
+ if (GET_COLOR(pPiece))
+ {
+ ASSERT(cEpSquare == (cTo + 16));
+ }
+ else
+ {
+ ASSERT(cEpSquare == (cTo - 16));
+ }
+#endif
+ ASSERT(IS_PAWN(pos->rgSquare[cEpSquare].pPiece));
+ ASSERT(OPPOSITE_COLORS(pPiece, pos->rgSquare[cEpSquare].pPiece));
+
+ //
+ // Logical OR replaced with bitwise for speed.
+ //
+ if (((SQUARE_IS_UNBLOCKED(cEpSquare)) |
+ (SQUARE_IS_UNBLOCKED(cFrom))) &&
+ (IS_ON_BOARD(ExposesCheckEp(pos, cEpSquare, cFrom, cTo, xKing))))
+ {
+ return(MOVE_FLAG_CHECKING);
+ }
+ }
+
+ //
+ // No direct check + no exposed check = no check.
+ //
+ return(0);
+}
+
+
+static void FORCEINLINE
+_AddNormalMove(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cFrom,
+ IN COOR cTo,
+ IN PIECE pCap)
+/**
+
+Routine description:
+
+ Adds a move from the generator functions to the stack of generated
+ moves.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cFrom : the move's from square
+ COOR cTo : the move's to square
+ PIECE pCap : what was captured (if anything)
+
+Return value:
+
+ static void FORCEINLINE
+
+**/
+{
+ PIECE pMoved = pos->rgSquare[cFrom].pPiece;
+ ULONG uPly = pStack->uPly;
+ MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
+
+ ASSERT(pMoved);
+ ASSERT(GET_COLOR(pMoved) == pos->uToMove);
+ ASSERT(IS_ON_BOARD(cFrom));
+ ASSERT(IS_ON_BOARD(cTo));
+
+ pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
+ pMvf->mv.uMove = MAKE_MOVE_WITH_NO_PROM_OR_FLAGS(cFrom, cTo, pMoved, pCap);
+ pStack->uEnd[uPly]++;
+
+ ASSERT(pMvf->mv.uMove == MAKE_MOVE(cFrom, cTo, pMoved, pCap, 0, 0));
+ ASSERT(SanityCheckMove(pos, pMvf->mv));
+}
+
+
+static void FORCEINLINE
+_AddEnPassant(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cFrom,
+ IN COOR cTo)
+/**
+
+Routine description:
+
+ Add an en passant pawn capture to the move stack.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cFrom : the from square
+ COOR cTo : the to square (and en passant square)
+
+Return value:
+
+ static void
+
+**/
+{
+ PIECE pMoved = BLACK_PAWN | pos->uToMove;
+ PIECE pCaptured = FLIP(pMoved);
+ ULONG uPly = pStack->uPly;
+ MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
+
+ ASSERT(pCaptured == (BLACK_PAWN | (FLIP(pos->uToMove))));
+ ASSERT(IS_ON_BOARD(cFrom));
+ ASSERT(IS_ON_BOARD(cTo));
+ ASSERT(RANK4(cFrom) || RANK5(cFrom));
+ ASSERT(VALID_EP_SQUARE(cTo));
+ ASSERT(IS_PAWN(pos->rgSquare[cFrom].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cFrom].pPiece) == pos->uToMove);
+
+ pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
+ pMvf->mv.uMove =
+ MAKE_MOVE(cFrom, cTo, pMoved, pCaptured, 0, MOVE_FLAG_SPECIAL);
+ pStack->uEnd[uPly]++;
+
+ ASSERT(SanityCheckMove(pos, pMvf->mv));
+}
+
+
+static void FORCEINLINE
+_AddCastle(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cFrom,
+ IN COOR cTo)
+/**
+
+Routine description:
+
+ Add a castle move to the move stack.
+
+Parameters:
+
+ MOVE_STACK *pStack,
+ POSITION *pos,
+ COOR cFrom,
+ COOR cTo
+
+Return value:
+
+ static void FORCEINLINE
+
+**/
+{
+ PIECE pMoved = BLACK_KING | pos->uToMove;
+ ULONG uPly = pStack->uPly;
+ MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
+
+ ASSERT(IS_ON_BOARD(cFrom));
+ ASSERT(IS_ON_BOARD(cTo));
+ ASSERT((E1 == cFrom) || (E8 == cFrom));
+ ASSERT(IS_KING(pos->rgSquare[cFrom].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cFrom].pPiece) == pos->uToMove);
+
+ pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
+ pMvf->mv.uMove = MAKE_MOVE(cFrom, cTo, pMoved, 0, 0, MOVE_FLAG_SPECIAL);
+ pStack->uEnd[uPly]++;
+
+ ASSERT(SanityCheckMove(pos, pMvf->mv));
+}
+
+
+static void FORCEINLINE
+_AddPromote(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cFrom,
+ IN COOR cTo)
+/**
+
+Routine description:
+
+ Adds a pawn promotion (set of) moves to the move stack. This can
+ be a capture-promotion or non-capture promotion. This function
+ pushes several moves onto the move stack, one for each possible
+ promotion target.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cFrom : the move's from square
+ COOR cTo : the move's to square
+
+Return value:
+
+ static void
+
+**/
+{
+ PIECE pMoved = BLACK_PAWN | pos->uToMove;
+ PIECE pCaptured = pos->rgSquare[cTo].pPiece;
+ ULONG uPly = pStack->uPly;
+ MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
+ ULONG u;
+
+#define ARRAY_LENGTH_TARG (4)
+ const static PIECE pTarg[ARRAY_LENGTH_TARG] = { BLACK_QUEEN,
+ BLACK_ROOK,
+ BLACK_BISHOP,
+ BLACK_KNIGHT };
+
+ ASSERT(IS_ON_BOARD(cFrom));
+ ASSERT(IS_ON_BOARD(cTo));
+ ASSERT(RANK1(cTo) || RANK8(cTo));
+ ASSERT(RANK7(cFrom) || RANK2(cFrom));
+ ASSERT(ARRAY_LENGTH(pTarg) == ARRAY_LENGTH_TARG);
+ ASSERT(IS_PAWN(pos->rgSquare[cFrom].pPiece));
+
+ for (u = 0; u < ARRAY_LENGTH_TARG; u++)
+ {
+ pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
+ pMvf->mv.uMove = MAKE_MOVE(cFrom, cTo, pMoved, pCaptured,
+ pTarg[u] | pos->uToMove, MOVE_FLAG_SPECIAL);
+ pStack->uEnd[uPly]++;
+ ASSERT(SanityCheckMove(pos, pMvf->mv));
+ }
+#undef ARRAY_LENGTH_TARG
+}
+
+
+static void FORCEINLINE
+_AddDoubleJump(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cFrom,
+ IN COOR cTo)
+/**
+
+Routine description:
+
+ This function adds a pawn double jump to the move stack.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cFrom : the move's from square
+ COOR cTo : the move's to square
+
+Return value:
+
+ static void
+
+**/
+{
+ PIECE pMoved = BLACK_PAWN | pos->uToMove;
+ ULONG uPly = pStack->uPly;
+ MOVE_STACK_MOVE_VALUE_FLAGS *pMvf;
+
+ ASSERT(IS_ON_BOARD(cFrom));
+ ASSERT(IS_ON_BOARD(cTo));
+ ASSERT(RANK2(cFrom) || RANK7(cFrom));
+ ASSERT(RANK4(cTo) || RANK5(cTo));
+ ASSERT(IS_PAWN(pos->rgSquare[cFrom].pPiece));
+
+ pMvf = &(pStack->mvf[pStack->uEnd[uPly]]);
+ pMvf->mv.uMove = MAKE_MOVE(cFrom, cTo, pMoved, 0, 0, MOVE_FLAG_SPECIAL);
+ pStack->uEnd[uPly]++;
+
+ ASSERT(SanityCheckMove(pos, pMvf->mv));
+}
+
+
+const INT g_iNDeltas[] = {
+ -33, -31, -18, -14, +14, +18, +31, +33, 0 };
+
+void
+GenerateKnight(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cKnight)
+/**
+
+Routine description:
+
+ This function is called by GenerateMoves' JumpTable. Its job
+ is to generate and push pseudo-legal knight moves for the
+ knight sitting on square cKnight.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cKnight : the knight's location
+
+Return value:
+
+ void
+
+**/
+{
+ COOR c;
+ ULONG u = 0;
+ PIECE p;
+
+ ASSERT(IS_KNIGHT(pos->rgSquare[cKnight].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == pos->uToMove);
+ ASSERT(g_iNDeltas[u] != 0);
+
+ do
+ {
+ c = cKnight + g_iNDeltas[u];
+ u++;
+ if (IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+
+ //
+ // This logical OR replaced with bitwise OR; the effect is
+ // the same the the bitwise is marginally faster.
+ //
+ if (IS_EMPTY(p) | (OPPOSITE_COLORS(p, pos->uToMove)))
+ {
+ _AddNormalMove(pStack, pos, cKnight, c, p);
+ }
+ }
+ }
+ while(0 != g_iNDeltas[u]);
+}
+
+void
+GenerateWhiteKnight(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cKnight)
+{
+ COOR c;
+ ULONG u = 0;
+ PIECE p;
+
+ ASSERT(IS_KNIGHT(pos->rgSquare[cKnight].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == pos->uToMove);
+ ASSERT(g_iNDeltas[u] != 0);
+
+ do
+ {
+ c = cKnight + g_iNDeltas[u];
+ u++;
+ if (IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+
+ //
+ // Note: this test covers empty squares also -- it is just
+ // testing that the low order bit (color bit) of p is zero
+ // which is true for black pieces and empty squares. This
+ // is done for speed over readability.
+ //
+ if (GET_COLOR(p) == BLACK)
+ {
+ ASSERT(IS_EMPTY(p) || (GET_COLOR(p) == BLACK));
+ _AddNormalMove(pStack, pos, cKnight, c, p);
+ }
+ }
+ }
+ while(0 != g_iNDeltas[u]);
+}
+
+//
+// These logical AND/ORs replaced with bitwise AND/OR; the effect is
+// the same the the bitwise is marginally faster.
+//
+#define BLOCKS_THE_CHECK(sq) \
+ ((iAttackDelta != 0) & \
+ (iAttackDelta == DIRECTION_BETWEEN_SQUARES(cAttacker, (sq))) & \
+ (((cAttacker > cKing) & ((sq) > cKing)) | \
+ ((cAttacker < cKing) & ((sq) < cKing))))
+
+void
+SaveMeKnight(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cKnight,
+ IN COOR cKing,
+ IN COOR cAttacker)
+/**
+
+Routine description:
+
+ This routine is called by GenerateEscapes' JumpTable. Its job is
+ to generate moves by the knight on square cKnight that alleviate
+ check to the king on square cKing. The (lone) piece attacking the
+ king is on square cAttacker.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cKnight : the location of the knight
+ COOR cKing : the location of the friendly king
+ COOR cAttacker : the location of the checking piece
+
+Return value:
+
+ void
+
+**/
+{
+ COOR c, cExposed;
+ int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
+ ULONG u = 0;
+ PIECE p;
+
+ ASSERT(InCheck(pos, pos->uToMove));
+ ASSERT(IS_KNIGHT(pos->rgSquare[cKnight].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) ==
+ GET_COLOR(pos->rgSquare[cKing].pPiece));
+ ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
+ pos->rgSquare[cKing].pPiece));
+ ASSERT(g_iNDeltas[u] != 0);
+
+ do
+ {
+ c = cKnight + g_iNDeltas[u];
+ u++;
+ if (IS_ON_BOARD(c))
+ {
+ if ((c == cAttacker) || BLOCKS_THE_CHECK(c))
+ {
+ cExposed = ExposesCheck(pos, cKnight, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(!p ||
+ OPPOSITE_COLORS(p, pos->rgSquare[cKnight].pPiece));
+ ASSERT(!p || (c == cAttacker));
+ _AddNormalMove(pStack, pos, cKnight, c, p);
+ }
+ }
+ }
+ }
+ while(0 != g_iNDeltas[u]);
+}
+
+
+const INT g_iBDeltas[] = {
+ -17, -15, +15, +17, 0 };
+
+void
+GenerateBishop(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cBishop)
+/**
+
+Routine description:
+
+ This routine is called by GenerateMoves' JumpTable to generate
+ pseudo-legal bishop moves by the piece at cBishop.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cBishop : the bishop location
+
+Return value:
+
+ void
+
+**/
+{
+ COOR c;
+ PIECE p;
+
+ ASSERT(IS_BISHOP(pos->rgSquare[cBishop].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cBishop].pPiece) == pos->uToMove);
+
+ //
+ // Note: manually unrolled loop
+ //
+ c = cBishop - 17;
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if (IS_EMPTY(p))
+ {
+ _AddNormalMove(pStack, pos, cBishop, c, 0);
+ }
+ else
+ {
+ if (OPPOSITE_COLORS(p, pos->uToMove))
+ {
+ _AddNormalMove(pStack, pos, cBishop, c, p);
+ }
+ break;
+ }
+ c += -17;
+ }
+
+ c = cBishop - 15;
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if (IS_EMPTY(p))
+ {
+ _AddNormalMove(pStack, pos, cBishop, c, 0);
+ }
+ else
+ {
+ if (OPPOSITE_COLORS(p, pos->uToMove))
+ {
+ _AddNormalMove(pStack, pos, cBishop, c, p);
+ }
+ break;
+ }
+ c += -15;
+ }
+
+ c = cBishop + 15;
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if (IS_EMPTY(p))
+ {
+ _AddNormalMove(pStack, pos, cBishop, c, 0);
+ }
+ else
+ {
+ if (OPPOSITE_COLORS(p, pos->uToMove))
+ {
+ _AddNormalMove(pStack, pos, cBishop, c, p);
+ }
+ break;
+ }
+ c += 15;
+ }
+
+ c = cBishop + 17;
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if (IS_EMPTY(p))
+ {
+ _AddNormalMove(pStack, pos, cBishop, c, 0);
+ }
+ else
+ {
+ if (OPPOSITE_COLORS(p, pos->uToMove))
+ {
+ _AddNormalMove(pStack, pos, cBishop, c, p);
+ }
+ break;
+ }
+ c += 17;
+ }
+}
+
+
+void
+SaveMeBishop(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cBishop,
+ IN COOR cKing,
+ IN COOR cAttacker)
+/**
+
+Routine description:
+
+ This routine is called by GenerateEscapes' JumpTable in order to
+ generate moves by the bishop at cBishop to alleviate check on its
+ friendly king (at cKing) which is being checked by a piece at
+ cAttacker.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cBishop : the bishop's location
+ COOR cKing : the friendly king's location
+ COOR cAttacker : the checking piece's location
+
+Return value:
+
+ void
+
+**/
+{
+ int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
+ COOR c;
+ COOR cExposed;
+ ULONG u = 0;
+ PIECE p;
+
+ ASSERT(InCheck(pos, pos->uToMove));
+ ASSERT(IS_BISHOP(pos->rgSquare[cBishop].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cBishop].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cBishop].pPiece) ==
+ GET_COLOR(pos->rgSquare[cKing].pPiece));
+ ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
+ pos->rgSquare[cKing].pPiece));
+ ASSERT(g_iBDeltas[u] != 0);
+
+ do
+ {
+ c = cBishop + g_iBDeltas[u];
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if ((c == cAttacker) || BLOCKS_THE_CHECK(c))
+ {
+ ASSERT(!p ||
+ OPPOSITE_COLORS(p, pos->rgSquare[cBishop].pPiece));
+ ASSERT(!p || (c == cAttacker));
+ cExposed = ExposesCheck(pos, cBishop, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == c))
+ {
+ _AddNormalMove(pStack, pos, cBishop, c, p);
+ break;
+ }
+ }
+ else if (!IS_EMPTY(p))
+ {
+ break;
+ }
+ c += g_iBDeltas[u];
+ }
+ u++;
+ }
+ while(0 != g_iBDeltas[u]);
+}
+
+
+const INT g_iRDeltas[] = {
+ -1, +1, +16, -16, 0 };
+
+
+void
+GenerateRook(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cRook)
+/**
+
+Routine description:
+
+ This routine is called by GenerateMoves' JumpTable in order to
+ generate pseudo-legal moves for the rook at position cRook.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cRook : the rook's location
+
+Return value:
+
+ void
+
+**/
+{
+ COOR c;
+ PIECE p;
+
+ ASSERT(IS_ROOK(pos->rgSquare[cRook].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == pos->uToMove);
+
+ //
+ // Note: manually unrolled loop
+ //
+ c = cRook - 1;
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if (IS_EMPTY(p))
+ {
+ _AddNormalMove(pStack, pos, cRook, c, 0);
+ }
+ else
+ {
+ if (OPPOSITE_COLORS(p, pos->uToMove))
+ {
+ _AddNormalMove(pStack, pos, cRook, c, p);
+ }
+ break;
+ }
+ c += -1;
+ }
+
+ c = cRook + 1;
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if (IS_EMPTY(p))
+ {
+ _AddNormalMove(pStack, pos, cRook, c, 0);
+ }
+ else
+ {
+ if (OPPOSITE_COLORS(p, pos->uToMove))
+ {
+ _AddNormalMove(pStack, pos, cRook, c, p);
+ }
+ break;
+ }
+ c += 1;
+ }
+
+ c = cRook + 16;
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if (IS_EMPTY(p))
+ {
+ _AddNormalMove(pStack, pos, cRook, c, 0);
+ }
+ else
+ {
+ if (OPPOSITE_COLORS(p, pos->uToMove))
+ {
+ _AddNormalMove(pStack, pos, cRook, c, p);
+ }
+ break;
+ }
+ c += 16;
+ }
+
+ c = cRook - 16;
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if (IS_EMPTY(p))
+ {
+ _AddNormalMove(pStack, pos, cRook, c, 0);
+ }
+ else
+ {
+ if (OPPOSITE_COLORS(p, pos->uToMove))
+ {
+ _AddNormalMove(pStack, pos, cRook, c, p);
+ }
+ break;
+ }
+ c += -16;
+ }
+}
+
+void
+SaveMeRook(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cRook,
+ IN COOR cKing,
+ IN COOR cAttacker)
+/**
+
+Routine description:
+
+ This routine is called by GenerateEscapes' JumpTable in order to
+ generate moves by the rook at location cRook that alleviate check
+ to the king at cKing. The lone checking piece is at cAttacker.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cRook : the rook's location
+ COOR cKing : the king's location
+ COOR cAttacker : the checker's location
+
+Return value:
+
+ void
+
+**/
+{
+ int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
+ COOR c;
+ COOR cExposed;
+ ULONG u = 0;
+ PIECE p;
+
+ ASSERT(InCheck(pos, pos->uToMove));
+ ASSERT(IS_ROOK(pos->rgSquare[cRook].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) ==
+ GET_COLOR(pos->rgSquare[cKing].pPiece));
+ ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
+ pos->rgSquare[cKing].pPiece));
+ ASSERT(g_iRDeltas[u] != 0);
+
+ do
+ {
+ c = cRook + g_iRDeltas[u];
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if ((c == cAttacker) || BLOCKS_THE_CHECK(c))
+ {
+ ASSERT(!p ||
+ OPPOSITE_COLORS(p, pos->rgSquare[cRook].pPiece));
+ ASSERT(!p || (c == cAttacker));
+ cExposed = ExposesCheck(pos, cRook, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == c))
+ {
+ _AddNormalMove(pStack, pos, cRook, c, p);
+ break;
+ }
+ }
+ else if (!IS_EMPTY(p))
+ {
+ break;
+ }
+ c += g_iRDeltas[u];
+ }
+ u++;
+ }
+ while(0 != g_iRDeltas[u]);
+}
+
+
+void
+GenerateQueen(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cQueen)
+/**
+
+Routine description:
+
+ This routine is called by GenerateMoves' JumpTable in order to
+ generate pseudo-legal moves for the queen at position cQueen.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cQueen : the queen's location
+
+Return value:
+
+ void
+
+**/
+{
+ COOR c;
+ ULONG u = 0;
+ PIECE p;
+
+ ASSERT(IS_QUEEN(pos->rgSquare[cQueen].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == pos->uToMove);
+ ASSERT(g_iQKDeltas[u] != 0);
+
+ do
+ {
+ c = cQueen + g_iQKDeltas[u];
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if (IS_EMPTY(p))
+ {
+ _AddNormalMove(pStack, pos, cQueen, c, 0);
+ }
+ else
+ {
+ if (OPPOSITE_COLORS(p, pos->uToMove))
+ {
+ _AddNormalMove(pStack, pos, cQueen, c, p);
+ }
+ break;
+ }
+ c += g_iQKDeltas[u];
+ }
+ u++;
+ }
+ while(0 != g_iQKDeltas[u]);
+}
+
+
+void
+SaveMeQueen(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cQueen,
+ IN COOR cKing,
+ IN COOR cAttacker)
+/**
+
+Routine description:
+
+ This routine is called by code in GenerateEscapes in order to
+ generate moves by the queen at square cQueen that alleviate check
+ on the friendly king at square cKing. The lone enemy piece that
+ is checking the king is sitting on square cAttacker.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cQueen : the queen's location
+ COOR cKing : the friendly king's location
+ COOR cAttacker : the attacker's location
+
+Return value:
+
+ void
+
+**/
+{
+ COOR c;
+ COOR cExposed;
+ int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
+ ULONG u = 0;
+ PIECE p;
+
+ ASSERT(InCheck(pos, pos->uToMove));
+ ASSERT(IS_QUEEN(pos->rgSquare[cQueen].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) ==
+ GET_COLOR(pos->rgSquare[cKing].pPiece));
+ ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
+ pos->rgSquare[cKing].pPiece));
+ ASSERT(g_iQKDeltas[u] != 0);
+
+ do
+ {
+ c = cQueen + g_iQKDeltas[u];
+ while(IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ if ((c == cAttacker) || BLOCKS_THE_CHECK(c))
+ {
+ ASSERT(!p ||
+ OPPOSITE_COLORS(p, pos->rgSquare[cQueen].pPiece));
+ ASSERT(!p || (c == cAttacker));
+ cExposed = ExposesCheck(pos, cQueen, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == c))
+ {
+ _AddNormalMove(pStack, pos, cQueen, c, p);
+ break;
+ }
+ }
+ else if (!IS_EMPTY(p))
+ {
+ break;
+ }
+ c += g_iQKDeltas[u];
+ }
+ u++;
+ }
+ while(0 != g_iQKDeltas[u]);
+}
+
+void
+GenerateBlackKing(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cKing)
+/**
+
+Routine description:
+
+ This function is called by GenerateMoves in order to generate
+ pseudo-legal moves for the black king at square cKing. Note: this
+ function often generates illegal castling moves and relies on the
+ MakeMove code to decide about the legality of the castle.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cKing : the king location
+
+Return value:
+
+ void
+
+**/
+{
+ COOR c;
+ ULONG u = 0;
+ PIECE p;
+
+ ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == pos->uToMove);
+ ASSERT(g_iQKDeltas[u] != 0);
+
+ do
+ {
+ c = cKing + g_iQKDeltas[u];
+ u++;
+ if (IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+
+ //
+ // This logical OR replaced with bitwise OR; the effect is
+ // the same the the bitwise is marginally faster.
+ //
+ if (IS_EMPTY(p) | (GET_COLOR(p)))
+ {
+ _AddNormalMove(pStack, pos, cKing, c, p);
+ }
+ }
+ }
+ while(0 != g_iQKDeltas[u]);
+
+ if ((pos->bvCastleInfo & BLACK_CAN_CASTLE) == 0) return;
+#ifdef DEBUG
+ p = pos->rgSquare[E8].pPiece;
+ ASSERT(IS_KING(p));
+ ASSERT(cKing == E8);
+#endif
+
+ //
+ // Note: no castling through check is enforced at the time the
+ // move is played. This is a "pseudo-legal" move when generated.
+ //
+ if ((pos->bvCastleInfo & CASTLE_BLACK_SHORT) &&
+ (IS_EMPTY(pos->rgSquare[G8].pPiece)) &&
+ (IS_EMPTY(pos->rgSquare[F8].pPiece)))
+ {
+ ASSERT(pos->rgSquare[H8].pPiece == BLACK_ROOK);
+ _AddCastle(pStack, pos, E8, G8);
+ }
+
+ if ((pos->bvCastleInfo & CASTLE_BLACK_LONG) &&
+ (IS_EMPTY(pos->rgSquare[C8].pPiece)) &&
+ (IS_EMPTY(pos->rgSquare[D8].pPiece)) &&
+ (IS_EMPTY(pos->rgSquare[B8].pPiece)))
+ {
+ ASSERT(pos->rgSquare[A8].pPiece == BLACK_ROOK);
+ _AddCastle(pStack, pos, E8, C8);
+ }
+}
+
+//
+// N.B. There is no SaveYourselfBlackKing or SaveYourselfWhiteKing
+// because this functionality is built into phase 1 of
+// GenerateEscapes.
+//
+
+void
+GenerateWhiteKing(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cKing)
+/**
+
+Routine description:
+
+ This function is called by GenerateMoves in order to generate
+ pseudo-legal king moves by the white king at square cKing. Note:
+ it often generates illegal castling moves and relies on the code
+ in MakeMove to determine the legality of castles.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cKing : the king location
+
+Return value:
+
+ void
+
+**/
+{
+ COOR c;
+ ULONG u = 0;
+ PIECE p;
+
+ ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == pos->uToMove);
+ ASSERT(g_iQKDeltas[u] != 0);
+
+ do
+ {
+ c = cKing + g_iQKDeltas[u];
+ u++;
+ if (IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+
+ //
+ // Note: this test covers empty squares also -- it is just
+ // testing that the low order bit (color bit) of p is zero
+ // which is true for black pieces and empty squares. This
+ // is done for speed over readability.
+ //
+ if (GET_COLOR(p) == BLACK)
+ {
+ ASSERT(IS_EMPTY(p) || (GET_COLOR(p) == BLACK));
+ _AddNormalMove(pStack, pos, cKing, c, p);
+ }
+ }
+ }
+ while(0 != g_iQKDeltas[u]);
+
+ if ((pos->bvCastleInfo & WHITE_CAN_CASTLE) == 0) return;
+#ifdef DEBUG
+ p = pos->rgSquare[E1].pPiece;
+ ASSERT(IS_KING(p));
+ ASSERT(cKing == E1);
+#endif
+
+ //
+ // Note: no castling through check is enforced at the time the
+ // move is played. This is a "pseudo-legal" move when generated.
+ //
+ if ((pos->bvCastleInfo & CASTLE_WHITE_SHORT) &&
+ (IS_EMPTY(pos->rgSquare[G1].pPiece)) &&
+ (IS_EMPTY(pos->rgSquare[F1].pPiece)))
+ {
+ ASSERT(pos->rgSquare[H1].pPiece == WHITE_ROOK);
+ _AddCastle(pStack, pos, E1, G1);
+ }
+
+ if ((pos->bvCastleInfo & CASTLE_WHITE_LONG) &&
+ (IS_EMPTY(pos->rgSquare[C1].pPiece)) &&
+ (IS_EMPTY(pos->rgSquare[B1].pPiece)) &&
+ (IS_EMPTY(pos->rgSquare[D1].pPiece)))
+ {
+ ASSERT(pos->rgSquare[A1].pPiece == WHITE_ROOK);
+ _AddCastle(pStack, pos, E1, C1);
+ }
+}
+
+
+void
+GenerateWhitePawn(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cPawn)
+/**
+
+Routine description:
+
+ This function is called by GenerateMoves in order to generate
+ pseudo-legal pawn moves by the pawn at cPawn. It handles
+ en passant captures, double jumps, promotions, etc...
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cPawn : the pawn's location
+
+Return value:
+
+ void
+
+**/
+{
+ ULONG uRank = RANK(cPawn);
+ PIECE p;
+ COOR cTo;
+
+ ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
+ ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7));
+
+ if (uRank != 7)
+ {
+ cTo = cPawn - 16;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ _AddNormalMove(pStack, pos, cPawn, cTo, 0);
+ if (uRank == 2)
+ {
+ cTo = cPawn - 32;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ _AddDoubleJump(pStack, pos, cPawn, cTo);
+ }
+ }
+ }
+ cTo = cPawn - 15;
+ if (IS_ON_BOARD(cTo))
+ {
+ if (cTo == pos->cEpSquare)
+ {
+ _AddEnPassant(pStack, pos, cPawn, cTo);
+ }
+ p = pos->rgSquare[cTo].pPiece;
+
+ //
+ // This logical AND replaced with bitwise AND; the effect
+ // is the same the the bitwise is marginally faster.
+ //
+ if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK))
+ {
+ _AddNormalMove(pStack, pos, cPawn, cTo, p);
+ }
+ }
+ cTo = cPawn - 17;
+ if (IS_ON_BOARD(cTo))
+ {
+ if (cTo == pos->cEpSquare)
+ {
+ _AddEnPassant(pStack, pos, cPawn, cTo);
+ }
+ p = pos->rgSquare[cTo].pPiece;
+
+ //
+ // This logical AND replaced with bitwise AND; the effect
+ // is the same the the bitwise is marginally faster.
+ //
+ if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK))
+ {
+ _AddNormalMove(pStack, pos, cPawn, cTo, p);
+ }
+ }
+ }
+ else
+ {
+ ASSERT(RANK7(cPawn));
+ cTo = cPawn - 16;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ cTo = cPawn - 15;
+ if (IS_ON_BOARD(cTo))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+
+ //
+ // This logical AND replaced with bitwise AND; the effect
+ // is the same the the bitwise is marginally faster.
+ //
+ if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK))
+ {
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ }
+ cTo = cPawn - 17;
+ if (IS_ON_BOARD(cTo))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+
+ //
+ // This logical AND replaced with bitwise AND; the effect
+ // is the same the the bitwise is marginally faster.
+ //
+ if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK))
+ {
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ }
+ }
+}
+
+
+void
+SaveMeWhitePawn(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cPawn,
+ IN COOR cKing,
+ IN COOR cAttacker)
+/**
+
+Routine description:
+
+ This function is called by the GenerateEscapes code in order to
+ ask for a pawn's help in alleviating check on the king at square
+ cKing. The lone attacker to said king is on square cAttacker.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cPawn : the pawn's location
+ COOR cKing : the friendly king's location
+ COOR cAttacker : the location of the lone checker to the king
+
+Return value:
+
+ void
+
+**/
+{
+ ULONG uRank = RANK(cPawn);
+ PIECE p;
+ COOR cTo;
+ COOR cExposed;
+ int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
+
+ ASSERT(InCheck(pos, pos->uToMove));
+ ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == WHITE);
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) ==
+ GET_COLOR(pos->rgSquare[cKing].pPiece));
+ ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
+ pos->rgSquare[cKing].pPiece));
+ ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7));
+
+ if (uRank != 7)
+ {
+ cTo = cPawn - 16;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ if (BLOCKS_THE_CHECK(cTo) &&
+ !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing)))
+ {
+ _AddNormalMove(pStack, pos, cPawn, cTo, 0);
+ }
+
+ if (uRank == 2)
+ {
+ cTo = cPawn - 32;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece) &&
+ BLOCKS_THE_CHECK(cTo) &&
+ !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing)))
+ {
+ _AddDoubleJump(pStack, pos, cPawn, cTo);
+ }
+ }
+ }
+
+ cTo = cPawn - 15;
+ if (cTo == cAttacker)
+ {
+ cExposed = ExposesCheck(pos, cPawn, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(GET_COLOR(p) == BLACK);
+ ASSERT(IS_ON_BOARD(cTo));
+ _AddNormalMove(pStack, pos, cPawn, cTo, p);
+ }
+ }
+
+ //
+ // N.B. There is no possible way to block check with an
+ // en-passant capture because by definition the last move made
+ // by the other side was the pawn double jump. So only handle
+ // if the double jumping enemy pawn is the attacker and we can
+ // kill it en passant.
+ //
+ if ((cTo == pos->cEpSquare) && (cAttacker == cTo + 16))
+ {
+ _AddEnPassant(pStack, pos, cPawn, cTo);
+ }
+
+ cTo = cPawn - 17;
+ if (cTo == cAttacker)
+ {
+ cExposed = ExposesCheck(pos, cPawn, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(GET_COLOR(p) == BLACK);
+ ASSERT(IS_ON_BOARD(cTo));
+ _AddNormalMove(pStack, pos, cPawn, cTo, p);
+ }
+ }
+ if ((cTo == pos->cEpSquare) && (cAttacker == cTo + 16))
+ {
+ _AddEnPassant(pStack, pos, cPawn, cTo);
+ }
+ }
+ else
+ {
+ ASSERT(RANK7(cPawn));
+ cTo = cPawn - 16;
+ if (BLOCKS_THE_CHECK(cTo) && IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+
+ cTo = cPawn - 15;
+ if (cTo == cAttacker)
+ {
+ cExposed = ExposesCheck(pos, cPawn, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(GET_COLOR(p) == BLACK);
+ ASSERT(IS_ON_BOARD(cTo));
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ }
+
+ cTo = cPawn - 17;
+ if (cTo == cAttacker)
+ {
+ cExposed = ExposesCheck(pos, cPawn, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(GET_COLOR(p) == BLACK);
+ ASSERT(IS_ON_BOARD(cTo));
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ }
+ }
+}
+
+
+void
+GenerateBlackPawn(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cPawn)
+/**
+
+Routine description:
+
+ This code is called by GenerateMoves in order to handle move
+ generation for the black pawn on square cPawn. It handles
+ en passant captures, double jumps, and promotion.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the position
+ COOR cPawn : the pawn location
+
+Return value:
+
+ void
+
+**/
+{
+ ULONG uRank = RANK(cPawn);
+ PIECE p;
+ COOR cTo;
+
+ ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
+ ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7));
+
+ if (uRank != 2)
+ {
+ cTo = cPawn + 16;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ _AddNormalMove(pStack, pos, cPawn, cTo, 0);
+ if (uRank == 7)
+ {
+ cTo = cPawn + 32;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ _AddDoubleJump(pStack, pos, cPawn, cTo);
+ }
+ }
+ }
+ cTo = cPawn + 15;
+ if (IS_ON_BOARD(cTo))
+ {
+ if (cTo == pos->cEpSquare)
+ {
+ _AddEnPassant(pStack, pos, cPawn, cTo);
+ }
+ p = pos->rgSquare[cTo].pPiece;
+
+ //
+ // This logical AND replaced with bitwise AND; the effect
+ // is the same the the bitwise is marginally faster.
+ //
+ if (!IS_EMPTY(p) & (GET_COLOR(p)))
+ {
+ _AddNormalMove(pStack, pos, cPawn, cTo, p);
+ }
+ }
+ cTo = cPawn + 17;
+ if (IS_ON_BOARD(cTo))
+ {
+ if (cTo == pos->cEpSquare)
+ {
+ _AddEnPassant(pStack, pos, cPawn, cTo);
+ }
+ p = pos->rgSquare[cTo].pPiece;
+
+ //
+ // This logical AND replaced with bitwise AND; the effect
+ // is the same the the bitwise is marginally faster.
+ //
+ if (!IS_EMPTY(p) & (GET_COLOR(p)))
+ {
+ _AddNormalMove(pStack, pos, cPawn, cTo, p);
+ }
+ }
+ }
+ else
+ {
+ ASSERT(RANK2(cPawn));
+ cTo = cPawn + 16;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ cTo = cPawn + 15;
+ if (IS_ON_BOARD(cTo))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+
+ //
+ // This logical AND replaced with bitwise AND; the effect
+ // is the same the the bitwise is marginally faster.
+ //
+ if (!IS_EMPTY(p) & (GET_COLOR(p)))
+ {
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ }
+ cTo = cPawn + 17;
+ if (IS_ON_BOARD(cTo))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+
+ //
+ // This logical AND replaced with bitwise AND; the effect
+ // is the same the the bitwise is marginally faster.
+ //
+ if (!IS_EMPTY(p) & (GET_COLOR(p)))
+ {
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ }
+ }
+}
+
+
+void
+SaveMeBlackPawn(IN MOVE_STACK *pStack,
+ IN POSITION *pos,
+ IN COOR cPawn,
+ IN COOR cKing,
+ IN COOR cAttacker)
+/**
+
+Routine description:
+
+ This code is called to ask for a black pawn's help in alleviating
+ check to its king. The pawn is on square cPawn. The friendly
+ king is on square cKing. The lone enemy piece attacking it is on
+ square cAttacker.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+ COOR cPawn : the pawn location
+ COOR cKing : the friendly king location
+ COOR cAttacker : the lone checker location
+
+Return value:
+
+ void
+
+**/
+{
+ ULONG uRank = RANK(cPawn);
+ PIECE p;
+ COOR cTo;
+ COOR cExposed;
+ int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing);
+
+ ASSERT(InCheck(pos, pos->uToMove));
+ ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == BLACK);
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove);
+ ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) ==
+ GET_COLOR(pos->rgSquare[cKing].pPiece));
+ ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece,
+ pos->rgSquare[cKing].pPiece));
+ ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7));
+
+ if (uRank != 2)
+ {
+ cTo = cPawn + 16;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ if (BLOCKS_THE_CHECK(cTo) &&
+ !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing)))
+ {
+ _AddNormalMove(pStack, pos, cPawn, cTo, 0);
+ }
+
+ if (uRank == 7)
+ {
+ cTo = cPawn + 32;
+ if (IS_EMPTY(pos->rgSquare[cTo].pPiece) &&
+ BLOCKS_THE_CHECK(cTo) &&
+ !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing)))
+ {
+ _AddDoubleJump(pStack, pos, cPawn, cTo);
+ }
+ }
+ }
+
+ cTo = cPawn + 15;
+ if (cTo == cAttacker)
+ {
+ cExposed = ExposesCheck(pos, cPawn, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(GET_COLOR(p) == WHITE);
+ ASSERT(IS_ON_BOARD(cTo));
+ _AddNormalMove(pStack, pos, cPawn, cTo, p);
+ }
+ }
+
+ //
+ // N.B. There is no possible way to block check with an
+ // en-passant capture because by definition the last move made
+ // by the other side was the pawn double jump. So only handle
+ // if the double jumping enemy pawn is the attacker and we can
+ // kill it en passant.
+ //
+ if ((cTo == pos->cEpSquare) &&
+ (cAttacker == cTo - 16))
+ {
+ _AddEnPassant(pStack, pos, cPawn, cTo);
+ }
+
+ cTo = cPawn + 17;
+ if (cTo == cAttacker)
+ {
+ cExposed = ExposesCheck(pos, cPawn, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(GET_COLOR(p) == WHITE);
+ ASSERT(IS_ON_BOARD(cTo));
+ _AddNormalMove(pStack, pos, cPawn, cTo, p);
+ }
+ }
+ if ((cTo == pos->cEpSquare) &&
+ (cAttacker == cTo - 16))
+ {
+ _AddEnPassant(pStack, pos, cPawn, cTo);
+ }
+ }
+ else
+ {
+ ASSERT(RANK2(cPawn));
+ cTo = cPawn + 16;
+ if (BLOCKS_THE_CHECK(cTo) &&
+ IS_EMPTY(pos->rgSquare[cTo].pPiece))
+ {
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+
+ cTo = cPawn + 15;
+ if (cTo == cAttacker)
+ {
+ cExposed = ExposesCheck(pos, cPawn, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(GET_COLOR(p) == WHITE);
+ ASSERT(IS_ON_BOARD(cTo));
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ }
+
+ cTo = cPawn + 17;
+ if (cTo == cAttacker)
+ {
+ cExposed = ExposesCheck(pos, cPawn, cKing);
+ if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker))
+ {
+ p = pos->rgSquare[cTo].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(GET_COLOR(p) == WHITE);
+ ASSERT(IS_ON_BOARD(cTo));
+ _AddPromote(pStack, pos, cPawn, cTo);
+ }
+ }
+ }
+}
+
+
+void
+InvalidGenerator(IN UNUSED MOVE_STACK *pStack,
+ IN UNUSED POSITION *pos,
+ IN UNUSED COOR cPawn)
+/**
+
+Routine description:
+
+ If you'd like to make a call, please hang up and try your call
+ again...
+
+Parameters:
+
+ IN UNUSED MOVE_STACK *pStack,
+ IN UNUSED POSITION *pos,
+ IN UNUSED COOR cPawn
+
+Return value:
+
+ void
+
+**/
+{
+ UtilPanic(SHOULD_NOT_GET_HERE,
+ NULL, NULL, NULL, NULL,
+ __FILE__, __LINE__);
+}
+
+
+void
+InvalidSaveMe(IN UNUSED MOVE_STACK *pStack,
+ IN UNUSED POSITION *pos,
+ IN UNUSED COOR cPawn,
+ IN UNUSED COOR cKing,
+ IN UNUSED COOR cAttacker)
+/**
+
+Routine description:
+
+ ...if you need help, please dial the operator.
+
+Parameters:
+
+ IN UNUSED MOVE_STACK *pStack,
+ IN UNUSED POSITION *pos,
+ IN UNUSED COOR cPawn,
+ IN UNUSED COOR cKing,
+ IN UNUSED COOR cAttacker
+
+Return value:
+
+ void
+
+**/
+{
+ UtilPanic(SHOULD_NOT_GET_HERE,
+ NULL, NULL, NULL, NULL,
+ __FILE__, __LINE__);
+}
+
+
+static void
+_GenerateAllMoves(IN MOVE_STACK *pStack,
+ IN POSITION *pos)
+/**
+
+Routine description:
+
+ This code generates all pseudo-legal moves in a position. Please
+ see notes at the top of this module for details.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board position
+
+Return value:
+
+ static void
+
+**/
+{
+ COOR c;
+ static void (*JumpTable[])(MOVE_STACK *, POSITION *, COOR) =
+ {
+ InvalidGenerator, // EMPTY
+ InvalidGenerator, // EMPTY | WHITE
+ InvalidGenerator, // 2 (BLACK_PAWN)
+ InvalidGenerator, // 3 (WHITE_PAWN)
+ GenerateKnight, // 4 (BLACK_KNIGHT)
+ GenerateWhiteKnight, // 5 (WHITE_KNIGHT)
+ GenerateBishop, // 6 (BLACK_BISHOP)
+ GenerateBishop, // 7 (WHITE_BISHOP)
+ GenerateRook, // 8 (BLACK_ROOK)
+ GenerateRook, // 9 (WHITE_ROOK)
+ GenerateQueen, // 10 (BLACK_QUEEN)
+ GenerateQueen, // 11 (WHITE_QUEEN)
+ GenerateBlackKing, // 12 (BLACK_KING)
+ GenerateWhiteKing // 13 (WHITE_KING)
+ };
+ ULONG u;
+#ifdef DEBUG
+ PIECE p;
+#endif
+
+ for(u = pos->uNonPawnCount[pos->uToMove][0] - 1;
+ u != (ULONG)-1;
+ u--)
+ {
+ c = pos->cNonPawns[pos->uToMove][u];
+#ifdef DEBUG
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(!IS_PAWN(p));
+ ASSERT(GET_COLOR(p) == pos->uToMove);
+#endif
+ (JumpTable[pos->rgSquare[c].pPiece])(pStack, pos, c);
+ }
+
+ if (pos->uToMove == BLACK)
+ {
+ for(u = 0; u < pos->uPawnCount[BLACK]; u++)
+ {
+ c = pos->cPawns[BLACK][u];
+#ifdef DEBUG
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(IS_PAWN(p));
+ ASSERT(GET_COLOR(p) == pos->uToMove);
+#endif
+ GenerateBlackPawn(pStack, pos, c);
+ }
+ } else {
+ ASSERT(pos->uToMove == WHITE);
+ for(u = 0; u < pos->uPawnCount[WHITE]; u++)
+ {
+ c = pos->cPawns[WHITE][u];
+#ifdef DEBUG
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(IS_PAWN(p));
+ ASSERT(GET_COLOR(p) == pos->uToMove);
+#endif
+ GenerateWhitePawn(pStack, pos, c);
+ }
+ }
+}
+
+
+
+static ULONG
+_GenerateEscapes(IN MOVE_STACK *pStack,
+ IN POSITION *pos)
+/**
+
+Routine description:
+
+ This function is called when side to move is in check in order to
+ generate pseudo-legal escapes from check. All legal escapes from
+ check are a subset of the moves returned by this function. In
+ general it's pretty good; the only bug I know about is that it
+ will generate a capture that alleviates check where the capturing
+ piece is pinned to the king (and thus the capture is illegal).
+ The purpose of this function is to allow the search to detect when
+ there is one legal reply to check and possibly extend.
+
+Parameters:
+
+ MOVE_STACK *pStack : the move stack
+ POSITION *pos : the board
+
+Return value:
+
+ ULONG : the number of pieces checking the king
+
+**/
+{
+ ULONG u;
+ ULONG v;
+ COOR c;
+ COOR cDefender;
+ PIECE p;
+ PIECE pKing;
+ COOR cKing = pos->cNonPawns[pos->uToMove][0];
+ int iIndex;
+ SEE_LIST rgCheckers;
+ int iDelta;
+ ULONG uReturn = 0;
+ static void (*JumpTable[])
+ (MOVE_STACK *, POSITION *, COOR, COOR, COOR) =
+ {
+ InvalidSaveMe, // EMPTY
+ InvalidSaveMe, // EMPTY | WHITE
+ InvalidSaveMe, // 2 (BLACK_PAWN)
+ InvalidSaveMe, // 3 (WHITE_PAWN)
+ SaveMeKnight, // 4 (BLACK_KNIGHT)
+ SaveMeKnight, // 5 (WHITE_KNIGHT)
+ SaveMeBishop, // 6 (BLACK_BISHOP)
+ SaveMeBishop, // 7 (WHITE_BISHOP)
+ SaveMeRook, // 8 (BLACK_ROOK)
+ SaveMeRook, // 9 (WHITE_ROOK)
+ SaveMeQueen, // 10 (BLACK_QUEEN)
+ SaveMeQueen, // 11 (WHITE_QUEEN)
+ InvalidSaveMe, // kings already considered
+ InvalidSaveMe // kings already considered
+ };
+
+ ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
+ ASSERT(TRUE == InCheck(pos, pos->uToMove));
+
+ //
+ // Use the SEE function GetAttacks to find out how many pieces are
+ // giving check and from whence. Note: we don't sort rgCheckers
+ // here; maybe it would be worth trying. In any event it
+ // shouldn't be affected by treating the list as a minheap
+ // vs. as a sorted list.
+ //
+ GetAttacks(&rgCheckers, pos, cKing, FLIP(pos->uToMove));
+ ASSERT(rgCheckers.uCount > 0);
+ ASSERT(rgCheckers.uCount < 17);
+ uReturn = rgCheckers.uCount;
+
+ //
+ // Phase I: See if the king can flee or take the checking piece.
+ //
+ pKing = pos->rgSquare[cKing].pPiece;
+ ASSERT(GET_COLOR(pKing) == pos->uToMove);
+ ASSERT(IS_KING(pKing));
+ ASSERT(OPPOSITE_COLORS(pKing, rgCheckers.data[0].pPiece));
+ u = 0;
+ while(0 != g_iQKDeltas[u])
+ {
+ c = cKing + g_iQKDeltas[u];
+ u++;
+ if (IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+
+ //
+ // This logical OR replaced with bitwise OR; the effect is
+ // the same the the bitwise is marginally faster.
+ //
+ if (IS_EMPTY(p) | (OPPOSITE_COLORS(p, pKing)))
+ {
+ if (FALSE == IsAttacked(c, pos, FLIP(pos->uToMove)))
+ {
+ //
+ // So we know the destination square is not under
+ // attack right now... but that could be because
+ // the present (pre-move) king position "blocks"
+ // the attack. Detect this.
+ //
+ for (v = 0;
+ v < rgCheckers.uCount;
+ v++)
+ {
+ ASSERT(OPPOSITE_COLORS(pKing,
+ rgCheckers.data[v].pPiece));
+ if (!IS_PAWN(rgCheckers.data[v].pPiece) &&
+ !IS_KNIGHT(rgCheckers.data[v].pPiece))
+ {
+ iIndex = (int)rgCheckers.data[v].cLoc -
+ (int)cKing;
+ iDelta = CHECK_DELTA_WITH_INDEX(iIndex);
+ ASSERT(iDelta);
+
+ iIndex = (int)rgCheckers.data[v].cLoc -
+ (int)c;
+ if (iDelta == CHECK_DELTA_WITH_INDEX(iIndex))
+ {
+ goto loop;
+ }
+ }
+ }
+ ASSERT(v == rgCheckers.uCount);
+
+ //
+ // If we get here the square is not under attack
+ // and it does not contain our own piece. It's
+ // either empty or contains an enemy piece.
+ //
+ ASSERT(!p || OPPOSITE_COLORS(pKing, p));
+ _AddNormalMove(pStack, pos, cKing, c, p);
+ uReturn += 0x00010000;
+ }
+ }
+ }
+ loop:
+ ;
+ }
+
+ //
+ // N.B. If there is more than one piece checking the king then
+ // there is no way to escape check by blocking check with another
+ // piece or capturing the offending piece. The two attackers
+ // cannot be on the same rank, file or diagonal because this would
+ // depend on the previous position being illegal.
+ //
+ if (rgCheckers.uCount > 1)
+ {
+#ifdef DEBUG
+ //
+ // Note: the above comment is correct but this assertion fails
+ // on positions that we made up to run TestSearch. Disabled
+ // for now.
+ //
+#if 0
+ iDelta = DIRECTION_BETWEEN_SQUARES(rgCheckers.data[0].cLoc, cKing);
+ for (v = 1; v < rgCheckers.uCount; v++)
+ {
+ if (DIRECTION_BETWEEN_SQUARES(rgCheckers.data[v].cLoc, cKing) !=
+ iDelta)
+ {
+ break;
+ }
+ }
+ ASSERT(v != rgCheckers.uCount);
+#endif
+#endif
+ return(uReturn);
+ }
+
+ //
+ // Phase 2: If we get here there is a lone attacker causing check.
+ // See if the other pieces can block the check or take the
+ // checking piece.
+ //
+ c = rgCheckers.data[0].cLoc;
+ for (u = 1; // don't consider the king
+ u < pos->uNonPawnCount[pos->uToMove][0];
+ u++)
+ {
+ cDefender = pos->cNonPawns[pos->uToMove][u];
+ ASSERT(IS_ON_BOARD(cDefender));
+ p = pos->rgSquare[cDefender].pPiece;
+ ASSERT(p && !IS_PAWN(p));
+ ASSERT(GET_COLOR(p) == pos->uToMove);
+
+ (JumpTable[p])(pStack, pos, cDefender, cKing, c);
+ }
+
+ // Consider all pawns too
+ if (pos->uToMove == BLACK)
+ {
+ for (u = 0; u < pos->uPawnCount[BLACK]; u++)
+ {
+ cDefender = pos->cPawns[BLACK][u];
+#ifdef DEBUG
+ ASSERT(IS_ON_BOARD(cDefender));
+ p = pos->rgSquare[cDefender].pPiece;
+ ASSERT(p && IS_PAWN(p));
+ ASSERT(GET_COLOR(p) == pos->uToMove);
+#endif
+ SaveMeBlackPawn(pStack, pos, cDefender, cKing, c);
+ }
+ } else {
+ ASSERT(pos->uToMove == WHITE);
+ for (u = 0; u < pos->uPawnCount[WHITE]; u++)
+ {
+ cDefender = pos->cPawns[WHITE][u];
+#ifdef DEBUG
+ ASSERT(IS_ON_BOARD(cDefender));
+ p = pos->rgSquare[cDefender].pPiece;
+ ASSERT(p && IS_PAWN(p));
+ ASSERT(GET_COLOR(p) == pos->uToMove);
+#endif
+ SaveMeWhitePawn(pStack, pos, cDefender, cKing, c);
+ }
+ }
+ return(uReturn);
+}
+
+
+typedef struct _PRECOMP_KILLERS
+{
+ MOVE mv;
+ ULONG uBonus;
+}
+PRECOMP_KILLERS;
+
+static void
+_ScoreAllMoves(IN MOVE_STACK *pStack,
+ IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN MOVE mvHash)
+/**
+
+Routine description:
+
+ We have just generated all the moves, now score them. See comments
+ inline below about order.
+
+Parameters:
+
+ IN MOVE_STACK *pStack,
+ IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN MOVE mvHash,
+
+Return value:
+
+ static void
+
+**/
+{
+ ULONG uPly = ctx->uPly;
+ POSITION *pos = &ctx->sPosition;
+ ULONG u;
+ MOVE mv;
+ SCORE s;
+ MOVE_STACK_MOVE_VALUE_FLAGS mvf;
+ ULONG uHashMoveLoc = (ULONG)-1;
+ ULONG uColor = pos->uToMove;
+ PRECOMP_KILLERS sKillers[4];
+ COOR cEnprise = GetEnprisePiece(pos, uColor);
+
+ //
+ // We have generated all moves here. We also know that we are not
+ // escaping from check. There can be a hash move -- if there is
+ // then the search has already tried it and we should not generate
+ // it again.
+ //
+ // White has 218 legal moves in this position:
+ // 3Q4/1Q4Q1/4Q3/2Q4R/Q4Q2/3Q4/1Q4Rp/1K1BBNNk w - - 0 1
+ //
+
+ //
+ // Pre-populate killer/bonuses
+ //
+ sKillers[0].mv = ctx->mvKiller[uPly][0];
+ sKillers[0].uBonus = FIRST_KILLER;
+ sKillers[1].mv.uMove = sKillers[3].mv.uMove = 0;
+ sKillers[2].mv = ctx->mvKiller[uPly][1];
+ sKillers[2].uBonus = THIRD_KILLER;
+ if (uPly > 1)
+ {
+ sKillers[1].mv = ctx->mvKiller[uPly - 2][0];
+ sKillers[1].uBonus = SECOND_KILLER;
+ sKillers[3].mv = ctx->mvKiller[uPly - 2][1];
+ sKillers[3].uBonus = FOURTH_KILLER;
+ }
+ sKillers[0].uBonus |=
+ (SORT_THESE_FIRST * (IS_KILLERMATE_MOVE(sKillers[0].mv) != 0));
+ sKillers[1].uBonus |=
+ (SORT_THESE_FIRST * (IS_KILLERMATE_MOVE(sKillers[1].mv) != 0));
+ sKillers[2].uBonus |=
+ (SORT_THESE_FIRST * (IS_KILLERMATE_MOVE(sKillers[2].mv) != 0));
+ sKillers[3].uBonus |=
+ (SORT_THESE_FIRST * (IS_KILLERMATE_MOVE(sKillers[3].mv) != 0));
+
+ //
+ // Score moves
+ //
+ ASSERT(MOVE_COUNT(ctx, uPly) <= MAX_MOVES_PER_PLY);
+ for (u = pStack->uBegin[uPly];
+ u < pStack->uEnd[uPly];
+ u++)
+ {
+ mv = pStack->mvf[u].mv;
+ ASSERT(mv.uMove);
+ ASSERT(GET_COLOR(mv.pMoved) == uColor);
+ if (!IS_SAME_MOVE(mv, mvHash))
+ {
+#ifdef DEBUG
+ pStack->mvf[u].iValue = -MAX_INT;
+ pStack->mvf[u].bvFlags = 0;
+#endif
+ //
+ // 1. Hash move (already handled by search, all we have to
+ // do here is not generate it)
+ // 2. Killer mate move, if one exists
+ // 3. Winning captures & promotions (MVV/LVA to tiebreak)
+ // 4. Even captures & promotions -- >= SORT_THESE_FIRST
+ // 5. Killer move 1-4
+ // 6. "good" moves (moves away from danger etc...)
+ // 7. Other non capture moves (using dynamic move ordering scheme)
+ // 8. Losing captures -- <0
+ //
+
+ //
+ // Captures and promotions, use SEE
+ //
+ if (IS_CAPTURE_OR_PROMOTION(mv))
+ {
+ ASSERT((mv.pCaptured) || (mv.pPromoted));
+ s = PIECE_VALUE(mv.pCaptured) -
+ PIECE_VALUE(mv.pMoved);
+ if ((s <= 0) || mv.pPromoted)
+ {
+ s = SEE(pos, mv);
+ }
+
+ if (s >= 0)
+ {
+ s += PIECE_VALUE_OVER_100(mv.pCaptured) + 120;
+ s += PIECE_VALUE_OVER_100(mv.pPromoted);
+ s -= PIECE_VALUE_OVER_100(mv.pMoved);
+ s += SORT_THESE_FIRST;
+
+ //
+ // IDEA: bonus for capturing opponent's last moved
+ // piece to encourage quicker fail highs?
+ //
+ ASSERT(s >= SORT_THESE_FIRST);
+ ASSERT(s > 0);
+ ASSERT((s & STRIP_OFF_FLAGS) < VALUE_KING);
+ }
+#ifdef DEBUG
+ else
+ {
+ ASSERT((s & STRIP_OFF_FLAGS) > -VALUE_KING);
+ s -= VALUE_KING;
+ ASSERT(s < 0);
+ }
+#endif
+ }
+
+ //
+ // Non-captures/promotes use history/killer
+ //
+ else
+ {
+ ASSERT((!mv.pCaptured) && (!mv.pPromoted));
+ s = g_iPSQT[mv.pMoved][mv.cTo]; // 0..1000
+ s |= (IS_SAME_MOVE(sKillers[0].mv, mv) * sKillers[0].uBonus);
+ s |= (IS_SAME_MOVE(sKillers[1].mv, mv) * sKillers[1].uBonus);
+ s |= (IS_SAME_MOVE(sKillers[2].mv, mv) * sKillers[2].uBonus);
+ s |= (IS_SAME_MOVE(sKillers[3].mv, mv) * sKillers[3].uBonus);
+ s |= ((GOOD_MOVE + PIECE_VALUE(mv.pMoved) / 2) *
+ (mv.cFrom == cEnprise));
+ ASSERT(s >= 0);
+ }
+ pStack->mvf[u].iValue = s;
+ ASSERT(pStack->mvf[u].iValue != -MAX_INT);
+ }
+ else
+ {
+ ASSERT(uHashMoveLoc == (ULONG)-1);
+ uHashMoveLoc = u;
+#ifdef DEBUG
+ ASSERT((mv.cFrom == mvHash.cFrom) && (mv.cTo == mvHash.cTo));
+ pStack->mvf[u].bvFlags |= MVF_MOVE_SEARCHED;
+#endif
+ }
+
+ } // next move
+
+ //
+ // If we generated the hash move, stick it at the end and pretend
+ // we didn't... it was already considered by the search and we
+ // should not generate it again.
+ //
+ if (uHashMoveLoc != (ULONG)-1)
+ {
+ ASSERT(MOVE_COUNT(ctx, uPly) >= 1);
+ ASSERT(uHashMoveLoc >= pStack->uBegin[uPly]);
+ ASSERT(uHashMoveLoc < pStack->uEnd[uPly]);
+ mvf = pStack->mvf[uHashMoveLoc];
+ pStack->mvf[uHashMoveLoc] = pStack->mvf[pStack->uEnd[uPly] - 1];
+#ifdef DEBUG
+ pStack->mvf[pStack->uEnd[uPly] - 1] = mvf;
+#endif
+ pStack->uEnd[uPly]--;
+ }
+}
+
+
+
+static void
+_ScoreAllEscapes(IN MOVE_STACK *pStack,
+ IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN MOVE mvHash)
+/**
+
+Routine description:
+
+ We have just generated all the moves, now score them. See comments
+ inline below about order.
+
+Parameters:
+
+ IN MOVE_STACK *pStack,
+ IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN MOVE mvHash,
+
+Return value:
+
+ static void
+
+**/
+{
+ ULONG uPly = ctx->uPly;
+ POSITION *pos = &ctx->sPosition;
+ ULONG u;
+ MOVE mv;
+ SCORE s;
+ MOVE_STACK_MOVE_VALUE_FLAGS mvf;
+ ULONG uHashMoveLoc = (ULONG)-1;
+ COOR c;
+ ULONG v;
+ PIECE p;
+ static SCORE _bonus[2][7] = {
+ { 0, 2000, 1250, 1500, 1100, 850, 0 }, // friend
+ { 0, 1600, 100, 100, 50, -1, -1 }, // foe
+ };
+
+ //
+ // We have generated legal escapes from check here. There can be
+ // a hash move -- if there is then the search has already tried it
+ // and we should not generate it again.
+ //
+
+ ASSERT(MOVE_COUNT(ctx, uPly) <= MAX_MOVES_PER_PLY);
+ for (u = pStack->uBegin[uPly];
+ u < pStack->uEnd[uPly];
+ u++)
+ {
+ mv = pStack->mvf[u].mv;
+ ASSERT(mv.uMove);
+ ASSERT(GET_COLOR(mv.pMoved) == pos->uToMove);
+ if (!IS_SAME_MOVE(mv, mvHash))
+ {
+#ifdef DEBUG
+ pStack->mvf[u].iValue = -MAX_INT;
+ pStack->mvf[u].bvFlags = 0;
+#endif
+ pStack->mvf[u].mv.bvFlags |= MOVE_FLAG_ESCAPING_CHECK;
+
+ //
+ // 1. Hash move (already handled by search, all we have to
+ // do here is not generate it)
+ // 2. Winning captures by pieces other than the checked king
+ // 3. Winning captures by the king
+ // 4. Even captures by pieces other than the king
+ // 5. Killer escapes
+ // 6. Moves that block the check and are even
+ // 7. King moves
+ // 8. Losing captures / blocks
+ //
+
+ //
+ // Captures and promotions, use SEE
+ //
+ if (IS_CAPTURE_OR_PROMOTION(mv))
+ {
+ ASSERT((mv.pCaptured) || (mv.pPromoted));
+ s = PIECE_VALUE(mv.pCaptured) -
+ PIECE_VALUE(mv.pMoved);
+ if ((s <= 0) || mv.pPromoted)
+ {
+ s = SEE(pos, mv);
+ }
+
+ if (s > 0)
+ {
+ // Winning captures: take with pieces before with king
+ s += PIECE_VALUE_OVER_100(mv.pPromoted);
+ s -= PIECE_VALUE_OVER_100(mv.pMoved);
+ s >>= IS_KING(mv.pMoved);
+ s |= SORT_THESE_FIRST;
+ ASSERT(s >= SORT_THESE_FIRST);
+ ASSERT(s > 0);
+ ASSERT((s & STRIP_OFF_FLAGS) < VALUE_KING);
+ }
+ else if (s == 0)
+ {
+ // Even capture -- can't be capturing w/ king
+ ASSERT(!IS_KING(mv.pMoved));
+ s += PIECE_VALUE_OVER_100(mv.pPromoted) + 20;
+ s -= PIECE_VALUE_OVER_100(mv.pMoved);
+ s |= FIRST_KILLER;
+ ASSERT(s > 0);
+ }
+#ifdef DEBUG
+ else
+ {
+ // Losing capture
+ ASSERT(!IS_KING(mv.pMoved));
+ ASSERT((s & STRIP_OFF_FLAGS) > -VALUE_KING);
+ s -= VALUE_KING;
+ ASSERT(s < 0);
+ }
+#endif
+ }
+
+ //
+ // Non-captures/promotes
+ //
+ else
+ {
+ ASSERT((!mv.pCaptured) && (!mv.pPromoted));
+ if (IS_SAME_MOVE(mv, ctx->mvKillerEscapes[uPly][0]) ||
+ IS_SAME_MOVE(mv, ctx->mvKillerEscapes[uPly][1]))
+ {
+ s = SECOND_KILLER;
+ ASSERT(s >= 0);
+ }
+ else
+ {
+ s = g_iPSQT[mv.pMoved][mv.cTo]; // 0..1000
+ if (!IS_KING(mv.pMoved))
+ {
+ // 0..2400
+ s += (VALUE_QUEEN - PIECE_VALUE(mv.pMoved)) * 4;
+ if (SEE(pos, mv) >= 0)
+ {
+ // Non-losing block of check
+ ASSERT(SEE(pos, mv) == 0);
+ s |= THIRD_KILLER;
+ ASSERT(s >= 0);
+ }
+ else
+ {
+ // Losing block of check
+ s -= VALUE_KING;
+ ASSERT(s < 0);
+ }
+ }
+ else
+ {
+ // If the king is fleeing, encourage a spot next
+ // to friendly pieces.
+ v = 0;
+ do {
+ c = mv.cTo + g_iQKDeltas[v++];
+ if (IS_ON_BOARD(c))
+ {
+ p = pos->rgSquare[c].pPiece;
+ s += _bonus[OPPOSITE_COLORS(pos->uToMove, p)]
+ [PIECE_TYPE(p)];
+ ASSERT(_bonus[OPPOSITE_COLORS(pos->uToMove, p)]
+ [PIECE_TYPE(p)] >= 0);
+ }
+ }
+ while(g_iQKDeltas[v] != 0);
+ }
+ }
+ }
+ pStack->mvf[u].iValue = s;
+ ASSERT(pStack->mvf[u].iValue != -MAX_INT);
+ }
+ else
+ {
+ ASSERT(uHashMoveLoc == (ULONG)-1);
+ uHashMoveLoc = u;
+#ifdef DEBUG
+ ASSERT((mv.cFrom == mvHash.cFrom) && (mv.cTo == mvHash.cTo));
+ pStack->mvf[u].bvFlags |= MVF_MOVE_SEARCHED;
+#endif
+ }
+ } // next move
+
+ //
+ // If we generated the hash move, stick it at the end and pretend
+ // we didn't... it was already considered by the search and we
+ // should not generate it again.
+ //
+ if (uHashMoveLoc != (ULONG)-1)
+ {
+ ASSERT(MOVE_COUNT(ctx, uPly) >= 1);
+ ASSERT(uHashMoveLoc >= pStack->uBegin[uPly]);
+ ASSERT(uHashMoveLoc < pStack->uEnd[uPly]);
+ mvf = pStack->mvf[uHashMoveLoc];
+ pStack->mvf[uHashMoveLoc] = pStack->mvf[pStack->uEnd[uPly] - 1];
+#ifdef DEBUG
+ pStack->mvf[pStack->uEnd[uPly] - 1] = mvf;
+#endif
+ pStack->uEnd[uPly]--;
+ }
+}
+
+
+
+static void
+_ScoreQSearchMovesInclChecks(IN MOVE_STACK *pStack,
+ IN SEARCHER_THREAD_CONTEXT *ctx)
+/**
+
+Routine description:
+
+ We have just generated all the moves. We were called from the
+ Qsearch so:
+
+ 1. Unlike _ScoreAllMoves we don't have to think about a hash move
+ 2. QSearch only cares about winning/even captures/promotes and
+ checking moves.
+
+ Like _ScoreSearchMoves, this function takes "hints" from the
+ searcher context about what squares are in danger and tries to
+ order moves away from those squares sooner. Unlike
+ _ScoreSearchMoves this function also attempts to capture enemies
+ that are in danger sooner.
+
+Parameters:
+
+ IN MOVE_STACK *pStack,
+ IN SEARCHER_THREAD_CONTEXT *ctx
+
+Return value:
+
+ static void
+
+**/
+{
+ register POSITION *pos = &ctx->sPosition;
+ PLY_INFO *pi = &ctx->sPlyInfo[ctx->uPly];
+ ULONG uPly = ctx->uPly;
+ ULONG u, uColor = pos->uToMove;
+ MOVE mv;
+ MOVE mvLast = (pi-1)->mv;
+ SCORE s;
+ COOR cEnprise = GetEnprisePiece(pos, uColor);
+
+ //
+ // We have generate all moves or just legal escapes from check
+ // here. There can be a hash move -- if there is then the search
+ // has already tried it and we should not generate it again.
+ //
+ // White has 218 legal moves in this position:
+ // 3Q4/1Q4Q1/4Q3/2Q4R/Q4Q2/3Q4/1Q4Rp/1K1BBNNk w - - 0 1
+ //
+ ASSERT(MOVE_COUNT(ctx, uPly) <= MAX_MOVES_PER_PLY);
+ ASSERT(uPly >= 1);
+
+ for (u = pStack->uBegin[uPly];
+ u < pStack->uEnd[uPly];
+ u++)
+ {
+ mv = pStack->mvf[u].mv;
+ ASSERT(GET_COLOR(mv.pMoved) == uColor);
+#ifdef DEBUG
+ pStack->mvf[u].iValue = -MAX_INT;
+ pStack->mvf[u].bvFlags = 0;
+#endif
+ pStack->mvf[u].mv.bvFlags |= WouldGiveCheck(ctx, mv);
+
+ //
+ // 1. There can't be a hash move so don't worry about it
+ // 2. Winning captures & promotions
+ // 3. Even captures & promotions -- >= SORT_THESE_FIRST
+ // 4. Checking non-capture moves
+ // 5. Losing captures that check -- > 0
+ // 6. Everything else is ignored -- <= 0
+ //
+
+ //
+ // Captures and promotions, use SEE
+ //
+ if (IS_CAPTURE_OR_PROMOTION(mv))
+ {
+ ASSERT((mv.pCaptured) || (mv.pPromoted));
+ s = PIECE_VALUE(mv.pCaptured) -
+ PIECE_VALUE(mv.pMoved);
+ if ((s <= 0) || mv.pPromoted)
+ {
+ s = SEE(pos, mv);
+ }
+
+ if (s >= 0)
+ {
+ //
+ // Winning / even captures / promotes should be >=
+ // SORT_THESE_FIRST.
+ //
+ s += PIECE_VALUE_OVER_100(mv.pCaptured) + 120;
+ s += PIECE_VALUE_OVER_100(mv.pPromoted);
+ s -= PIECE_VALUE_OVER_100(mv.pMoved);
+ s += SORT_THESE_FIRST;
+
+ //
+ // Bonus if it checks too
+ //
+ s += (pStack->mvf[u].mv.bvFlags & MOVE_FLAG_CHECKING) * 4;
+
+ //
+ // Bonus for capturing last moved enemy piece.
+ //
+ s += (mv.cTo == mvLast.cTo) * 64;
+ ASSERT(s >= SORT_THESE_FIRST);
+ ASSERT(s > 0);
+ }
+ else
+ {
+ //
+ // Make losing captures into either zero scores or
+ // slightly positive if they check. Qsearch is going
+ // to bail as soon as it sees the first zero so no
+ // need to keep real SEE scores on these.
+ //
+ s = (pStack->mvf[u].mv.bvFlags & MOVE_FLAG_CHECKING);
+ ASSERT(s >= 0);
+ }
+ }
+
+ //
+ // Non-captures/promotes -- we don't care unless they check
+ //
+ else
+ {
+ s = (pStack->mvf[u].mv.bvFlags & MOVE_FLAG_CHECKING) * 2;
+ ASSERT(s >= 0);
+ ASSERT(s < SORT_THESE_FIRST);
+ }
+
+ //
+ // If this move moves a piece that was in danger, consider it
+ // sooner.
+ //
+ if (s > 0)
+ {
+ ASSERT(IS_CAPTURE_OR_PROMOTION(pStack->mvf[u].mv) ||
+ IS_CHECKING_MOVE(pStack->mvf[u].mv));
+ s += (mv.cFrom == cEnprise) * (PIECE_VALUE(mv.pMoved) / 4);
+ ASSERT(s > 0);
+ }
+ pStack->mvf[u].iValue = s;
+ ASSERT(pStack->mvf[u].iValue != -MAX_INT);
+
+ } // next move
+}
+
+
+static void
+_ScoreQSearchMovesNoChecks(IN MOVE_STACK *pStack,
+ IN SEARCHER_THREAD_CONTEXT *ctx)
+/**
+
+Routine description:
+
+ We have just generated all the moves. We were called from the
+ Qsearch so:
+
+ 1. Unlike _ScoreAllMoves we don't have to think about a hash move
+ 2. QSearch only cares about winning/even captures/promotes. At
+ this point it is so deep that it doesn't even care about
+ checks.
+
+ Like _ScoreSearchMoves, this function takes "hints" from the
+ searcher context about what squares are in danger and tries to
+ order moves away from those squares sooner. Unlike
+ _ScoreSearchMoves this function also attempts to capture enemies
+ that are in danger sooner.
+
+Parameters:
+
+ IN MOVE_STACK *pStack,
+ IN SEARCHER_THREAD_CONTEXT *ctx
+
+Return value:
+
+ static void
+
+**/
+{
+ register POSITION *pos = &(ctx->sPosition);
+ ULONG uPly = ctx->uPly;
+ ULONG u, uColor;
+ MOVE mv;
+ SCORE s;
+
+ //
+ // We have generate all moves or just legal escapes from check
+ // here. There can be a hash move -- if there is then the search
+ // has already tried it and we should not generate it again.
+ //
+ // White has 218 legal moves in this position:
+ // 3Q4/1Q4Q1/4Q3/2Q4R/Q4Q2/3Q4/1Q4Rp/1K1BBNNk w - - 0 1
+ //
+ ASSERT(MOVE_COUNT(ctx, uPly) <= MAX_MOVES_PER_PLY);
+
+ uColor = pos->uToMove;
+ for (u = pStack->uBegin[uPly];
+ u < pStack->uEnd[uPly];
+ u++)
+ {
+ mv = pStack->mvf[u].mv;
+ ASSERT(GET_COLOR(mv.pMoved) == uColor);
+#ifdef DEBUG
+ pStack->mvf[u].iValue = -MAX_INT;
+ pStack->mvf[u].bvFlags = 0;
+#endif
+ //
+ // 1. There can't be a hash move so don't worry about it
+ // 2. Winning captures & promotions
+ // 3. Even captures & promotions -- >= SORT_THESE_FIRST
+ // 4. Everything else is ignored -- <= 0
+ //
+
+ //
+ // Captures and promotions, use SEE
+ //
+ s = 0;
+ if (IS_CAPTURE_OR_PROMOTION(mv))
+ {
+ ASSERT((mv.pCaptured) || (mv.pPromoted));
+ s = PIECE_VALUE(mv.pCaptured) -
+ PIECE_VALUE(mv.pMoved);
+ //
+ // TODO: how does this handle positions like K *p *k ?
+ //
+ if (s <= 0)
+ {
+ s = SEE(pos, mv);
+ }
+
+ if (s >= 0)
+ {
+ //
+ // Winning / even captures / promotes should be >=
+ // SORT_THESE_FIRST.
+ //
+ s += PIECE_VALUE_OVER_100(mv.pCaptured) + 120;
+ s += PIECE_VALUE_OVER_100(mv.pPromoted);
+ s -= PIECE_VALUE_OVER_100(mv.pMoved);
+ s += SORT_THESE_FIRST;
+
+ //
+ // IDEA: bonus for capturing an enprise enemy piece?
+ //
+ ASSERT(s >= SORT_THESE_FIRST);
+ ASSERT(s > 0);
+ }
+ }
+ pStack->mvf[u].iValue = s;
+ ASSERT(pStack->mvf[u].iValue != -MAX_INT);
+
+ } // next move
+}
+
+
+
+void
+GenerateMoves(IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN MOVE mvHash,
+ IN ULONG uType)
+/**
+
+Routine description:
+
+ This is the entrypoint to the move generator. It invokes the
+ requested generation code which push moves onto the move stack in
+ ctx. Once the moves are generated this code optionally will score
+ the moves.
+
+Parameters:
+
+ SEARCHER_THREAD_CONTEXT *ctx : a searcher thread's context
+
+ IN: ctx->sPosition.uDangerCount and ctx->sPosition.cDanger
+ data is used to tweak move scoring. This data MUST be
+ consistent with the state of the current board.
+
+ OUT: ctx->sMoveStack.uBegin[ctx->uPly] is set,
+ ctx->sMoveStack.uEnd[ctx->uPly] is set,
+ ctx->sMoveStack.mvf[begin..end] are populated
+
+ MOVE mvHash : the hash move to not generate
+
+ ULONG uType : what kind of moves to generate
+
+ GENERATE_ALL_MOVES : pseudo-legal generation of all moves;
+ scored by _ScoreAllMoves. Return value is count of moves
+ generated.
+
+ GENERATE_ESCAPE: called when stm in check, generate evasions
+ scored by _ScoreAllMoves. Return value is special code;
+ see inline comment below.
+
+ GENERATE_CAPTURES_PROMS_CHECKS: caps, promotions, and checks
+ scored by _ScoreQSearchMoves. Return value is count of
+ moves generated.
+
+ GENERATE_CAPTURES_PROMS: caps, promotions. Skip the checks.
+
+ GENERATE_DONT_SCORE: pseudo-legal generation of all moves;
+ not scored to save time. Return value always zero.
+
+Return value:
+
+ void
+
+**/
+{
+ register ULONG uPly = ctx->uPly;
+ MOVE_STACK *pStack = &ctx->sMoveStack;
+ POSITION *pos = &ctx->sPosition;
+ ULONG uReturn;
+#ifdef DEBUG
+ POSITION board;
+ ULONG u;
+ MOVE mv;
+ PIECE p;
+
+ ASSERT((uPly >= 0) && (uPly < MAX_PLY_PER_SEARCH));
+ memcpy(&board, pos, sizeof(POSITION));
+ memcpy(&(pStack->board[uPly]), pos, sizeof(POSITION));
+
+ //
+ // Make sure the hash move makes sense (if provided)
+ //
+ if (mvHash.uMove)
+ {
+ p = pos->rgSquare[mvHash.cFrom].pPiece;
+ ASSERT(p);
+ ASSERT(GET_COLOR(p) == pos->uToMove);
+ p = pos->rgSquare[mvHash.cTo].pPiece;
+ ASSERT(!p || OPPOSITE_COLORS(p, pos->uToMove));
+ }
+#endif
+ pStack->uPly = uPly;
+ pStack->uEnd[uPly] = pStack->uBegin[uPly];
+ pStack->sGenFlags[uPly].uAllGenFlags = 0;
+
+ switch(uType)
+ {
+ case GENERATE_CAPTURES_PROMS_CHECKS:
+ ASSERT(!InCheck(pos, pos->uToMove));
+ ASSERT(mvHash.uMove == 0);
+ _FindUnblockedSquares(pStack, pos);
+ _GenerateAllMoves(pStack, pos);
+ _ScoreQSearchMovesInclChecks(pStack, ctx);
+ break;
+ case GENERATE_CAPTURES_PROMS:
+ ASSERT(!InCheck(pos, pos->uToMove));
+ ASSERT(mvHash.uMove == 0);
+ _FindUnblockedSquares(pStack, pos);
+ _GenerateAllMoves(pStack, pos);
+ _ScoreQSearchMovesNoChecks(pStack, ctx);
+ break;
+ case GENERATE_ALL_MOVES:
+ ASSERT(!InCheck(pos, pos->uToMove));
+ _FindUnblockedSquares(pStack, pos);
+ _GenerateAllMoves(pStack, pos);
+ _ScoreAllMoves(pStack, ctx, mvHash);
+ uReturn = MOVE_COUNT(ctx, uPly);
+ break;
+ case GENERATE_ESCAPES:
+ ASSERT(InCheck(pos, pos->uToMove));
+ _FindUnblockedSquares(pStack, pos);
+ uReturn = _GenerateEscapes(pStack, pos);
+ ASSERT(uReturn);
+ pStack->sGenFlags[uPly].uKingMoveCount = uReturn >> 16;
+ pStack->sGenFlags[uPly].uCheckingPieces = uReturn & 0xFF;
+ _ScoreAllEscapes(pStack, ctx, mvHash);
+ break;
+
+ // Note: this is just for plytest / seetest. Just generate
+ // moves, do not score them.
+ case GENERATE_DONT_SCORE:
+ if (InCheck(pos, pos->uToMove))
+ {
+ (void)_GenerateEscapes(pStack, pos);
+ for (uReturn = pStack->uBegin[uPly];
+ uReturn < pStack->uEnd[uPly];
+ uReturn++)
+ {
+ pStack->mvf[uReturn].mv.bvFlags |=
+ MOVE_FLAG_ESCAPING_CHECK;
+ }
+ }
+ else
+ {
+ (void)_GenerateAllMoves(pStack, pos);
+ }
+ goto end;
+#ifdef DEBUG
+ default:
+ uReturn = 0;
+ ASSERT(FALSE);
+#endif
+ }
+
+#ifdef DEBUG
+ //
+ // Sanity check the move list we just generated...
+ //
+ ASSERT(MOVE_COUNT(ctx, uPly) >= 0);
+ for (u = pStack->uBegin[uPly];
+ u < pStack->uEnd[uPly];
+ u++)
+ {
+ mv = pStack->mvf[u].mv;
+ ASSERT(!IS_SAME_MOVE(mv, mvHash));
+ SanityCheckMove(pos, mv);
+
+ if (WouldGiveCheck(ctx, mv))
+ {
+ if (MakeMove(ctx, mv))
+ {
+ ASSERT(ctx->uPly == (uPly + 1));
+ ASSERT(InCheck(pos, pos->uToMove));
+ UnmakeMove(ctx, mv);
+
+ //
+ // Note: this is so that DEBUG and RELEASE builds search
+ // the same trees. Without this the MakeMove/UnmakeMove
+ // pair here can affect the order of the pieces in the
+ // POSITION piece lists which in turn affects the order
+ // in which moves are generated down the road.
+ //
+ memcpy(&ctx->sPosition, &board, sizeof(POSITION));
+ }
+ }
+ else
+ {
+ if (MakeMove(ctx, mv))
+ {
+ ASSERT(ctx->uPly == (uPly + 1));
+ ASSERT(!InCheck(pos, pos->uToMove));
+ UnmakeMove(ctx, mv);
+
+ //
+ // Note: this is so that DEBUG and RELEASE builds search
+ // the same trees. Without this the MakeMove/UnmakeMove
+ // pair here can affect the order of the pieces in the
+ // POSITION piece lists which in turn affects the order
+ // in which moves are generated down the road.
+ //
+ memcpy(&ctx->sPosition, &board, sizeof(POSITION));
+ }
+ }
+ }
+ ASSERT(PositionsAreEquivalent(&board, pos));
+#endif
+ end:
+ pStack->sGenFlags[uPly].uMoveCount = MOVE_COUNT(ctx, uPly);
+ pStack->uBegin[uPly + 1] = pStack->uEnd[uPly];
+}