From d184a2ea4314d6bd4b8933482c0c1e7a420f2872 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Sat, 5 Sep 2026 09:35:19 -0700 Subject: 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 Claude-Session: https://claude.ai/code/session_01XxmVi2sTMwpPp4i6WYFjan --- src/eval.c | 492 +++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 299 insertions(+), 193 deletions(-) (limited to 'src/eval.c') diff --git a/src/eval.c b/src/eval.c index bb19d9d..9eafc1a 100755 --- a/src/eval.c +++ b/src/eval.c @@ -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; - - 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; + 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; + + 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); } // -- cgit v1.3