From 3eac7f469add78edb9dd7dced36bde5427346321 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Sun, 30 Aug 2026 12:48:34 -0700 Subject: Replace bishop/knight/rook/queen mobility jump tables with inline switch dispatch. The per-square mobility ray-walk called through a function pointer for every square visited (piece type varies square to square, so the CPU's indirect-branch predictor couldn't learn it), and nearly every handler reduced to a couple of constant increments and a stop/continue flag. Replaced each PMOBILITY_HELPER table with a small case-tag table and an inlined switch, dropping all now-dead handler functions and the PMOBILITY_HELPER typedef. Also hoisted the ray-invariant rank/file direction test (rook's "connected" bonus, queen's bishop/rook xray cases) out of the per-square switch to compute once per ray instead. Verified semantics-preserving: node counts, solve/fail sets, and PVs are byte-for-byte identical to head_reference across all three curated ECM suites (ringers/confident_quick/hard_quick) at sd 10. bench shows a consistent ~6-7% NPS improvement over two back-to-back runs. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012Q8wbZABsyG7PZQUd9WBAw --- src/eval.c | 1948 ++++++++++++++++++------------------------------------------ 1 file changed, 570 insertions(+), 1378 deletions(-) (limited to 'src') diff --git a/src/eval.c b/src/eval.c index fc22127..a9be77f 100755 --- a/src/eval.c +++ b/src/eval.c @@ -22,7 +22,79 @@ Revision History: #include "chess.h" typedef void (*PEVAL_HELPER)(POSITION *, COOR, PAWN_HASH_ENTRY *); -typedef FLAG (FASTCALL *PMOBILITY_HELPER)(POSITION *, COOR, ULONG *, ULONG *); + +// +// Bishop-mobility ray-walk outcome categories -- see BMobCaseTable in +// _EvalBishop. Replaces a table of function pointers (one indirect +// call/ret per square visited) with a table of these tags dispatched +// via switch, inlined directly into the ray-walking loop. +// +typedef enum _BMOB_CASE +{ + BMOB_EMPTY = 0, // empty square + BMOB_INVALID, // should never occur (off-board sentinel) + BMOB_FRIEND_PAWN, // own-color pawn + BMOB_ENEMY_PAWN, // enemy pawn (worth less than a bishop) + BMOB_FRIEND_BLOCK, // own-color knight/rook/king: blocks, no xray + BMOB_ENEMY_SAME, // enemy bishop/knight: captures, blocks + BMOB_FRIEND_XRAY, // own-color bishop/queen: xray through, keep going + BMOB_ENEMY_GREATER, // enemy rook/queen/king: captures, xray bit, keep going +} BMOB_CASE; + +// +// Knight-mobility outcome categories -- see NMobCaseTable in _EvalKnight. +// Knight mobility only ever looks at the single landing square (no ray to +// walk/stop), so there's no xray/stop concept here, just "does landing on +// this square count as mobility." +// +typedef enum _NMOB_CASE +{ + NMOB_INVALID = 0, // should never occur (off-board sentinel) + NMOB_MOBILE_SQUARE, // empty square or enemy pawn: counts unless unsafe for a minor + NMOB_ENEMY_OTHER, // any other enemy piece: always counts + NMOB_FRIEND, // any friendly piece: never counts +} NMOB_CASE; + +// +// Rook-mobility outcome categories -- see RMobCaseTable in _EvalRook. +// RMOB_FRIEND_ROOK carries a real side effect (the "connected rooks" +// bonus), so unlike bishop/knight this isn't purely mobility/xray-bit +// bookkeeping -- kept as a distinct case rather than folded into +// RMOB_FRIEND_QUEEN even though both keep scanning with the xray bit set. +// +typedef enum _RMOB_CASE +{ + RMOB_EMPTY = 0, // empty square + RMOB_INVALID, // should never occur (off-board sentinel) + RMOB_ENEMY_LESS, // enemy pawn/knight/bishop: captures, blocks + RMOB_FRIEND_BLOCK, // own-color knight/bishop/king: blocks, no xray + RMOB_FRIEND_ROOK, // own-color rook: connected-rooks bonus, xray, keep going + RMOB_ENEMY_SAME, // enemy rook: captures, blocks + RMOB_FRIEND_QUEEN, // own-color queen: xray through, keep going + RMOB_ENEMY_GREATER, // enemy queen/king: captures, xray bit, keep going +} RMOB_CASE; + +// +// Queen-mobility outcome categories -- see QMobCaseTable in _EvalQueen. +// A queen's ray set is the union of a bishop's 4 diagonals and a rook's +// 4 orthogonals, so QMOB_FRIEND_BISHOP/QMOB_FRIEND_ROOK only let the +// queen xray through when the *ray it's currently walking* matches that +// piece's own move pattern (diagonal for a bishop, orthogonal for a +// rook) -- computed once per ray below (fOrthogonalRay), not per square, +// since every square on a given ray shares the same rank/file +// relationship to the queen's home square. +// +typedef enum _QMOB_CASE +{ + QMOB_EMPTY = 0, // empty square + QMOB_INVALID, // should never occur (off-board sentinel) + QMOB_ENEMY_LESS, // enemy piece worth less than a queen: captures, blocks + QMOB_FRIEND_BLOCK, // own-color knight/king: blocks, no xray + QMOB_FRIEND_BISHOP, // own-color bishop: xray only on a diagonal ray + QMOB_FRIEND_ROOK, // own-color rook: xray only on an orthogonal ray + QMOB_FRIEND_QUEEN, // own-color queen: xray through, keep going + QMOB_ENEMY_GE, // enemy queen/king: captures, blocks +} QMOB_CASE; // // To simplify code / maintenance I use the same loop for both colors @@ -2180,1280 +2252,193 @@ Return value: // Look for connected, supported and outside passed pawns to give // extra bonuses. // - _EvaluateConnectedSupportedOutsidePassers(pos, pHash); - - // - // TODO: recognize quartgrips and stonewalls - // - return(pHash); -} - - -ULONG -CountKingSafetyDefects(IN OUT POSITION *pos, - IN ULONG uSide) -/** - -Routine description: - - Determine how many defects uSide's king position has _quickly_. - TODO: add more knowledge as cheaply as possible... - -Parameters: - - POSITION *pos, - ULONG uSide - -Return value: - - ULONG - -**/ -{ - ULONG uCounter = 0; - ULONG xSide = FLIP(uSide); - COOR cKing; - COOR c; - int i; - PIECE p; - ULONG u; - - // - // Don't count king safety defects if the real eval code in - // _EvalKing would not... - // - if (pos->uNonPawnMaterial[xSide] < DO_KING_SAFETY_THRESHOLD) { - return 0; - } - - cKing = pos->cNonPawns[uSide][0]; - uCounter = KING_INITIAL_COUNTER_BY_LOCATION[uSide][cKing] >> 1; - ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); - ASSERT(pos->rgSquare[cKing].uIndex == 0); - ASSERT(IS_ON_BOARD(cKing)); - ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == uSide); - - // - // Make sure cKing - 1, cKing and cKing + 1 are on the board - // - cKing += (!IS_ON_BOARD(cKing - 1)); - cKing -= (!IS_ON_BOARD(cKing + 1)); - ASSERT(IS_ON_BOARD(cKing)); - ASSERT(IS_ON_BOARD(cKing + 1)); - ASSERT(IS_ON_BOARD(cKing - 1)); - - // - // Consider all enemy pieces except the king (not including pawns) - // - for (u = 1; - u < pos->uNonPawnCount[xSide][0]; - u++) - { - c = pos->cNonPawns[xSide][u]; - ASSERT(IS_ON_BOARD(c)); - i = (int)c - (int)(cKing + 1); - ASSERT((i >= -128) && (i <= 125)); - p = pos->rgSquare[c].pPiece; - ASSERT(pos->rgSquare[c].uIndex == u); - ASSERT(!IS_KING(p)); - ASSERT(GET_COLOR(p) == xSide); - p = 1 << PIECE_TYPE(p); - - uCounter += ((i == 0) | (i == -2) | - ((CHECK_VECTOR_WITH_INDEX(i, xSide) & p) != 0) | - ((CHECK_VECTOR_WITH_INDEX(i + 1, xSide) & p) != 0) | - ((CHECK_VECTOR_WITH_INDEX(i + 2, xSide) & p) != 0)); - } - ASSERT(uCounter < 15); - - // Save the number of enemy pieces pointing at this king for later use. - pos->uPiecesPointingAtKing[uSide] = - (uCounter - (KING_INITIAL_COUNTER_BY_LOCATION[uSide][cKing] >> 1)); - return uCounter; -} - - -static void -EstimatePositionalScore(IN POSITION *pos, - IN UNUSED PAWN_HASH_ENTRY *pHash, - IN OUT SCORE *piAlphaMargin, - IN OUT SCORE *piBetaMargin) -/** - -Routine description: - - Before doing an early lazy eval, look at the position and widen - the alpha/beta margins to account for the positional terms that - have not been computed yet at this point in Eval() -- king safety - plus a flat residual covering mobility, passers, and everything - else that genuinely requires attack-generation to know exactly. - - Both terms below are calibrated from measured data (CALIBRATE_ - POSITIONAL instrumentation, ~1.6M full-eval samples over an ECM - slice), not guessed: for each value, p90 of the *actual* score - swing between this point in Eval() and full-eval completion, so - the resulting margin is wrong (too narrow) on at most ~10% of - calls, in either bucket. See conversation history for the - percentile tables -- if re-tuning, regenerate them, don't hand- - edit these numbers. - - Note: king safety and "everything else" turned out to be close to - independent of piece count and of each other, so summing their - two p90s (rather than deriving one joint p90) is a deliberately - conservative (wider than strictly necessary) combination. - - O(1) -- no attack bitboard generation -- so it's safe to call - whenever the cheap material-only lazy check (the caller's - first-pass margin) wasn't enough to resolve the cutoff on its own. - -Parameters: - - POSITION *pos - PAWN_HASH_ENTRY *pHash : unused now (kept for call-site symmetry); - pHash->iScore is already folded into pos->iScore by this point, - and the passer-specific estimate this used to compute turned - out to be negligible next to the residual term below. - SCORE *piAlphaMargin, *piBetaMargin : widened in place, identically - (no measured basis for an asymmetric alpha/beta split) - -Return value: - - void - -**/ -{ - // p90 of |true king-safety swing|, indexed by combined defect - // count (CountKingSafetyDefects(stm) + CountKingSafetyDefects(xsm)), - // clamped above index 10 (sparse data beyond that). - static const SCORE iKingSwingP90[11] = { - 47, 62, 87, 119, 169, 157, 181, 282, 342, 342, 385 - }; - // p90 of |mobility + passers + everything else combined|, measured - // directly (no useful correlation found with piece count). - static const SCORE iResidualP90 = 154; - ULONG uDefects = (CountKingSafetyDefects(pos, WHITE) + - CountKingSafetyDefects(pos, BLACK)); - SCORE iKingTerm = iKingSwingP90[MINU(10, uDefects)]; - - *piAlphaMargin += iKingTerm + iResidualP90; - *piBetaMargin += iKingTerm + iResidualP90; -} - - -static void -_InvalidEvaluator(UNUSED POSITION *pos, - UNUSED COOR c, - UNUSED PAWN_HASH_ENTRY *pHash) -/** - -Routine description: - - This code should never be called - -Parameters: - - POSITION *pos, - COOR c, - PAWN_HASH_ENTRY *pHash, - -Return value: - - void - -**/ -{ - UtilPanic(SHOULD_NOT_GET_HERE, - NULL, NULL, NULL, NULL, - __FILE__, __LINE__); -} - -// -// ====================================================================== -// - -static FLAG FASTCALL -_InvalidMobilityHelper(UNUSED POSITION *pos, - UNUSED COOR c, - UNUSED ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - - This code should never be called - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ - UtilPanic(SHOULD_NOT_GET_HERE, - NULL, NULL, NULL, NULL, - __FILE__, __LINE__); - return(TRUE); -} - -// ---------------------------------------------------------------------- - -static FLAG FASTCALL -_BMinorToEnemyLess(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - - Consider a black minor (knight | bishop) taking an enemy pawn - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL : always TRUE, stop scanning in this direction - -**/ -{ -#ifdef DEBUG - PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; - PIECE p = pos->rgSquare[c].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(!IS_EMPTY(p)); - ASSERT(PIECE_VALUE(p) < VALUE_BISHOP); - ASSERT(!IS_EMPTY(pMinor)); - ASSERT(IS_PAWN(p)); - ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor)); - ASSERT(OPPOSITE_COLORS(p, pMinor)); - ASSERT(GET_COLOR(pMinor) == BLACK); -#endif - *puMobility += (!UNSAFE_FOR_MINOR(pos->rgSquare[c|8].bvAttacks[WHITE])); - ASSERT(*puMobility <= 8); - return(TRUE); // stop scanning this dir -} - -static FLAG FASTCALL -_WMinorToEnemyLess(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; - PIECE p = pos->rgSquare[c].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(!IS_EMPTY(p)); - ASSERT(IS_PAWN(p)); - ASSERT(PIECE_VALUE(p) < VALUE_BISHOP); - ASSERT(!IS_EMPTY(pMinor)); - ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor)); - ASSERT(OPPOSITE_COLORS(p, pMinor)); - ASSERT(GET_COLOR(pMinor) == WHITE); -#endif - *puMobility += (!UNSAFE_FOR_MINOR(pos->rgSquare[c|8].bvAttacks[BLACK])); - ASSERT(*puMobility <= 8); - return(TRUE); // stop scanning this dir -} - -static FLAG FASTCALL -_WRookToEnemyLess(POSITION *pos, - COOR c, - ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ROOK(pRook)); - ASSERT(!IS_EMPTY(p)); - ASSERT(OPPOSITE_COLORS(p, pRook)); - ASSERT(PIECE_VALUE(p) < VALUE_ROOK); - ASSERT(WHITE == GET_COLOR(pRook)); -#endif - *puMobility += (!UNSAFE_FOR_ROOK(pos->rgSquare[c|8].bvAttacks[BLACK])); - ASSERT(*puMobility <= 7); - return(TRUE); // stop scanning this dir -} - -static FLAG FASTCALL -_BRookToEnemyLess(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_ROOK(pRook)); - ASSERT(OPPOSITE_COLORS(p, pRook)); - ASSERT(PIECE_VALUE(p) < VALUE_ROOK); - ASSERT(BLACK == GET_COLOR(pRook)); -#endif - *puMobility += (!UNSAFE_FOR_ROOK(pos->rgSquare[c|8].bvAttacks[WHITE])); - ASSERT(*puMobility <= 7); - return(TRUE); // stop scanning this dir -} - - -static FLAG FASTCALL -_BQueenToEnemyLess(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_QUEEN(pQueen)); - ASSERT(PIECE_VALUE(p) < VALUE_QUEEN); - ASSERT(GET_COLOR(pQueen) == BLACK); - ASSERT(OPPOSITE_COLORS(pQueen, p)); -#endif - *puMobility += (!UNSAFE_FOR_QUEEN(pos->rgSquare[c|8].bvAttacks[WHITE])); - ASSERT(*puMobility <= 27); - return(TRUE); // stop scanning this dir -} - - -static FLAG FASTCALL -_WQueenToEnemyLess(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_QUEEN(pQueen)); - ASSERT(PIECE_VALUE(p) < VALUE_QUEEN); - ASSERT(GET_COLOR(pQueen) == WHITE); - ASSERT(OPPOSITE_COLORS(pQueen, p)); -#endif - *puMobility += (!UNSAFE_FOR_QUEEN(pos->rgSquare[c|8].bvAttacks[BLACK])); - ASSERT(*puMobility <= 27); - return(TRUE); // stop scanning this dir -} - -// ---------------------------------------------------------------------- - -static FLAG FASTCALL -_EMinorToEnemySame(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; - PIECE p = pos->rgSquare[c].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(!IS_EMPTY(p)); - ASSERT(!IS_EMPTY(pMinor)); - ASSERT(OPPOSITE_COLORS(p, pMinor)); - - if (IS_BISHOP(pMinor)) - { - ASSERT(PIECE_VALUE(p) == VALUE_BISHOP); - } - else - { - ASSERT(IS_KNIGHT(pMinor)); - ASSERT(PIECE_VALUE(p) >= VALUE_KNIGHT); - } -#endif - *puMobility += 1; - ASSERT(*puMobility <= 8); - return(TRUE); // stop scanning this dir -} - - -static FLAG FASTCALL -_ERookToEnemySame(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_ROOK(pRook)); - ASSERT(!IS_EMPTY(p)); - ASSERT(IS_ROOK(p)); - ASSERT(OPPOSITE_COLORS(p, pRook)); -#endif - *puMobility += 1; - ASSERT(*puMobility <= 7); - return(TRUE); // stop scanning this dir -} - - -static FLAG FASTCALL -_EQueenToEnemyGreaterEqual(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_QUEEN(pQueen)); - ASSERT(!IS_EMPTY(p)); - ASSERT(PIECE_VALUE(p) >= VALUE_QUEEN); - ASSERT(OPPOSITE_COLORS(pQueen, p)); -#endif - *puMobility += 1; - ASSERT(*puMobility <= 27); - return(TRUE); // stop scanning this dir -} - -// ---------------------------------------------------------------------- - -static FLAG FASTCALL -_EMinorToEnemyGreater(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; - PIECE p = pos->rgSquare[c].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(!IS_EMPTY(p)); - ASSERT(PIECE_VALUE(p) > VALUE_BISHOP); - ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor)); - ASSERT(OPPOSITE_COLORS(p, pMinor)); -#endif - *puMobility += 1; - ASSERT(*puMobility <= 8); - *puBit = MINOR_XRAY_BIT; - return(FALSE); // keep scanning this dir -} - -static FLAG FASTCALL -_ERookToEnemyGreater(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_ROOK(pRook)); - ASSERT(!IS_EMPTY(p)); - ASSERT(OPPOSITE_COLORS(p, pRook)); - ASSERT(PIECE_VALUE(p) > VALUE_ROOK); -#endif - *puMobility += 1; - ASSERT(*puMobility <= 7); - *puBit = ROOK_XRAY_BIT; - return(FALSE); // keep scanning this dir -} - -// ---------------------------------------------------------------------- - -static FLAG FASTCALL -_AnythingToFriendNoXray(IN POSITION *pos, - IN COOR c, - UNUSED ULONG *puMobility, - UNUSED ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - COOR cMover = pos->cPiece; - PIECE pMover = pos->rgSquare[cMover].pPiece; - PIECE pFriend = pos->rgSquare[c].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(cMover)); - ASSERT(!IS_EMPTY(pFriend)); - ASSERT(!IS_EMPTY(pMover)); - ASSERT(GET_COLOR(pMover) == GET_COLOR(pFriend)); - if (IS_BISHOP(pMover)) - { - ASSERT(!IS_BISHOP(pFriend)); - ASSERT(!IS_QUEEN(pFriend)); - } - else if (IS_ROOK(pMover)) - { - ASSERT(!IS_QUEEN(pFriend)); - ASSERT(!IS_ROOK(pFriend)); - } - else if (IS_QUEEN(pMover)) - { - ASSERT(!IS_BISHOP(pFriend)); - ASSERT(!IS_ROOK(pFriend)); - ASSERT(!IS_QUEEN(pFriend)); - } -#endif - return(TRUE); // stop scanning this dir -} - -// ---------------------------------------------------------------------- - -static FLAG FASTCALL -_EMinorToFriendXray(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; - PIECE p = pos->rgSquare[c].pPiece; - - ASSERT(!IS_EMPTY(p)); - ASSERT(!IS_EMPTY(pMinor)); - ASSERT(IS_BISHOP(pMinor)); - ASSERT(GET_COLOR(p) == GET_COLOR(pMinor)); -#endif - *puBit = MINOR_XRAY_BIT; - return(FALSE); -} - -static FLAG FASTCALL -_BRookToFriendRook(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ - COOR cRook = pos->cPiece; - FLAG fHoriz = ((c & 0xF0) == (cRook & 0xF0)); -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pRook = pos->rgSquare[cRook].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(cRook)); - ASSERT(IS_ROOK(pRook)); - ASSERT(!IS_EMPTY(p)); - ASSERT(IS_ROOK(p)); - ASSERT(GET_COLOR(p) == GET_COLOR(pRook)); - ASSERT(GET_COLOR(p) == BLACK); - ASSERT((fHoriz && (RANK(c) == RANK(pos->cPiece))) || - (!fHoriz && (FILE(c) == FILE(pos->cPiece)))); -#endif - EVAL_TERM(BLACK, - ROOK, - c, - pos->iScore[BLACK], - (ROOK_CONNECTED_HORIZ * fHoriz + - ROOK_CONNECTED_VERT * FLIP(fHoriz)), - "rook connected"); - *puBit = ROOK_XRAY_BIT; - return(FALSE); -} - - -static FLAG FASTCALL -_WRookToFriendRook(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ - COOR cRook = pos->cPiece; - FLAG fHoriz = ((c & 0xF0) == (cRook & 0xF0)); -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pRook = pos->rgSquare[cRook].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(cRook)); - ASSERT(IS_ROOK(pRook)); - ASSERT(!IS_EMPTY(p)); - ASSERT(IS_ROOK(p)); - ASSERT(GET_COLOR(p) == GET_COLOR(pRook)); - ASSERT(GET_COLOR(p) == WHITE); - ASSERT((fHoriz && (RANK(c) == RANK(pos->cPiece))) || - (!fHoriz && (FILE(c) == FILE(pos->cPiece)))); -#endif - EVAL_TERM(WHITE, - ROOK, - c, - pos->iScore[WHITE], - (ROOK_CONNECTED_HORIZ * fHoriz + - ROOK_CONNECTED_VERT * FLIP(fHoriz)), - "rook connected"); - *puBit = ROOK_XRAY_BIT; - return(FALSE); -} - - -static FLAG FASTCALL -_ERookToFriendQueen(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_ROOK(pRook)); - ASSERT(!IS_EMPTY(p)); - ASSERT(IS_QUEEN(p)); - ASSERT(GET_COLOR(p) == GET_COLOR(pRook)); -#endif - *puBit = ROOK_XRAY_BIT; - return(FALSE); -} - -static FLAG FASTCALL -_EQueenToFriendBishop(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ - COOR cQueen = pos->cPiece; -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pQueen = pos->rgSquare[cQueen].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(cQueen)); - ASSERT(IS_QUEEN(pQueen)); - ASSERT(IS_BISHOP(p)); - ASSERT(GET_COLOR(p) == GET_COLOR(pQueen)); -#endif - *puBit = QUEEN_XRAY_BIT; - return((((c & 0xF0) == (cQueen & 0xF0)) || - ((c & 0x0F) == (cQueen & 0x0F)))); -} - - -static FLAG FASTCALL -_EQueenToFriendRook(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ - COOR cQueen = pos->cPiece; -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pQueen = pos->rgSquare[cQueen].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(cQueen)); - ASSERT(IS_QUEEN(pQueen)); - ASSERT(IS_ROOK(p)); - ASSERT(GET_COLOR(p) == GET_COLOR(pQueen)); -#endif - *puBit = QUEEN_XRAY_BIT; - return(FLIP((((c & 0xF0) == (cQueen & 0xF0)) || - ((c & 0x0F) == (cQueen & 0x0F))))); -} - - -static FLAG FASTCALL -_EQueenToFriendQueen(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - COOR cQueen = pos->cPiece; - PIECE p = pos->rgSquare[c].pPiece; - PIECE pQueen = pos->rgSquare[cQueen].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(cQueen)); - ASSERT(IS_QUEEN(pQueen)); - ASSERT(IS_QUEEN(p)); - ASSERT(GET_COLOR(p) == GET_COLOR(pQueen)); -#endif - *puBit = QUEEN_XRAY_BIT; - return(FALSE); -} - - -// ---------------------------------------------------------------------- - -static FLAG FASTCALL -_BMinorToEmpty(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; - PIECE p = pos->rgSquare[c].pPiece; - - ASSERT(IS_EMPTY(p)); - ASSERT(!IS_EMPTY(pMinor)); - ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor)); - ASSERT(GET_COLOR(pMinor) == BLACK); -#endif - *puMobility += (!UNSAFE_FOR_MINOR(pos->rgSquare[c|8].bvAttacks[WHITE])); - ASSERT(*puMobility <= 8); - return(FALSE); -} - -static FLAG FASTCALL -_WMinorToEmpty(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece; - PIECE p = pos->rgSquare[c].pPiece; - - ASSERT(IS_EMPTY(p)); - ASSERT(!IS_EMPTY(pMinor)); - ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor)); - ASSERT(GET_COLOR(pMinor) == WHITE); -#endif - *puMobility += (!UNSAFE_FOR_MINOR(pos->rgSquare[c|8].bvAttacks[BLACK])); - ASSERT(*puMobility <= 8); - return(FALSE); -} - -static FLAG FASTCALL -_WRookToEmpty(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit - -Return value: - - static FLAG FASTCALL - -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; + _EvaluateConnectedSupportedOutsidePassers(pos, pHash); - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_ROOK(pRook)); - ASSERT(IS_EMPTY(p)); - ASSERT(GET_COLOR(pRook) == WHITE); -#endif - *puMobility += (!UNSAFE_FOR_ROOK(pos->rgSquare[c|8].bvAttacks[BLACK])); - ASSERT(*puMobility <= 7); - return(FALSE); + // + // TODO: recognize quartgrips and stonewalls + // + return(pHash); } -static FLAG FASTCALL -_BRookToEmpty(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) + +ULONG +CountKingSafetyDefects(IN OUT POSITION *pos, + IN ULONG uSide) /** Routine description: + Determine how many defects uSide's king position has _quickly_. + TODO: add more knowledge as cheaply as possible... + Parameters: POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit + ULONG uSide Return value: - static FLAG FASTCALL + ULONG **/ { -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pRook = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_ROOK(pRook)); - ASSERT(IS_EMPTY(p)); - ASSERT(GET_COLOR(pRook) == BLACK); -#endif - *puMobility += (!UNSAFE_FOR_ROOK(pos->rgSquare[c|8].bvAttacks[WHITE])); - ASSERT(*puMobility <= 7); - return(FALSE); -} - -static FLAG FASTCALL -_BQueenToEmpty(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) -/** - -Routine description: + ULONG uCounter = 0; + ULONG xSide = FLIP(uSide); + COOR cKing; + COOR c; + int i; + PIECE p; + ULONG u; -Parameters: + // + // Don't count king safety defects if the real eval code in + // _EvalKing would not... + // + if (pos->uNonPawnMaterial[xSide] < DO_KING_SAFETY_THRESHOLD) { + return 0; + } - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit + cKing = pos->cNonPawns[uSide][0]; + uCounter = KING_INITIAL_COUNTER_BY_LOCATION[uSide][cKing] >> 1; + ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); + ASSERT(pos->rgSquare[cKing].uIndex == 0); + ASSERT(IS_ON_BOARD(cKing)); + ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == uSide); -Return value: + // + // Make sure cKing - 1, cKing and cKing + 1 are on the board + // + cKing += (!IS_ON_BOARD(cKing - 1)); + cKing -= (!IS_ON_BOARD(cKing + 1)); + ASSERT(IS_ON_BOARD(cKing)); + ASSERT(IS_ON_BOARD(cKing + 1)); + ASSERT(IS_ON_BOARD(cKing - 1)); - static FLAG FASTCALL + // + // Consider all enemy pieces except the king (not including pawns) + // + for (u = 1; + u < pos->uNonPawnCount[xSide][0]; + u++) + { + c = pos->cNonPawns[xSide][u]; + ASSERT(IS_ON_BOARD(c)); + i = (int)c - (int)(cKing + 1); + ASSERT((i >= -128) && (i <= 125)); + p = pos->rgSquare[c].pPiece; + ASSERT(pos->rgSquare[c].uIndex == u); + ASSERT(!IS_KING(p)); + ASSERT(GET_COLOR(p) == xSide); + p = 1 << PIECE_TYPE(p); -**/ -{ -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; + uCounter += ((i == 0) | (i == -2) | + ((CHECK_VECTOR_WITH_INDEX(i, xSide) & p) != 0) | + ((CHECK_VECTOR_WITH_INDEX(i + 1, xSide) & p) != 0) | + ((CHECK_VECTOR_WITH_INDEX(i + 2, xSide) & p) != 0)); + } + ASSERT(uCounter < 15); - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_QUEEN(pQueen)); - ASSERT(IS_EMPTY(p)); - ASSERT(GET_COLOR(pQueen) == BLACK); -#endif - *puMobility += (!UNSAFE_FOR_QUEEN(pos->rgSquare[c|8].bvAttacks[WHITE])); - ASSERT(*puMobility <= 27); - return(FALSE); + // Save the number of enemy pieces pointing at this king for later use. + pos->uPiecesPointingAtKing[uSide] = + (uCounter - (KING_INITIAL_COUNTER_BY_LOCATION[uSide][cKing] >> 1)); + return uCounter; } -static FLAG FASTCALL -_WQueenToEmpty(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) + +static void +EstimatePositionalScore(IN POSITION *pos, + IN UNUSED PAWN_HASH_ENTRY *pHash, + IN OUT SCORE *piAlphaMargin, + IN OUT SCORE *piBetaMargin) /** Routine description: + Before doing an early lazy eval, look at the position and widen + the alpha/beta margins to account for the positional terms that + have not been computed yet at this point in Eval() -- king safety + plus a flat residual covering mobility, passers, and everything + else that genuinely requires attack-generation to know exactly. + + Both terms below are calibrated from measured data (CALIBRATE_ + POSITIONAL instrumentation, ~1.6M full-eval samples over an ECM + slice), not guessed: for each value, p90 of the *actual* score + swing between this point in Eval() and full-eval completion, so + the resulting margin is wrong (too narrow) on at most ~10% of + calls, in either bucket. See conversation history for the + percentile tables -- if re-tuning, regenerate them, don't hand- + edit these numbers. + + Note: king safety and "everything else" turned out to be close to + independent of piece count and of each other, so summing their + two p90s (rather than deriving one joint p90) is a deliberately + conservative (wider than strictly necessary) combination. + + O(1) -- no attack bitboard generation -- so it's safe to call + whenever the cheap material-only lazy check (the caller's + first-pass margin) wasn't enough to resolve the cutoff on its own. + Parameters: - POSITION *pos, - COOR c, - ULONG *puMobility, - ULONG *puBit + POSITION *pos + PAWN_HASH_ENTRY *pHash : unused now (kept for call-site symmetry); + pHash->iScore is already folded into pos->iScore by this point, + and the passer-specific estimate this used to compute turned + out to be negligible next to the residual term below. + SCORE *piAlphaMargin, *piBetaMargin : widened in place, identically + (no measured basis for an asymmetric alpha/beta split) Return value: - static FLAG FASTCALL + void **/ { -#ifdef DEBUG - PIECE p = pos->rgSquare[c].pPiece; - PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece; + // p90 of |true king-safety swing|, indexed by combined defect + // count (CountKingSafetyDefects(stm) + CountKingSafetyDefects(xsm)), + // clamped above index 10 (sparse data beyond that). + static const SCORE iKingSwingP90[11] = { + 47, 62, 87, 119, 169, 157, 181, 282, 342, 342, 385 + }; + // p90 of |mobility + passers + everything else combined|, measured + // directly (no useful correlation found with piece count). + static const SCORE iResidualP90 = 154; + ULONG uDefects = (CountKingSafetyDefects(pos, WHITE) + + CountKingSafetyDefects(pos, BLACK)); + SCORE iKingTerm = iKingSwingP90[MINU(10, uDefects)]; - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_QUEEN(pQueen)); - ASSERT(IS_EMPTY(p)); - ASSERT(GET_COLOR(pQueen) == WHITE); -#endif - *puMobility += (!UNSAFE_FOR_QUEEN(pos->rgSquare[c|8].bvAttacks[BLACK])); - ASSERT(*puMobility <= 27); - return(FALSE); + *piAlphaMargin += iKingTerm + iResidualP90; + *piBetaMargin += iKingTerm + iResidualP90; } - -static FLAG FASTCALL -_EBishopToFriendPawn(IN POSITION *pos, - IN COOR c, - IN OUT ULONG *puMobility, - IN OUT ULONG *puBit) +static void +_InvalidEvaluator(UNUSED POSITION *pos, + UNUSED COOR c, + UNUSED PAWN_HASH_ENTRY *pHash) /** Routine description: + This code should never be called + Parameters: POSITION *pos, COOR c, - ULONG *puMobility, - ULONG *puBit + PAWN_HASH_ENTRY *pHash, Return value: - static FLAG FASTCALL + void **/ { -#ifdef DEBUG - PIECE pPawn = pos->rgSquare[c].pPiece; - PIECE pBishop = pos->rgSquare[pos->cPiece].pPiece; - - ASSERT(IS_ON_BOARD(c)); - ASSERT(IS_ON_BOARD(pos->cPiece)); - ASSERT(IS_BISHOP(pBishop)); - ASSERT(IS_PAWN(pPawn)); - ASSERT(GET_COLOR(pBishop) == GET_COLOR(pPawn)); -#endif - *puMobility += ((pos->bb & COOR_TO_BB(c)) != 0); - return(TRUE); + UtilPanic(SHOULD_NOT_GET_HERE, + NULL, NULL, NULL, NULL, + __FILE__, __LINE__); } - - - // // ====================================================================== // @@ -3514,39 +2499,48 @@ Return value: { static const BITBOARD bbColorSq[2] = { 0x55aa55aa55aa55aaULL, 0xaa55aa55aa55aa55ULL }; - static const PMOBILITY_HELPER BMobJumpTable[2][14] = + // + // Mobility outcome for each (mover-color, piece-landed-on) pair, + // replacing a table of function pointers with a table of case tags + // dispatched via switch -- avoids an indirect call/ret (and the + // associated indirect-branch-predictor miss, since the target piece + // varies square to square) per square visited on every bishop ray, + // the hottest inner loop in eval. See the switch in the ray-walking + // loop below for what each tag actually does. + // + static const UCHAR BMobCaseTable[2][14] = { {// (black) - _BMinorToEmpty, // EMPTY_SQUARE (0) - _InvalidMobilityHelper, // INVALID_PIECE (1) - _EBishopToFriendPawn, // BLACK_PAWN (2) - _BMinorToEnemyLess, // WHITE_PAWN (3) - _AnythingToFriendNoXray, // BLACK_KNIGHT (4) - _EMinorToEnemySame, // WHITE_KNIGHT (5) - _EMinorToFriendXray, // BLACK_BISHOP (6) - _EMinorToEnemySame, // WHITE_BISHOP (7) - _AnythingToFriendNoXray, // BLACK_ROOK (8) - _EMinorToEnemyGreater, // WHITE_ROOK (9) - _EMinorToFriendXray, // BLACK_QUEEN (10) - _EMinorToEnemyGreater, // WHITE_QUEEN (11) - _AnythingToFriendNoXray, // BLACK_KING (12) - _EMinorToEnemyGreater, // WHITE_KING (13) + BMOB_EMPTY, // EMPTY_SQUARE (0) + BMOB_INVALID, // INVALID_PIECE (1) + BMOB_FRIEND_PAWN, // BLACK_PAWN (2) + BMOB_ENEMY_PAWN, // WHITE_PAWN (3) + BMOB_FRIEND_BLOCK, // BLACK_KNIGHT (4) + BMOB_ENEMY_SAME, // WHITE_KNIGHT (5) + BMOB_FRIEND_XRAY, // BLACK_BISHOP (6) + BMOB_ENEMY_SAME, // WHITE_BISHOP (7) + BMOB_FRIEND_BLOCK, // BLACK_ROOK (8) + BMOB_ENEMY_GREATER, // WHITE_ROOK (9) + BMOB_FRIEND_XRAY, // BLACK_QUEEN (10) + BMOB_ENEMY_GREATER, // WHITE_QUEEN (11) + BMOB_FRIEND_BLOCK, // BLACK_KING (12) + BMOB_ENEMY_GREATER, // WHITE_KING (13) }, {// (white) - _WMinorToEmpty, // EMPTY_SQUARE (0) - _InvalidMobilityHelper, // INVALID_PIECE (1) - _WMinorToEnemyLess, // BLACK_PAWN (2) - _EBishopToFriendPawn, // WHITE_PAWN (3) - _EMinorToEnemySame, // BLACK_KNIGHT (4) - _AnythingToFriendNoXray, // WHITE_KNIGHT (5) - _EMinorToEnemySame, // BLACK_BISHOP (6) - _EMinorToFriendXray, // WHITE_BISHOP (7) - _EMinorToEnemyGreater, // BLACK_ROOK (8) - _AnythingToFriendNoXray, // WHITE_ROOK (9) - _EMinorToEnemyGreater, // BLACK_QUEEN (10) - _EMinorToFriendXray, // WHITE_QUEEN (11) - _EMinorToEnemyGreater, // BLACK_KING (12) - _AnythingToFriendNoXray, // WHITE_KING (13) + BMOB_EMPTY, // EMPTY_SQUARE (0) + BMOB_INVALID, // INVALID_PIECE (1) + BMOB_ENEMY_PAWN, // BLACK_PAWN (2) + BMOB_FRIEND_PAWN, // WHITE_PAWN (3) + BMOB_ENEMY_SAME, // BLACK_KNIGHT (4) + BMOB_FRIEND_BLOCK, // WHITE_KNIGHT (5) + BMOB_ENEMY_SAME, // BLACK_BISHOP (6) + BMOB_FRIEND_XRAY, // WHITE_BISHOP (7) + BMOB_ENEMY_GREATER, // BLACK_ROOK (8) + BMOB_FRIEND_BLOCK, // WHITE_ROOK (9) + BMOB_ENEMY_GREATER, // BLACK_QUEEN (10) + BMOB_FRIEND_XRAY, // WHITE_QUEEN (11) + BMOB_ENEMY_GREATER, // BLACK_KING (12) + BMOB_FRIEND_BLOCK, // WHITE_KING (13) } }; static const COOR cBishopAtHome[2][2] = @@ -3566,7 +2560,6 @@ Return value: ULONG uCurrentMobility; ULONG uBit; PIECE p; - PMOBILITY_HELPER pFun; ASSERT(IS_ON_BOARD(c)); p = pos->rgSquare[c].pPiece; @@ -3672,6 +2665,8 @@ Return value: while(IS_ON_BOARD(cSquare)) { + FLAG fStop; + // // Always toggle attack table bits. // @@ -3679,15 +2674,63 @@ Return value: pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing |= uBit; // - // What did we hit? + // What did we hit? Dispatched via switch instead of an + // indirect call through a function pointer -- the target + // piece varies square to square, so the old jump table + // defeated the CPU's indirect-branch predictor on every + // step of every ray. See BMOB_CASE above for what each tag + // means. // p = pos->rgSquare[cSquare].pPiece; - pFun = BMobJumpTable[uColor][p]; - ASSERT(pFun); - if (TRUE == (*pFun)(pos, - cSquare, - &uCurrentMobility, - &uBit)) + switch (BMobCaseTable[uColor][p]) + { + case BMOB_EMPTY: + uCurrentMobility += + !UNSAFE_FOR_MINOR(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]); + fStop = FALSE; + break; + + case BMOB_ENEMY_PAWN: + uCurrentMobility += + !UNSAFE_FOR_MINOR(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]); + fStop = TRUE; + break; + + case BMOB_FRIEND_PAWN: + uCurrentMobility += ((pos->bb & COOR_TO_BB(cSquare)) != 0); + fStop = TRUE; + break; + + case BMOB_FRIEND_BLOCK: + fStop = TRUE; + break; + + case BMOB_ENEMY_SAME: + uCurrentMobility += 1; + fStop = TRUE; + break; + + case BMOB_FRIEND_XRAY: + uBit = MINOR_XRAY_BIT; + fStop = FALSE; + break; + + case BMOB_ENEMY_GREATER: + uCurrentMobility += 1; + uBit = MINOR_XRAY_BIT; + fStop = FALSE; + break; + + case BMOB_INVALID: + default: + UtilPanic(SHOULD_NOT_GET_HERE, + NULL, NULL, NULL, NULL, + __FILE__, __LINE__); + fStop = TRUE; + break; + } + ASSERT(uCurrentMobility <= 8); + if (TRUE == fStop) { break; } @@ -3716,38 +2759,34 @@ Return value: "consecutive bishop mobility"); // - // Look for bishops with no mobility who are under attack. These - // pieces are trapped! + // Look for bishops with no mobility, they are trapped and, later, + // we'll see if they are also under attack too. // if (uTotalMobility == 0) { _RecordTrappedCandidate(pos, uColor, c); } + +#if 0 + // This is never used right now. uTotalMobility /= 2; ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0); ASSERT((uTotalMobility & 0x80000000) == 0); pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], uTotalMobility); +#endif // // Bonus for a bishop that's securely placed -- safe from ever // being challenged by an enemy pawn, and (checked below) defended - // by a friendly one -- near the enemy king. This is really a - // bishop-specific king-tropism/outpost bonus; despite the - // historical "active bad bishop" name, it does NOT check whether - // the bishop is actually "bad" (see the good/bad pawn-count terms - // above) before applying. Candidate for folding into a unified - // per-piece-type tropism system alongside - // KNIGHT_KING_TROPISM_BONUS/QUEEN_KING_TROPISM (shelved for now, - // pending a look at what every piece type currently does). + // by a friendly one -- near the enemy king. This is a + // bishop-specific king-tropism/outpost bonus. // bb = pHash->bbPawnLocations[FLIP(uColor)] & (~pHash->bbStationaryPawns[FLIP(uColor)]); if (TRUE == _IsSquareSafeFromEnemyPawn(pos, c, bb)) { - // - // Give a bonus based on distance from enemy king - // + // Defended (and defending) a friendly pawn. if (pos->rgSquare[c|8].bvAttacks[uColor].small.uPawn) { #ifdef DEBUG @@ -3790,39 +2829,39 @@ Return value: **/ { static const int iPawnStart[2] = { -17, +15 }; - static const PMOBILITY_HELPER NMobJumpTable[2][14] = + static const UCHAR NMobCaseTable[2][14] = { {// (black) - _BMinorToEmpty, // EMPTY_SQUARE (0) - _InvalidMobilityHelper, // INVALID_PIECE (1) - _AnythingToFriendNoXray, // BLACK_PAWN (2) - _BMinorToEnemyLess, // WHITE_PAWN (3) - _AnythingToFriendNoXray, // BLACK_KNIGHT (4) - _EMinorToEnemySame, // WHITE_KNIGHT (5) - _AnythingToFriendNoXray, // BLACK_BISHOP (6) - _EMinorToEnemySame, // WHITE_BISHOP (7) - _AnythingToFriendNoXray, // BLACK_ROOK (8) - _EMinorToEnemySame, // WHITE_ROOK (9) - _AnythingToFriendNoXray, // BLACK_QUEEN (10) - _EMinorToEnemySame, // WHITE_QUEEN (11) - _AnythingToFriendNoXray, // BLACK_KING (12) - _EMinorToEnemySame, // WHITE_KING (13) + NMOB_MOBILE_SQUARE, // EMPTY_SQUARE (0) + NMOB_INVALID, // INVALID_PIECE (1) + NMOB_FRIEND, // BLACK_PAWN (2) + NMOB_MOBILE_SQUARE, // WHITE_PAWN (3) + NMOB_FRIEND, // BLACK_KNIGHT (4) + NMOB_ENEMY_OTHER, // WHITE_KNIGHT (5) + NMOB_FRIEND, // BLACK_BISHOP (6) + NMOB_ENEMY_OTHER, // WHITE_BISHOP (7) + NMOB_FRIEND, // BLACK_ROOK (8) + NMOB_ENEMY_OTHER, // WHITE_ROOK (9) + NMOB_FRIEND, // BLACK_QUEEN (10) + NMOB_ENEMY_OTHER, // WHITE_QUEEN (11) + NMOB_FRIEND, // BLACK_KING (12) + NMOB_ENEMY_OTHER, // WHITE_KING (13) }, {// (white) - _WMinorToEmpty, // EMPTY_SQUARE (0) - _InvalidMobilityHelper, // INVALID_PIECE (1) - _WMinorToEnemyLess, // BLACK_PAWN (2) - _AnythingToFriendNoXray, // WHITE_PAWN (3) - _EMinorToEnemySame, // BLACK_KNIGHT (4) - _AnythingToFriendNoXray, // WHITE_KNIGHT (5) - _EMinorToEnemySame, // BLACK_BISHOP (6) - _AnythingToFriendNoXray, // WHITE_BISHOP (7) - _EMinorToEnemySame, // BLACK_ROOK (8) - _AnythingToFriendNoXray, // WHITE_ROOK (9) - _EMinorToEnemySame, // BLACK_QUEEN (10) - _AnythingToFriendNoXray, // WHITE_QUEEN (11) - _EMinorToEnemySame, // BLACK_KING (12) - _AnythingToFriendNoXray, // WHITE_KING (13) + NMOB_MOBILE_SQUARE, // EMPTY_SQUARE (0) + NMOB_INVALID, // INVALID_PIECE (1) + NMOB_MOBILE_SQUARE, // BLACK_PAWN (2) + NMOB_FRIEND, // WHITE_PAWN (3) + NMOB_ENEMY_OTHER, // BLACK_KNIGHT (4) + NMOB_FRIEND, // WHITE_KNIGHT (5) + NMOB_ENEMY_OTHER, // BLACK_BISHOP (6) + NMOB_FRIEND, // WHITE_BISHOP (7) + NMOB_ENEMY_OTHER, // BLACK_ROOK (8) + NMOB_FRIEND, // WHITE_ROOK (9) + NMOB_ENEMY_OTHER, // BLACK_QUEEN (10) + NMOB_FRIEND, // WHITE_QUEEN (11) + NMOB_ENEMY_OTHER, // BLACK_KING (12) + NMOB_FRIEND, // WHITE_KING (13) } }; static const COOR cKnightAtHome[2][2] = @@ -3839,7 +2878,6 @@ Return value: ULONG uMobilitySquares; SCORE i; ULONG uDist; - PMOBILITY_HELPER pFun; p = pos->rgSquare[c].pPiece; ASSERT(p && IS_KNIGHT(p)); @@ -4013,16 +3051,31 @@ Return value: pos->rgSquare[cSquare|8].bvAttacks[uColor].small.uMinor = 1; // - // See what we hit + // See what we hit. Dispatched via switch instead of an + // indirect call through a function pointer -- same + // rationale as the bishop ray-walk above. // p = pos->rgSquare[cSquare].pPiece; - pFun = NMobJumpTable[uColor][p]; - if (pFun != _AnythingToFriendNoXray) + switch (NMobCaseTable[uColor][p]) { - (void)(*pFun)(pos, - cSquare, - &uMobilitySquares, - NULL); + case NMOB_MOBILE_SQUARE: + uMobilitySquares += + !UNSAFE_FOR_MINOR(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]); + break; + + case NMOB_ENEMY_OTHER: + uMobilitySquares += 1; + break; + + case NMOB_FRIEND: + break; + + case NMOB_INVALID: + default: + UtilPanic(SHOULD_NOT_GET_HERE, + NULL, NULL, NULL, NULL, + __FILE__, __LINE__); + break; } // @@ -4044,10 +3097,14 @@ Return value: { _RecordTrappedCandidate(pos, uColor, c); } + +#if 0 + // This is never used right now. ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0); ASSERT((uMobilitySquares & 0x80000000) == 0); pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], uMobilitySquares); +#endif } static void @@ -4070,39 +3127,39 @@ Return value: **/ { - static const PMOBILITY_HELPER RMobJumpTable[2][14] = + static const UCHAR RMobCaseTable[2][14] = { {// (black) - _BRookToEmpty, // EMPTY_SQUARE (0) - _InvalidMobilityHelper, // INVALID_PIECE (1) - _AnythingToFriendNoXray, // BLACK_PAWN (2) - _BRookToEnemyLess, // WHITE_PAWN (3) - _AnythingToFriendNoXray, // BLACK_KNIGHT (4) - _BRookToEnemyLess, // WHITE_KNIGHT (5) - _AnythingToFriendNoXray, // BLACK_BISHOP (6) - _BRookToEnemyLess, // WHITE_BISHOP (7) - _BRookToFriendRook, // BLACK_ROOK (8) - _ERookToEnemySame, // WHITE_ROOK (9) - _ERookToFriendQueen, // BLACK_QUEEN (10) - _ERookToEnemyGreater, // WHITE_QUEEN (11) - _AnythingToFriendNoXray, // BLACK_KING (12) - _ERookToEnemyGreater, // WHITE_KING (13) + RMOB_EMPTY, // EMPTY_SQUARE (0) + RMOB_INVALID, // INVALID_PIECE (1) + RMOB_FRIEND_BLOCK, // BLACK_PAWN (2) + RMOB_ENEMY_LESS, // WHITE_PAWN (3) + RMOB_FRIEND_BLOCK, // BLACK_KNIGHT (4) + RMOB_ENEMY_LESS, // WHITE_KNIGHT (5) + RMOB_FRIEND_BLOCK, // BLACK_BISHOP (6) + RMOB_ENEMY_LESS, // WHITE_BISHOP (7) + RMOB_FRIEND_ROOK, // BLACK_ROOK (8) + RMOB_ENEMY_SAME, // WHITE_ROOK (9) + RMOB_FRIEND_QUEEN, // BLACK_QUEEN (10) + RMOB_ENEMY_GREATER, // WHITE_QUEEN (11) + RMOB_FRIEND_BLOCK, // BLACK_KING (12) + RMOB_ENEMY_GREATER, // WHITE_KING (13) }, {// (white) - _WRookToEmpty, // EMPTY_SQUARE (0) - _InvalidMobilityHelper, // INVALID_PIECE (1) - _WRookToEnemyLess, // BLACK_PAWN (2) - _AnythingToFriendNoXray, // WHITE_PAWN (3) - _WRookToEnemyLess, // BLACK_KNIGHT (4) - _AnythingToFriendNoXray, // WHITE_KNIGHT (5) - _WRookToEnemyLess, // BLACK_BISHOP (6) - _AnythingToFriendNoXray, // WHITE_BISHOP (7) - _ERookToEnemySame, // BLACK_ROOK (8) - _WRookToFriendRook, // WHITE_ROOK (9) - _ERookToEnemyGreater, // BLACK_QUEEN (10) - _ERookToFriendQueen, // WHITE_QUEEN (11) - _ERookToEnemyGreater, // BLACK_KING (12) - _AnythingToFriendNoXray, // WHITE_KING (13) + RMOB_EMPTY, // EMPTY_SQUARE (0) + RMOB_INVALID, // INVALID_PIECE (1) + RMOB_ENEMY_LESS, // BLACK_PAWN (2) + RMOB_FRIEND_BLOCK, // WHITE_PAWN (3) + RMOB_ENEMY_LESS, // BLACK_KNIGHT (4) + RMOB_FRIEND_BLOCK, // WHITE_KNIGHT (5) + RMOB_ENEMY_LESS, // BLACK_BISHOP (6) + RMOB_FRIEND_BLOCK, // WHITE_BISHOP (7) + RMOB_ENEMY_SAME, // BLACK_ROOK (8) + RMOB_FRIEND_ROOK, // WHITE_ROOK (9) + RMOB_ENEMY_GREATER, // BLACK_QUEEN (10) + RMOB_FRIEND_QUEEN, // WHITE_QUEEN (11) + RMOB_ENEMY_GREATER, // BLACK_KING (12) + RMOB_FRIEND_BLOCK, // WHITE_KING (13) }, }; @@ -4116,7 +3173,6 @@ Return value: COOR cSquare; BITBOARD bb; ULONG uBit; - PMOBILITY_HELPER pFun; SCORE i; ASSERT(IS_ON_BOARD(c)); @@ -4283,12 +3339,20 @@ Return value: ASSERT(g_iRDeltas[u] != 0); do { + // Ray-invariant, same reasoning as the queen's fOrthogonalRay: + // every square on this ray shares the same rank relationship to + // c as the first step does. + FLAG fHoriz; + uCurrentMobility = 0; uBit = ROOK_BIT; cSquare = c + g_iRDeltas[u]; + fHoriz = ((cSquare & 0xF0) == (c & 0xF0)); while(IS_ON_BOARD(cSquare)) { + FLAG fStop; + // // Twiddle our attack table bits. // @@ -4296,14 +3360,67 @@ Return value: ASSERT((cSquare | 8) == (cSquare + 8)); // - // What did we hit? + // What did we hit? Dispatched via switch instead of an + // indirect call through a function pointer -- same + // rationale as the bishop ray-walk above. // p = pos->rgSquare[cSquare].pPiece; - pFun = RMobJumpTable[uColor][p]; - if (TRUE == (*pFun)(pos, - cSquare, - &uCurrentMobility, - &uBit)) + switch (RMobCaseTable[uColor][p]) + { + case RMOB_EMPTY: + uCurrentMobility += + !UNSAFE_FOR_ROOK(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]); + fStop = FALSE; + break; + + case RMOB_ENEMY_LESS: + uCurrentMobility += + !UNSAFE_FOR_ROOK(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]); + fStop = TRUE; + break; + + case RMOB_FRIEND_BLOCK: + fStop = TRUE; + break; + + case RMOB_FRIEND_ROOK: + EVAL_TERM(uColor, + ROOK, + cSquare, + pos->iScore[uColor], + (ROOK_CONNECTED_HORIZ * fHoriz + + ROOK_CONNECTED_VERT * FLIP(fHoriz)), + "rook connected"); + uBit = ROOK_XRAY_BIT; + fStop = FALSE; + break; + + case RMOB_ENEMY_SAME: + uCurrentMobility += 1; + fStop = TRUE; + break; + + case RMOB_FRIEND_QUEEN: + uBit = ROOK_XRAY_BIT; + fStop = FALSE; + break; + + case RMOB_ENEMY_GREATER: + uCurrentMobility += 1; + uBit = ROOK_XRAY_BIT; + fStop = FALSE; + break; + + case RMOB_INVALID: + default: + UtilPanic(SHOULD_NOT_GET_HERE, + NULL, NULL, NULL, NULL, + __FILE__, __LINE__); + fStop = TRUE; + break; + } + ASSERT(uCurrentMobility <= 7); + if (TRUE == fStop) { break; } @@ -4340,10 +3457,13 @@ Return value: ROOK_MAX_MOBILITY_IN_A_ROW_BONUS[uMaxMobility], "consecutive rook mobility"); +#if 0 + // This is never used right now. ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0); ASSERT((uTotalMobility & 0x80000000) == 0); pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], uTotalMobility); +#endif if (uTotalMobility < 3) { // @@ -4427,39 +3547,39 @@ Return value: **/ { - static const PMOBILITY_HELPER QMobJumpTable[2][14] = + static const UCHAR QMobCaseTable[2][14] = { { - _BQueenToEmpty, // EMPTY_SQUARE (0) - _InvalidMobilityHelper, // INVALID_PIECE (1) - _AnythingToFriendNoXray, // BLACK_PAWN (2) - _BQueenToEnemyLess, // WHITE_PAWN (3) - _AnythingToFriendNoXray, // BLACK_KNIGHT (4) - _BQueenToEnemyLess, // WHITE_KNIGHT (5) - _EQueenToFriendBishop, // BLACK_BISHOP (6) - _BQueenToEnemyLess, // WHITE_BISHOP (7) - _EQueenToFriendRook, // BLACK_ROOK (8) - _BQueenToEnemyLess, // WHITE_ROOK (9) - _EQueenToFriendQueen, // BLACK_QUEEN (10) - _EQueenToEnemyGreaterEqual, // WHITE_QUEEN (11) - _AnythingToFriendNoXray, // BLACK_KING (12) - _EQueenToEnemyGreaterEqual, // WHITE_KING (13) + QMOB_EMPTY, // EMPTY_SQUARE (0) + QMOB_INVALID, // INVALID_PIECE (1) + QMOB_FRIEND_BLOCK, // BLACK_PAWN (2) + QMOB_ENEMY_LESS, // WHITE_PAWN (3) + QMOB_FRIEND_BLOCK, // BLACK_KNIGHT (4) + QMOB_ENEMY_LESS, // WHITE_KNIGHT (5) + QMOB_FRIEND_BISHOP, // BLACK_BISHOP (6) + QMOB_ENEMY_LESS, // WHITE_BISHOP (7) + QMOB_FRIEND_ROOK, // BLACK_ROOK (8) + QMOB_ENEMY_LESS, // WHITE_ROOK (9) + QMOB_FRIEND_QUEEN, // BLACK_QUEEN (10) + QMOB_ENEMY_GE, // WHITE_QUEEN (11) + QMOB_FRIEND_BLOCK, // BLACK_KING (12) + QMOB_ENEMY_GE, // WHITE_KING (13) }, { - _WQueenToEmpty, // EMPTY_SQUARE (0) - _InvalidMobilityHelper, // INVALID_PIECE (1) - _WQueenToEnemyLess, // BLACK_PAWN (2) - _AnythingToFriendNoXray, // WHITE_PAWN (3) - _WQueenToEnemyLess, // BLACK_KNIGHT (4) - _AnythingToFriendNoXray, // WHITE_KNIGHT (5) - _WQueenToEnemyLess, // BLACK_BISHOP (6) - _EQueenToFriendBishop, // WHITE_BISHOP (7) - _WQueenToEnemyLess, // BLACK_ROOK (8) - _EQueenToFriendRook, // WHITE_ROOK (9) - _EQueenToEnemyGreaterEqual, // BLACK_QUEEN (10) - _EQueenToFriendQueen, // WHITE_QUEEN (11) - _EQueenToEnemyGreaterEqual, // BLACK_KING (12) - _AnythingToFriendNoXray, // WHITE_KING (13) + QMOB_EMPTY, // EMPTY_SQUARE (0) + QMOB_INVALID, // INVALID_PIECE (1) + QMOB_ENEMY_LESS, // BLACK_PAWN (2) + QMOB_FRIEND_BLOCK, // WHITE_PAWN (3) + QMOB_ENEMY_LESS, // BLACK_KNIGHT (4) + QMOB_FRIEND_BLOCK, // WHITE_KNIGHT (5) + QMOB_ENEMY_LESS, // BLACK_BISHOP (6) + QMOB_FRIEND_BISHOP, // WHITE_BISHOP (7) + QMOB_ENEMY_LESS, // BLACK_ROOK (8) + QMOB_FRIEND_ROOK, // WHITE_ROOK (9) + QMOB_ENEMY_GE, // BLACK_QUEEN (10) + QMOB_FRIEND_QUEEN, // WHITE_QUEEN (11) + QMOB_ENEMY_GE, // BLACK_KING (12) + QMOB_FRIEND_BLOCK, // WHITE_KING (13) }, }; @@ -4469,7 +3589,6 @@ Return value: ULONG uTotalMobility; ULONG u; ULONG uBit; - PMOBILITY_HELPER pFun; COOR cKing; ASSERT(IS_ON_BOARD(c)); @@ -4522,24 +3641,83 @@ Return value: ASSERT(g_iQKDeltas[u] != 0); do { + // Ray-invariant: every square on this ray shares the same + // rank/file relationship to c as the very first step does, so + // this only needs computing once per direction, not per square + // (or even per friend-slider hit). + FLAG fOrthogonalRay; + uBit = QUEEN_BIT; cSquare = c + g_iQKDeltas[u]; + fOrthogonalRay = (((cSquare & 0xF0) == (c & 0xF0)) || + ((cSquare & 0x0F) == (c & 0x0F))); while(IS_ON_BOARD(cSquare)) { + FLAG fStop; + // // Toggle attack table bits. // pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing |= uBit; // - // What did we hit? + // What did we hit? Dispatched via switch instead of an + // indirect call through a function pointer -- same + // rationale as the bishop ray-walk above. // p = pos->rgSquare[cSquare].pPiece; - pFun = QMobJumpTable[uColor][p]; - if (TRUE == (*pFun)(pos, - cSquare, - &uTotalMobility, - &uBit)) + switch (QMobCaseTable[uColor][p]) + { + case QMOB_EMPTY: + uTotalMobility += + !UNSAFE_FOR_QUEEN(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]); + fStop = FALSE; + break; + + case QMOB_ENEMY_LESS: + uTotalMobility += + !UNSAFE_FOR_QUEEN(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]); + fStop = TRUE; + break; + + case QMOB_FRIEND_BLOCK: + fStop = TRUE; + break; + + case QMOB_FRIEND_BISHOP: + // Bishop only xrays a diagonal ray; blocks on an + // orthogonal one. + uBit = QUEEN_XRAY_BIT; + fStop = fOrthogonalRay; + break; + + case QMOB_FRIEND_ROOK: + // Rook only xrays an orthogonal ray; blocks on a + // diagonal one. + uBit = QUEEN_XRAY_BIT; + fStop = !fOrthogonalRay; + break; + + case QMOB_FRIEND_QUEEN: + uBit = QUEEN_XRAY_BIT; + fStop = FALSE; + break; + + case QMOB_ENEMY_GE: + uTotalMobility += 1; + fStop = TRUE; + break; + + case QMOB_INVALID: + default: + UtilPanic(SHOULD_NOT_GET_HERE, + NULL, NULL, NULL, NULL, + __FILE__, __LINE__); + fStop = TRUE; + break; + } + ASSERT(uTotalMobility <= 27); + if (TRUE == fStop) { break; } @@ -4565,10 +3743,13 @@ Return value: { _RecordTrappedCandidate(pos, uColor, c); } +#if 0 + // This is never used right now ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0); ASSERT((uTotalMobility & 0x80000000) == 0); pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor], uTotalMobility); +#endif // // Removed 2026-08-30 ("pointing near enemy K" / @@ -5626,7 +4807,10 @@ Return value: pos->iMaterialBalance[BLACK]); // ASSERT(!InCheck(pos, pos->uToMove)); +#if 0 + // This is never used right now. pos->uMinMobility[BLACK] = pos->uMinMobility[WHITE] = 100; +#endif pos->uNumTrapped[BLACK] = pos->uNumTrapped[WHITE] = 0; pos->iScore[BLACK] = @@ -6016,6 +5200,9 @@ Return value: #endif } +#if 0 + // + // Never used right now. // // Make one more pass over the piece list for the side on move now // that the full attack table is computed to detect @@ -6024,6 +5211,7 @@ Return value: // ctx->sPlyInfo[ctx->uPly].uMinMobility[BLACK] = pos->uMinMobility[BLACK]; ctx->sPlyInfo[ctx->uPly].uMinMobility[WHITE] = pos->uMinMobility[WHITE]; +#endif // // _EvalLookForDanger/_EvalTrappedPieces only ever *add* a hint @@ -6161,7 +5349,11 @@ Return value: (iScoreForSideToMove < +NMATE)); ASSERT(abs(ctx->sPlyInfo[ctx->uPly].iKingScore[BLACK]) < 700); ASSERT(abs(ctx->sPlyInfo[ctx->uPly].iKingScore[WHITE]) < 700); + +#if 0 + // Never used. ASSERT(ctx->sPlyInfo[ctx->uPly].uMinMobility[BLACK] <= 100); ASSERT(ctx->sPlyInfo[ctx->uPly].uMinMobility[WHITE] <= 100); +#endif return(iScoreForSideToMove); } -- cgit v1.3