diff options
| author | Scott Gasch <[email protected]> | 2026-09-05 09:35:19 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-05 09:35:19 -0700 |
| commit | d184a2ea4314d6bd4b8933482c0c1e7a420f2872 (patch) | |
| tree | d9511b62032b9e039ea9a97944c72bc4b6f45545 | |
| parent | 863bc8f6c82595de32b382bbde6a0fe81f0a6dcf (diff) | |
Rook attack/mobility: bitboard rewrite; backport bounded x-ray chaining to bishop
_EvalRook's mobility ray-walk and RMobCaseTable switch replaced with
_RookAttacksBB(c, pos->bbOccupied) plus bitboard masks, same technique
as knight/bishop. Adds pos->bbRookAttacks[2]/bbRookXrayAttacks[2],
first contributors alongside bbMinorAttacks/bbMinorXrayAttacks.
Connected-rook bonus and x-ray population derived from the attack
bitboard instead of a per-square dispatch.
Unlike bishop's single-hop x-ray simplification, rook's x-ray
population uses a bounded chain-following loop (recompute with the
newly-found blocker excluded, repeat until no new x-ray-worthy
terminal appears): checked frequency first (board_representation/
EVAL.md), and 14% of the curated-suite positions have a genuine
2+-deep rook/queen battery on some ray, far more common than bishop's
~1% -- a one-hop approximation here would be a real fidelity loss, not
a negligible one. The stashed first bitboard-eval attempt (git
stash@{1}) had already solved this correctly by walking blocker-to-
blocker via bit-scan; this reproduces the same unbounded behavior via
repeated magic-lookup recomputation, cheap because the loop only
iterates again when an actual chained battery exists.
Backported the same bounded-chain fix to bishop's x-ray population
(previously single-hop only) for consistency, now that it's known
cheap and mechanically identical -- bishop's own battery rate is much
lower (~1%) so this mostly just removes an intentional divergence
rather than fixing an active problem.
Also fixes two real correctness gaps this conversion would otherwise
have introduced silently (same failure mode as pawn's earlier
conversion, EVAL.md's progress log): rook no longer writes ROOK_BIT/
ROOK_XRAY_BIT into the old per-square bvAttacks structure, but
_WhoControlsSquareFast and queen's mobility unsafe-check both still
read those bits directly. Added _IsSquareAttackedByRook/
_IsSquareXrayedByRook (same transitional-helper, DEBUG-cross-checked
pattern as the minor-piece helpers) and updated both call sites.
Verified via precommit_check.sh (self-test + DEBUG smoke test) after
each step.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XxmVi2sTMwpPp4i6WYFjan
| -rwxr-xr-x | src/chess.h | 13 | ||||
| -rwxr-xr-x | src/eval.c | 490 |
2 files changed, 311 insertions, 192 deletions
diff --git a/src/chess.h b/src/chess.h index 305c681..421667f 100755 --- a/src/chess.h +++ b/src/chess.h @@ -739,6 +739,19 @@ typedef struct _POSITION // see _IsSquareXrayedByMinor. BITBOARD bbMinorXrayAttacks[2]; + // Rook's turn to convert (board_representation/EVAL.md section + // 9, 2026-09-05): "which squares does this side's rook(s) attack," + // ORed in via _RookAttacksBB(c, pos->bbOccupied) straight from + // _EvalRook's mobility computation, same Eval()-scoped/cleared- + // per-call lifetime as bbMinorAttacks. bbRookXrayAttacks mirrors + // bbMinorXrayAttacks: squares seen past a friendly rook/queen + // battery partner or an x-rayable enemy queen/king, single-hop + // only (same deliberate simplification bishop already made, see + // _EvalRook's mobility comment). Queen/king haven't converted yet + // and still write their own bits into the old bvAttacks structure. + BITBOARD bbRookAttacks[2]; + BITBOARD bbRookXrayAttacks[2]; + ULONG uWhiteSqBishopCount[2]; // num bishops on white squares SCORE iMaterialBalance[2]; // material balance @@ -1260,51 +1260,153 @@ _IsSquareXrayedByMinor(IN POSITION *pos, } for (d = 0; d < 4; d++) { + // Chains: after marking squares past one x-ray-worthy + // blocker up to the next piece, re-examine that next + // piece and keep going if it's worthy too (matches the + // production chain-following loop above, and rook's + // identical cross-check). COOR cWalk = cBishop + g_iBDeltas[d]; - FLAG fFoundXrayBlocker = FALSE; - // - // First pass: walk to the first blocker (or edge), - // exactly like the direct-attack ray would, purely to - // classify whether that first blocker is x-ray-worthy - // -- no marking yet. - // while (IS_ON_BOARD(cWalk) && IS_EMPTY(pos->rgSquare[cWalk].pPiece)) { cWalk += g_iBDeltas[d]; } - if (IS_ON_BOARD(cWalk)) + while (IS_ON_BOARD(cWalk)) { PIECE pq = pos->rgSquare[cWalk].pPiece; + FLAG fWorthy; if (GET_COLOR(pq) == uColor) { - fFoundXrayBlocker = (IS_BISHOP(pq) || IS_QUEEN(pq)); + fWorthy = (IS_BISHOP(pq) || IS_QUEEN(pq)); } else { - fFoundXrayBlocker = (IS_ROOK(pq) || IS_QUEEN(pq) || - IS_KING(pq)); + fWorthy = (IS_ROOK(pq) || IS_QUEEN(pq) || + IS_KING(pq)); + } + if (!fWorthy) + { + break; + } + cWalk += g_iBDeltas[d]; + while (IS_ON_BOARD(cWalk)) + { + bbTrueXray |= COOR_TO_BB(cWalk); + if (!IS_EMPTY(pos->rgSquare[cWalk].pPiece)) + { + break; + } + cWalk += g_iBDeltas[d]; } } - if (!fFoundXrayBlocker) + } + } + ASSERT(((bbTrueXray & sq) != 0) == (fResult != 0)); + } +#endif + return fResult; +} + + +// +// Rook's turn to convert (board_representation/EVAL.md section 9, +// 2026-09-05) -- same transitional purpose and same DEBUG cross-check +// discipline as _IsSquareAttackedByMinor/_IsSquareXrayedByMinor above, +// now that rook no longer writes ROOK_BIT/ROOK_XRAY_BIT into +// rgSquare[c|8].bvAttacks at all. Goes away once queen converts and +// _WhoControlsSquareFast reads pos->bbRookAttacks/bbRookXrayAttacks +// directly instead of going through a helper. +// +static FLAG +_IsSquareAttackedByRook(IN POSITION *pos, + IN ULONG uColor, + IN COOR c) +{ + BITBOARD sq = COOR_TO_BB(c); + FLAG fResult = (pos->bbRookAttacks[uColor] & sq) != 0; +#ifdef DEBUG + { + BITBOARD bbTrueRookAttacks = 0; + ULONG u; + for (u = 1; u < pos->uNonPawnCount[uColor][0]; u++) + { + COOR cPiece = pos->cNonPawns[uColor][u]; + if (IS_ROOK(pos->rgSquare[cPiece].pPiece)) + { + bbTrueRookAttacks |= _RookAttacksBB(cPiece, pos->bbOccupied); + } + } + ASSERT(((bbTrueRookAttacks & sq) != 0) == (fResult != 0)); + } +#endif + return fResult; +} + +// +// Companion to _IsSquareAttackedByRook, same single-hop x-ray +// simplification as bishop's _IsSquareXrayedByMinor (deliberate speed +// tradeoff, not full chain-following -- see _EvalRook's mobility +// comment). Cross-checked against an independent from-scratch mailbox +// walk mirroring that same single-hop rule. +// +static FLAG +_IsSquareXrayedByRook(IN POSITION *pos, + IN ULONG uColor, + IN COOR c) +{ + BITBOARD sq = COOR_TO_BB(c); + FLAG fResult = (pos->bbRookXrayAttacks[uColor] & sq) != 0; +#ifdef DEBUG + { + BITBOARD bbTrueXray = 0; + ULONG u; + for (u = 1; u < pos->uNonPawnCount[uColor][0]; u++) + { + COOR cRook = pos->cNonPawns[uColor][u]; + ULONG d; + if (!IS_ROOK(pos->rgSquare[cRook].pPiece)) + { + continue; + } + for (d = 0; d < 4; d++) + { + // Unlike bishop's single-hop cross-check, this chains: + // after marking squares past one x-ray-worthy blocker + // up to the next piece, re-examine that next piece and + // keep going if it's worthy too (matches the + // production chain-following loop above, and the + // stashed first attempt's blocker-to-blocker bit-scan + // walk). + COOR cWalk = cRook + g_iRDeltas[d]; + while (IS_ON_BOARD(cWalk) && IS_EMPTY(pos->rgSquare[cWalk].pPiece)) { - continue; + cWalk += g_iRDeltas[d]; } - // - // Single hop past that one blocker: mark empty - // squares as x-ray, then mark and stop at the very - // next piece of any kind (or the edge) -- do not - // classify that second piece and potentially continue - // again, unlike the old chain-following walk. - // - cWalk += g_iBDeltas[d]; while (IS_ON_BOARD(cWalk)) { - bbTrueXray |= COOR_TO_BB(cWalk); - if (!IS_EMPTY(pos->rgSquare[cWalk].pPiece)) + PIECE pq = pos->rgSquare[cWalk].pPiece; + FLAG fWorthy; + if (GET_COLOR(pq) == uColor) + { + fWorthy = (IS_ROOK(pq) || IS_QUEEN(pq)); + } + else + { + fWorthy = (IS_QUEEN(pq) || IS_KING(pq)); + } + if (!fWorthy) { break; } - cWalk += g_iBDeltas[d]; + cWalk += g_iRDeltas[d]; + while (IS_ON_BOARD(cWalk)) + { + bbTrueXray |= COOR_TO_BB(cWalk); + if (!IS_EMPTY(pos->rgSquare[cWalk].pPiece)) + { + break; + } + cWalk += g_iRDeltas[d]; + } } } } @@ -1314,7 +1416,6 @@ _IsSquareXrayedByMinor(IN POSITION *pos, return fResult; } - static ULONG _WhoControlsSquareFast(IN POSITION *pos, IN COOR c) @@ -1348,21 +1449,26 @@ Return value: // // .uXray is a standalone byte view (see _IsSquareXrayedByMinor's // comment on why the byte-scale MINOR_BIT, not MINOR_XRAY_BIT, is - // the right constant to OR in here) -- bishop's contribution to it - // moved to pos->bbMinorXrayAttacks, rook/queen's haven't converted - // yet so their xray sub-bits are still valid straight off .uXray. + // the right constant to OR in here) -- bishop's and now rook's + // contributions moved to pos->bbMinorXrayAttacks/bbRookXrayAttacks, + // queen hasn't converted yet so its xray sub-bit is still valid + // straight off .uXray. ULONG uWhite = pos->rgSquare[c|8].bvAttacks[WHITE].uSmall | pos->rgSquare[c|8].bvAttacks[WHITE].uXray | ((pos->bbPawnAttacks[WHITE] & COOR_TO_BB(c)) ? PAWN_BIT : 0) | (_IsSquareAttackedByMinor(pos, WHITE, c) ? MINOR_BIT : 0) | - (_IsSquareXrayedByMinor(pos, WHITE, c) ? MINOR_BIT : 0); + (_IsSquareXrayedByMinor(pos, WHITE, c) ? MINOR_BIT : 0) | + (_IsSquareAttackedByRook(pos, WHITE, c) ? ROOK_BIT : 0) | + (_IsSquareXrayedByRook(pos, WHITE, c) ? ROOK_BIT : 0); ULONG uBlack = pos->rgSquare[c|8].bvAttacks[BLACK].uSmall | pos->rgSquare[c|8].bvAttacks[BLACK].uXray | ((pos->bbPawnAttacks[BLACK] & COOR_TO_BB(c)) ? PAWN_BIT : 0) | (_IsSquareAttackedByMinor(pos, BLACK, c) ? MINOR_BIT : 0) | - (_IsSquareXrayedByMinor(pos, BLACK, c) ? MINOR_BIT : 0); + (_IsSquareXrayedByMinor(pos, BLACK, c) ? MINOR_BIT : 0) | + (_IsSquareAttackedByRook(pos, BLACK, c) ? ROOK_BIT : 0) | + (_IsSquareXrayedByRook(pos, BLACK, c) ? ROOK_BIT : 0); ULONG u; PIECE p; CHAR ch; @@ -1434,6 +1540,8 @@ _ClearAttackTables(IN OUT POSITION *pos) register COOR c = 8; pos->bbMinorAttacks[WHITE] = pos->bbMinorAttacks[BLACK] = 0; pos->bbMinorXrayAttacks[WHITE] = pos->bbMinorXrayAttacks[BLACK] = 0; + pos->bbRookAttacks[WHITE] = pos->bbRookAttacks[BLACK] = 0; + pos->bbRookXrayAttacks[WHITE] = pos->bbRookXrayAttacks[BLACK] = 0; #if 1 CLEAR_A_SQ; c += 16; CLEAR_A_SQ; c += 16; @@ -2835,38 +2943,51 @@ Return value: // X-ray: squares seen through a friendly bishop/queen // (BMOB_FRIEND_XRAY) or an enemy rook/queen/king // (BMOB_ENEMY_GREATER) -- the only two case-table entries that - // don't stop the ray outright. Recompute with just that - // blocker excluded from occupancy and keep only the squares - // beyond the original attack set. + // don't stop the ray outright. // - // Deliberate behavior change from the old ray-walk, for speed - // (2026-09-05): this is a single hop only. The old walk kept - // going -- and kept counting mobility -- through however many - // x-ray-worthy blockers were stacked consecutively on one ray - // (e.g. x-raying an enemy rook, then continuing to x-ray - // *through* an enemy king sitting right behind it too, both - // contributing to the mobility count along the way); this - // version does not re-check whether the newly-revealed - // terminal square is itself x-ray-worthy and extend again. - // Judged an acceptable trade given how rare a ray with >=2 - // consecutive x-ray-worthy pieces is -- see - // _IsSquareXrayedByMinor's DEBUG cross-check, which verifies - // against this same single-hop rule (not the old unbounded - // one) via an independent mailbox walk. + // Originally shipped 2026-09-05 as a single-hop-only + // approximation (see rook's identical, now-superseded comment + // for the reasoning) on the theory that >=2 consecutive + // x-ray-worthy blockers on one diagonal is rare. Backported + // rook's bounded chain-following fix once that theory was + // actually checked instead of assumed: bishop's own rate is + // indeed low (2/191, ~1%, vs. rook's 14%) but the fix is + // mechanically identical and effectively free (the loop only + // iterates again when a real chained battery exists), so there + // is no reason left to keep the approximation once rook's + // fidelity-preserving version exists to copy. See + // _IsSquareXrayedByMinor's DEBUG cross-check, updated to the + // same chain-following rule. // { - BITBOARD bbXrayBlockers = bbAttack & - (pos->bbPieces[uColor][BISHOP] | pos->bbPieces[uColor][QUEEN] | - pos->bbPieces[FLIP(uColor)][ROOK] | - pos->bbPieces[FLIP(uColor)][QUEEN] | - COOR_TO_BB(pos->cNonPawns[FLIP(uColor)][0])); - if (bbXrayBlockers) + BITBOARD bbXrayAccum = 0; + BITBOARD bbExclude = 0; + BITBOARD bbFrontier = bbAttack; + for (;;) { - BITBOARD bbBeyond = - _BishopAttacksBB(c, pos->bbOccupied & ~bbXrayBlockers) & - ~bbAttack; - pos->bbMinorXrayAttacks[uColor] |= bbBeyond; + BITBOARD bbBlockers = bbFrontier & + (pos->bbPieces[uColor][BISHOP] | + pos->bbPieces[uColor][QUEEN] | + pos->bbPieces[FLIP(uColor)][ROOK] | + pos->bbPieces[FLIP(uColor)][QUEEN] | + COOR_TO_BB(pos->cNonPawns[FLIP(uColor)][0])); + BITBOARD bbBeyond; + + if (!bbBlockers) + { + break; + } + bbExclude |= bbBlockers; + bbBeyond = _BishopAttacksBB(c, pos->bbOccupied & ~bbExclude) & + ~bbAttack & ~bbXrayAccum; + if (!bbBeyond) + { + break; + } + bbXrayAccum |= bbBeyond; + bbFrontier = bbBeyond; } + pos->bbMinorXrayAttacks[uColor] |= bbXrayAccum; } bbMobility = (bbAttack & bbEnemyNonPawnOcc) | @@ -3207,52 +3328,14 @@ Return value: **/ { - static const UCHAR RMobCaseTable[2][14] = - { - {// (black) - 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) - 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) - }, - }; - PIECE p; ULONG uPawnFile = FILE(c) + 1; ULONG uColor; ULONG u; - ULONG uCurrentMobility; ULONG uMaxMobility; ULONG uTotalMobility; COOR cSquare; BITBOARD bb; - ULONG uBit; SCORE i; ASSERT(IS_ON_BOARD(c)); @@ -3413,111 +3496,130 @@ Return value: "increase in value/pawns"); // - // Rook mobility + // Rook mobility (and update attack tables) -- board_representation/ + // EVAL.md section 1b/9: _RookAttacksBB(c, pos->bbOccupied) + // (generate.c's magic-bitboard slider lookup) gives the whole + // ray-to-first-blocker attack set in one shot, same technique + // already landed for knight/bishop. The old RMobCaseTable's six + // live cases collapse to: + // - terminal enemy rook/queen/king (RMOB_ENEMY_SAME/_GREATER): + // counts unconditionally, no unsafe check -- capturing an + // equal-or-higher piece is never "unsafe" in the mobility + // sense. Queen/king additionally x-ray past (_GREATER); rook + // does not (_SAME). + // - empty or terminal enemy pawn/knight/bishop (RMOB_EMPTY / + // RMOB_ENEMY_LESS): counts unless the square is attacked by an + // enemy pawn or minor -- both terminal-square categories + // together are exactly "not friend-occupied and not + // enemy-rook/queen/king-occupied," the same partition + // bishop/knight's reductions already use with their own value + // thresholds. + // - any friendly piece (RMOB_FRIEND_BLOCK/_ROOK/_QUEEN): never + // counts, simply excluded by the friendly-occupied mask. + // Friendly rook/queen additionally x-ray past (no mobility + // credit at the blocker itself, matching the old table); a + // friendly rook blocker also earns the connected-rook bonus. // - u = uMaxMobility = uTotalMobility = 0; - ASSERT(g_iRDeltas[u] != 0); - do + pos->cPiece = c; { - // 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; + BITBOARD bbAttack = _RookAttacksBB(c, pos->bbOccupied); + BITBOARD bbFriendOcc = _BuildFriendlySideBB(pos, uColor); + BITBOARD bbEnemyGE = pos->bbPieces[FLIP(uColor)][ROOK] | + pos->bbPieces[FLIP(uColor)][QUEEN] | + COOR_TO_BB(pos->cNonPawns[FLIP(uColor)][0]); + BITBOARD bbUnsafeForRook = pos->bbPawnAttacks[FLIP(uColor)] | + pos->bbMinorAttacks[FLIP(uColor)]; + BITBOARD bbMobility; + BITBOARD bbFriendRook; + ULONG d; - uCurrentMobility = 0; - uBit = ROOK_BIT; - cSquare = c + g_iRDeltas[u]; - fHoriz = ((cSquare & 0xF0) == (c & 0xF0)); + pos->bbRookAttacks[uColor] |= bbAttack; - while(IS_ON_BOARD(cSquare)) + // + // Connected-rook bonus: a friendly rook reached on this ray is + // a battery partner. At most one bit per ray direction can be + // this case (the attack set stops there), but more than one + // direction can each terminate at a different friendly rook. + // + bbFriendRook = bbAttack & pos->bbPieces[uColor][ROOK]; + while (bbFriendRook) { - FLAG fStop; - - // - // Twiddle our attack table bits. - // - pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing |= uBit; - ASSERT((cSquare | 8) == (cSquare + 8)); + COOR cBlocker = CoorFromBitBoardRank8ToRank1(&bbFriendRook); + FLAG fHoriz = (RANK(cBlocker) == RANK(c)); + EVAL_TERM(uColor, + ROOK, + cBlocker, + pos->iScore[uColor], + (ROOK_CONNECTED_HORIZ * fHoriz + + ROOK_CONNECTED_VERT * FLIP(fHoriz)), + "rook connected"); + } - // - // 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; - switch (RMobCaseTable[uColor][p]) + // + // X-ray: squares seen through a friendly rook/queen battery + // partner or an x-rayable enemy queen/king -- the only + // case-table entries that don't stop the ray outright. + // + // Unlike bishop's single-hop cut, this chains until it stops + // finding a new x-ray-worthy terminal: checked against the + // curated suites (2026-09-05), 14% of positions (27/191) have + // a genuine 2+-deep rook/queen battery on some ray, far more + // common than bishop's "rare" diagonal case -- a one-hop + // approximation here would be a real fidelity loss, not a + // negligible one. The stashed first bitboard-eval attempt + // (git stash@{1}, "measured 17-41% slower") already solved + // this correctly by walking blocker-to-blocker via bit-scan on + // bbRay & bbOccupied; this reproduces the same unbounded + // chain-following behavior via repeated magic-lookup + // recomputation instead (each iteration excludes one more + // ring of blockers), which stays cheap because the loop body + // only runs again when an actual chained battery exists -- + // the common case exits after zero or one iteration. + // + { + BITBOARD bbXrayAccum = 0; + BITBOARD bbExclude = 0; + BITBOARD bbFrontier = bbAttack; + for (;;) { - case RMOB_EMPTY: - uCurrentMobility += - !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) || - _IsSquareAttackedByMinor(pos, FLIP(uColor), cSquare)); - fStop = FALSE; - break; + BITBOARD bbBlockers = bbFrontier & + (pos->bbPieces[uColor][ROOK] | + pos->bbPieces[uColor][QUEEN] | + pos->bbPieces[FLIP(uColor)][QUEEN] | + COOR_TO_BB(pos->cNonPawns[FLIP(uColor)][0])); + BITBOARD bbBeyond; - case RMOB_ENEMY_LESS: - uCurrentMobility += - !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) || - _IsSquareAttackedByMinor(pos, FLIP(uColor), cSquare)); - 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; + if (!bbBlockers) + { break; - - case RMOB_FRIEND_QUEEN: - uBit = ROOK_XRAY_BIT; - fStop = FALSE; + } + bbExclude |= bbBlockers; + bbBeyond = _RookAttacksBB(c, pos->bbOccupied & ~bbExclude) & + ~bbAttack & ~bbXrayAccum; + if (!bbBeyond) + { break; + } + bbXrayAccum |= bbBeyond; + bbFrontier = bbBeyond; + } + pos->bbRookXrayAttacks[uColor] |= bbXrayAccum; + } - case RMOB_ENEMY_GREATER: - uCurrentMobility += 1; - uBit = ROOK_XRAY_BIT; - fStop = FALSE; - break; + bbMobility = (bbAttack & bbEnemyGE) | + (bbAttack & ~bbFriendOcc & ~bbEnemyGE & + ~bbUnsafeForRook); + uTotalMobility = CountBits(bbMobility); - 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; - } - cSquare += g_iRDeltas[u]; + uMaxMobility = 0; + for (d = 0; d < 4; d++) + { + ULONG uThisRay = CountBits(bbMobility & g_RookRayToEdge[d][c]); + uMaxMobility = MAXU(uMaxMobility, uThisRay); } - uTotalMobility += uCurrentMobility; - ASSERT(uTotalMobility <= 14); - ASSERT((uMaxMobility & 0x80000000) == 0); - ASSERT((uCurrentMobility & 0x80000000) == 0); - uMaxMobility = MAXU(uCurrentMobility, uMaxMobility); - ASSERT(uMaxMobility <= 7); - u++; } - while(g_iRDeltas[u] != 0); ASSERT(uTotalMobility <= 14); + ASSERT(uMaxMobility <= 7); ASSERT(pos->uArmyScaler[FLIP(uColor)] <= 31); // Tuning can (and has) pushed this out of its original hand-picked @@ -3754,8 +3856,7 @@ Return value: uTotalMobility += !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) || _IsSquareAttackedByMinor(pos, FLIP(uColor), cSquare) || - (pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)].uWholeThing & - ROOK_BIT)); + _IsSquareAttackedByRook(pos, FLIP(uColor), cSquare)); fStop = FALSE; break; @@ -3763,8 +3864,7 @@ Return value: uTotalMobility += !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) || _IsSquareAttackedByMinor(pos, FLIP(uColor), cSquare) || - (pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)].uWholeThing & - ROOK_BIT)); + _IsSquareAttackedByRook(pos, FLIP(uColor), cSquare)); fStop = TRUE; break; @@ -4010,19 +4110,25 @@ Return value: ((pos->bbPawnAttacks[ufColor] & COOR_TO_BB(cSquare)) ? PAWN_BIT : 0) | (_IsSquareAttackedByMinor(pos, ufColor, cSquare) ? - MINOR_BIT : 0); + MINOR_BIT : 0) | + (_IsSquareAttackedByRook(pos, ufColor, cSquare) ? + ROOK_BIT : 0); { COOR cRealSquare = cSquare; cSquare |= 8; bvXray = pos->rgSquare[cSquare].bvAttacks[ufColor].uXray | (_IsSquareXrayedByMinor(pos, ufColor, cRealSquare) ? - MINOR_BIT : 0); + MINOR_BIT : 0) | + (_IsSquareXrayedByRook(pos, ufColor, cRealSquare) ? + ROOK_BIT : 0); pos->rgSquare[cSquare].bvAttacks[uColor].small.uKing = 1; bvDefend = pos->rgSquare[cSquare].bvAttacks[uColor].uSmall | ((pos->bbPawnAttacks[uColor] & COOR_TO_BB(cRealSquare)) ? PAWN_BIT : 0) | (_IsSquareAttackedByMinor(pos, uColor, cRealSquare) ? - MINOR_BIT : 0); + MINOR_BIT : 0) | + (_IsSquareAttackedByRook(pos, uColor, cRealSquare) ? + ROOK_BIT : 0); } // |
