summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-05 02:17:20 -0700
committerScott Gasch <[email protected]>2026-09-05 02:17:20 -0700
commitde3f3663483f846d369efa5234a84c7da385be95 (patch)
tree71af0527a47c0996b0116351dee0d68409db2fad /src
parent7e3e6b69969cf6657f518102be515702da6a7343 (diff)
Bishop attack/mobility: bitboard rewrite, deliberate x-ray simplification
_EvalBishop's mobility ray-walk (per-square g_iBDeltas delta-walk + BMobCaseTable switch dispatch) replaced with _BishopAttacksBB(c, pos->bbOccupied) -- generate.c's magic-bitboard slider lookup, already used by move generation -- plus bitboard masks for the mobility count, matching knight's reduction pattern: enemy-non-pawn-terminal counts unconditionally, empty-or-enemy-pawn- terminal counts unless pawn-unsafe (via bbPawnAttacks), and the one case that isn't a pure occupancy mask -- a terminal friendly, non-stationary pawn on this bishop's own color complex -- keeps its credit via bbPc (already computed for the existing good/bad transient-pawn scoring, untouched this change). Adds POSITION::bbMinorAttacks contributions from bishop (direct attack bits) and POSITION::bbMinorXrayAttacks (new field) for squares seen through a friendly bishop/queen battery partner or an x-rayable enemy rook/queen/king. Deliberate behavior change from the old ray-walk, made for speed per direct instruction: the old walk's fStop=FALSE for the x-ray cases meant it 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). The bitboard version only extends one hop past the first x-ray-worthy blocker; it does not re-check whether the newly-revealed terminal square is itself x-ray-worthy and extend again. This was found and deliberately kept (not fixed) after a DEBUG assert caught the exact case on 8/1R1B4/2B1r3/5k2/2P2P2/1p6/1Kb5/7n w - - during precommit's random- sample smoke test -- judged an acceptable trade given how rare a ray with >=2 consecutive x-ray-worthy pieces is. Two new transitional helpers (_IsSquareAttackedByMinor's bishop contribution, and new _IsSquareXrayedByMinor) carry bishop's combined state to every remaining consumer (rook/queen mobility-safety checks, king's danger computation, _WhoControlsSquareFast) -- both DEBUG- asserted against an independent from-scratch mailbox ray-walk (using the still-live rgSquare representation, not any shared code with the production bitboard technique) implementing this same single-hop rule, so a real regression fails loudly rather than drifting into a wrong score. Verified via precommit_check.sh and the full tests/ecm_ringers.ep_ suite at sd10 against the prior commit (7e3e6b6): same 10/11 solved (same single miss, ECM.335, in both), node counts now legitimately differ per position (expected given the semantic change) but stay in a bounded, reasonable range (-17% to +20%), nothing resembling the 60%+ blowups a real bug produced earlier in this same session before being caught, isolated, and traced to this exact x-ray-chain gap. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01P6g6iF6mD1Hau6nCZCzwYj
Diffstat (limited to 'src')
-rwxr-xr-xsrc/chess.h32
-rwxr-xr-xsrc/eval.c336
2 files changed, 226 insertions, 142 deletions
diff --git a/src/chess.h b/src/chess.h
index 2e9189a..305c681 100755
--- a/src/chess.h
+++ b/src/chess.h
@@ -716,19 +716,29 @@ typedef struct _POSITION
// bbRookAttacks/bbQueenAttacks that will retire the rest of it.
BITBOARD bbPawnAttacks[2];
- // First mover of the bbMinorAttacks pair (knight only for now --
- // bishop is a separate, later step; see board_representation/
- // EVAL.md section 2). "Which squares does this side's knight(s)
- // attack" -- computed via g_KnightAttacksBB[c] and OR'd in per
- // knight, Eval()-scoped scratch like bbPawnAttacks above, cleared
- // once per Eval() call in _ClearAttackTables since (unlike
- // bbPawnAttacks) it's accumulated across multiple pieces via |=,
- // not assigned wholesale. Bishop still writes its own minor-bit
- // contribution into the old rgSquare[c|8].bvAttacks structure for
- // now -- every consumer that needs "any minor's attack," not just
- // knight's, ORs both sources together until bishop converts too.
+ // "Which squares does this side's knight(s) or bishop(s) attack" --
+ // knight ORs in g_KnightAttacksBB[c] directly (no per-square
+ // bit-scan needed); bishop ORs in _BishopAttacksBB(c,
+ // pos->bbOccupied), generate.c's magic-bitboard slider lookup.
+ // Eval()-scoped scratch like bbPawnAttacks above, cleared once per
+ // Eval() call in _ClearAttackTables since (unlike bbPawnAttacks)
+ // it's accumulated across multiple pieces via |=, not assigned
+ // wholesale. Rook/queen/king are not converted yet and still write
+ // their own bits into the old rgSquare[c|8].bvAttacks structure --
+ // see _IsSquareAttackedByMinor (eval.c), the transitional helper
+ // every consumer goes through until they convert too.
BITBOARD bbMinorAttacks[2];
+ // Squares seen *through* a friendly bishop/queen battery partner
+ // or an x-rayable enemy rook/queen/king, for bishops only (knights
+ // never x-ray) -- see _EvalBishop's mobility loop. Same lifetime/
+ // clearing discipline as bbMinorAttacks above. Nothing else writes
+ // this yet (rook/queen have their own xray bits still living in
+ // the old bvAttacks structure), so unlike bbMinorAttacks' direct-
+ // bit case there's no old-structure fallback to combine with --
+ // see _IsSquareXrayedByMinor.
+ BITBOARD bbMinorXrayAttacks[2];
+
ULONG uWhiteSqBishopCount[2]; // num bishops on white squares
SCORE iMaterialBalance[2]; // material balance
diff --git a/src/eval.c b/src/eval.c
index 10a1327..bb19d9d 100755
--- a/src/eval.c
+++ b/src/eval.c
@@ -1204,6 +1204,116 @@ _IsSquareAttackedByMinor(IN POSITION *pos,
return fResult;
}
+//
+// Companion to _IsSquareAttackedByMinor, same transitional purpose:
+// bishop is the only minor that ever x-rays (knights don't), and its
+// x-ray bit is moving fully to pos->bbMinorXrayAttacks in this same
+// change -- unlike the direct-attack case, there's no old-structure
+// fallback to bridge here (nothing else has ever written a minor
+// x-ray bit), so this is just a plain bitboard read. Goes away once
+// rook/queen convert and _EvalKing/_WhoControlsSquareFast read their
+// xray bitboards directly instead of going through any helper.
+//
+// Deliberate behavior change from the old ray-walk, made for speed
+// per direct instruction (2026-09-05): the old BMobCaseTable's
+// fStop=FALSE for BMOB_FRIEND_XRAY/BMOB_ENEMY_GREATER meant the walk
+// kept going -- and kept counting mobility -- through however many
+// x-ray-worthy blockers were stacked consecutively on one ray (e.g. a
+// bishop x-raying an enemy rook, then continuing to x-ray *through*
+// an enemy king sitting right behind it too). _EvalBishop's bitboard
+// version only extends *one* hop past the first x-ray-worthy blocker
+// (recompute with just that blocker excluded, keep what's newly
+// revealed) -- it does not check whether the newly-revealed terminal
+// square is itself x-ray-worthy and continue again. This is simpler
+// and faster, and the position it changes behavior on (>=2 specific
+// piece types stacked on the same diagonal) is rare enough that
+// trading exact fidelity for it was judged worthwhile; if that
+// judgment turns out wrong, the fix is a bounded loop repeating the
+// "exclude terminal blocker, recompute" step until it stops finding a
+// new x-ray-worthy terminal, not a design change.
+//
+// DEBUG-only: cross-checked against a from-scratch mailbox ray-walk
+// that mirrors this same single-hop rule directly (not the old,
+// unbounded-chain rule) -- deliberately not sharing any code with
+// _EvalBishop's own "recompute with blocker excluded" bitboard
+// technique, so this is a genuinely independent check on that
+// technique, not a restatement of it.
+//
+static FLAG
+_IsSquareXrayedByMinor(IN POSITION *pos,
+ IN ULONG uColor,
+ IN COOR c)
+{
+ BITBOARD sq = COOR_TO_BB(c);
+ FLAG fResult = (pos->bbMinorXrayAttacks[uColor] & sq) != 0;
+#ifdef DEBUG
+ {
+ BITBOARD bbTrueXray = 0;
+ ULONG u;
+ for (u = 1; u < pos->uNonPawnCount[uColor][0]; u++)
+ {
+ COOR cBishop = pos->cNonPawns[uColor][u];
+ ULONG d;
+ if (!IS_BISHOP(pos->rgSquare[cBishop].pPiece))
+ {
+ continue;
+ }
+ for (d = 0; d < 4; d++)
+ {
+ 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))
+ {
+ PIECE pq = pos->rgSquare[cWalk].pPiece;
+ if (GET_COLOR(pq) == uColor)
+ {
+ fFoundXrayBlocker = (IS_BISHOP(pq) || IS_QUEEN(pq));
+ }
+ else
+ {
+ fFoundXrayBlocker = (IS_ROOK(pq) || IS_QUEEN(pq) ||
+ IS_KING(pq));
+ }
+ }
+ if (!fFoundXrayBlocker)
+ {
+ continue;
+ }
+ //
+ // 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))
+ {
+ break;
+ }
+ cWalk += g_iBDeltas[d];
+ }
+ }
+ }
+ ASSERT(((bbTrueXray & sq) != 0) == (fResult != 0));
+ }
+#endif
+ return fResult;
+}
+
static ULONG
_WhoControlsSquareFast(IN POSITION *pos,
@@ -1236,16 +1346,23 @@ Return value:
// position (PAWN_BIT) so g_SwapTable's indexing below sees the
// same bit shape it always has.
//
+ // .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.
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);
+ (_IsSquareAttackedByMinor(pos, WHITE, c) ? MINOR_BIT : 0) |
+ (_IsSquareXrayedByMinor(pos, WHITE, c) ? MINOR_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);
+ (_IsSquareAttackedByMinor(pos, BLACK, c) ? MINOR_BIT : 0) |
+ (_IsSquareXrayedByMinor(pos, BLACK, c) ? MINOR_BIT : 0);
ULONG u;
PIECE p;
CHAR ch;
@@ -1316,6 +1433,7 @@ _ClearAttackTables(IN OUT POSITION *pos)
{
register COOR c = 8;
pos->bbMinorAttacks[WHITE] = pos->bbMinorAttacks[BLACK] = 0;
+ pos->bbMinorXrayAttacks[WHITE] = pos->bbMinorXrayAttacks[BLACK] = 0;
#if 1
CLEAR_A_SQ; c += 16;
CLEAR_A_SQ; c += 16;
@@ -2566,50 +2684,6 @@ Return value:
{
static const BITBOARD bbColorSq[2] = { 0x55aa55aa55aa55aaULL,
0xaa55aa55aa55aa55ULL };
- //
- // 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)
- 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)
- 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] =
{
{ C8, F8 }, // BLACK
@@ -2624,8 +2698,6 @@ Return value:
ULONG u;
ULONG uTotalMobility;
ULONG uMaxMobility;
- ULONG uCurrentMobility;
- ULONG uBit;
PIECE p;
ASSERT(IS_ON_BOARD(c));
@@ -2719,99 +2791,99 @@ Return value:
//
//
- // Bishop mobility (and update attack table bits)
+ // Bishop mobility (and update attack tables) -- board_representation/
+ // EVAL.md section 1b: _BishopAttacksBB(c, pos->bbOccupied)
+ // (generate.c's magic-bitboard slider lookup, already used by move
+ // generation) gives the whole ray-to-first-blocker attack set in
+ // one shot -- no per-square delta walk, no mailbox read, no switch
+ // dispatch. The old BMobCaseTable's seven cases collapse to:
+ // - terminal enemy non-pawn (opposing minor/rook/queen/king):
+ // counts unconditionally -- covers both BMOB_ENEMY_SAME (no
+ // x-ray) and BMOB_ENEMY_GREATER (x-rays past too); the
+ // x-ray/no-x-ray distinction only matters for attack-bit
+ // population below, not the mobility count itself.
+ // - empty or terminal enemy pawn: counts unless pawn-unsafe
+ // (BMOB_EMPTY / BMOB_ENEMY_PAWN) -- both terminal-square
+ // categories together are exactly "not friend-occupied and not
+ // enemy-non-pawn-occupied," the same partition knight's
+ // reduction already uses.
+ // - terminal friendly, non-stationary pawn on this bishop's own
+ // color complex (BMOB_FRIEND_PAWN): still counts, the one case
+ // that isn't a pure occupancy mask -- bbPc (already computed
+ // above for the good/bad transient-pawn scoring) is exactly
+ // "non-stationary pawns of either color on this color complex";
+ // ANDing with pos->bbPawns[uColor] and the attack set isolates
+ // just this bishop's own transient pawns.
+ // - any other friendly piece (knight/rook/king): blocks, no
+ // count, no x-ray -- simply excluded by the friendly-occupied
+ // mask, nothing else needed.
//
- u = uMaxMobility = uTotalMobility = 0;
pos->bb = bbPc;
- ASSERT(g_iBDeltas[u] != 0);
- do
{
- uCurrentMobility = 0;
- uBit = MINOR_BIT;
- cSquare = c + g_iBDeltas[u];
-
- while(IS_ON_BOARD(cSquare))
- {
- FLAG fStop;
+ BITBOARD bbAttack = _BishopAttacksBB(c, pos->bbOccupied);
+ BITBOARD bbFriendOcc = _BuildFriendlySideBB(pos, uColor);
+ BITBOARD bbEnemyNonPawnOcc = _BuildFriendlySideBB(pos, FLIP(uColor)) &
+ ~pos->bbPawns[FLIP(uColor)];
+ BITBOARD bbUnsafeForMinor = pos->bbPawnAttacks[FLIP(uColor)];
+ BITBOARD bbTransientCredit = bbAttack & pos->bbPawns[uColor] & bbPc;
+ BITBOARD bbMobility;
+ ULONG d;
- //
- // Always toggle attack table bits.
- //
- ASSERT((cSquare|8) == (cSquare + 8));
- pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing |= uBit;
+ pos->bbMinorAttacks[uColor] |= bbAttack;
- //
- // 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;
- switch (BMobCaseTable[uColor][p])
+ //
+ // 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.
+ //
+ // 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.
+ //
+ {
+ 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)
{
- case BMOB_EMPTY:
- uCurrentMobility +=
- !(pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare));
- fStop = FALSE;
- break;
-
- case BMOB_ENEMY_PAWN:
- uCurrentMobility +=
- !(pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare));
- 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;
+ BITBOARD bbBeyond =
+ _BishopAttacksBB(c, pos->bbOccupied & ~bbXrayBlockers) &
+ ~bbAttack;
+ pos->bbMinorXrayAttacks[uColor] |= bbBeyond;
+ }
+ }
- case BMOB_ENEMY_GREATER:
- uCurrentMobility += 1;
- uBit = MINOR_XRAY_BIT;
- fStop = FALSE;
- break;
+ bbMobility = (bbAttack & bbEnemyNonPawnOcc) |
+ (bbAttack & ~bbFriendOcc & ~bbEnemyNonPawnOcc &
+ ~bbUnsafeForMinor) |
+ bbTransientCredit;
+ uTotalMobility = CountBits(bbMobility);
- 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;
- }
- cSquare += g_iBDeltas[u];
+ uMaxMobility = 0;
+ for (d = 0; d < 4; d++)
+ {
+ ULONG uThisRay = CountBits(bbMobility & g_BishopRayToEdge[d][c]);
+ uMaxMobility = MAXU(uMaxMobility, uThisRay);
}
- uTotalMobility += uCurrentMobility;
- ASSERT((uMaxMobility & 0x80000000) == 0);
- ASSERT((uCurrentMobility & 0x80000000) == 0);
- uMaxMobility = MAXU(uMaxMobility, uCurrentMobility);
- ASSERT(uMaxMobility <= 7);
- u++;
}
- while(g_iBDeltas[u] != 0);
ASSERT(uTotalMobility <= 13);
+ ASSERT(uMaxMobility <= 7);
EVAL_TERM(uColor,
BISHOP,
c,
@@ -3942,7 +4014,9 @@ Return value:
{
COOR cRealSquare = cSquare;
cSquare |= 8;
- bvXray = pos->rgSquare[cSquare].bvAttacks[ufColor].uXray;
+ bvXray = pos->rgSquare[cSquare].bvAttacks[ufColor].uXray |
+ (_IsSquareXrayedByMinor(pos, ufColor, cRealSquare) ?
+ MINOR_BIT : 0);
pos->rgSquare[cSquare].bvAttacks[uColor].small.uKing = 1;
bvDefend = pos->rgSquare[cSquare].bvAttacks[uColor].uSmall |
((pos->bbPawnAttacks[uColor] & COOR_TO_BB(cRealSquare)) ?