summaryrefslogtreecommitdiff
path: root/src/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval.c')
-rwxr-xr-xsrc/eval.c742
1 files changed, 135 insertions, 607 deletions
diff --git a/src/eval.c b/src/eval.c
index 6adf27b..1ca2beb 100755
--- a/src/eval.c
+++ b/src/eval.c
@@ -1152,403 +1152,6 @@ Return value:
}
-//
-// Transitional helper, board_representation/EVAL.md section 2: during
-// the piece-by-piece migration off bvAttacks, knight now writes its
-// attacks into pos->bbMinorAttacks directly while bishop (not
-// converted yet) still writes the old per-square
-// rgSquare[c|8].bvAttacks[color].small.uMinor bit. Every consumer
-// that needs "does any minor (knight or bishop) attack this square"
-// goes through this one function instead of each hand-rolling its own
-// OR of the two sources -- once bishop converts too, this collapses
-// to a plain pos->bbMinorAttacks[color] read and this whole function
-// goes away; it is not meant to be a permanent fixture.
-//
-// DEBUG-only: cross-checked against an independent recomputation
-// (g_KnightAttacksBB / _BishopAttacksBB, both already-trusted
-// primitives used elsewhere in move generation, entirely separate
-// code from either the ray-walk's uBit bookkeeping or the new
-// bitboard population) so a bug in either mechanism fails loudly here
-// instead of silently drifting into a wrong score several plies deep
-// in search -- exactly the failure mode a prior, larger version of
-// this same migration hit and had to be rolled back for.
-//
-static FLAG
-_IsSquareAttackedByMinor(IN POSITION *pos,
- IN ULONG uColor,
- IN COOR c)
-{
- BITBOARD sq = COOR_TO_BB(c);
- FLAG fResult = ((pos->bbMinorAttacks[uColor] & sq) != 0) ||
- (pos->rgSquare[c|8].bvAttacks[uColor].small.uMinor != 0);
-#ifdef DEBUG
- {
- BITBOARD bbTrueMinorAttacks = 0;
- ULONG u;
- for (u = 1; u < pos->uNonPawnCount[uColor][0]; u++)
- {
- COOR cPiece = pos->cNonPawns[uColor][u];
- PIECE p = pos->rgSquare[cPiece].pPiece;
- if (IS_KNIGHT(p))
- {
- bbTrueMinorAttacks |= g_KnightAttacksBB[cPiece];
- }
- else if (IS_BISHOP(p))
- {
- bbTrueMinorAttacks |= _BishopAttacksBB(cPiece, pos->bbOccupied);
- }
- }
- ASSERT(((bbTrueMinorAttacks & sq) != 0) == (fResult != 0));
- }
-#endif
- 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++)
- {
- // 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];
- while (IS_ON_BOARD(cWalk) && IS_EMPTY(pos->rgSquare[cWalk].pPiece))
- {
- cWalk += g_iBDeltas[d];
- }
- while (IS_ON_BOARD(cWalk))
- {
- PIECE pq = pos->rgSquare[cWalk].pPiece;
- FLAG fWorthy;
- if (GET_COLOR(pq) == uColor)
- {
- fWorthy = (IS_BISHOP(pq) || IS_QUEEN(pq));
- }
- else
- {
- 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];
- }
- }
- }
- }
- 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))
- {
- cWalk += g_iRDeltas[d];
- }
- while (IS_ON_BOARD(cWalk))
- {
- 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_iRDeltas[d];
- while (IS_ON_BOARD(cWalk))
- {
- bbTrueXray |= COOR_TO_BB(cWalk);
- if (!IS_EMPTY(pos->rgSquare[cWalk].pPiece))
- {
- break;
- }
- cWalk += g_iRDeltas[d];
- }
- }
- }
- }
- ASSERT(((bbTrueXray & sq) != 0) == (fResult != 0));
- }
-#endif
- return fResult;
-}
-
-//
-// Queen's turn to convert (board_representation/EVAL.md section 9,
-// 2026-09-05) -- same transitional purpose and same DEBUG cross-check
-// discipline as _IsSquareAttackedByRook/_IsSquareXrayedByRook above,
-// now that queen no longer writes QUEEN_BIT/QUEEN_XRAY_BIT into
-// rgSquare[c|8].bvAttacks at all. Goes away once king converts and
-// _EvalKing reads pos->bbQueenAttacks/bbQueenXrayAttacks directly.
-//
-static FLAG
-_IsSquareAttackedByQueen(IN POSITION *pos,
- IN ULONG uColor,
- IN COOR c)
-{
- BITBOARD sq = COOR_TO_BB(c);
- FLAG fResult = (pos->bbQueenAttacks[uColor] & sq) != 0;
-#ifdef DEBUG
- {
- BITBOARD bbTrueQueenAttacks = 0;
- ULONG u;
- for (u = 1; u < pos->uNonPawnCount[uColor][0]; u++)
- {
- COOR cPiece = pos->cNonPawns[uColor][u];
- if (IS_QUEEN(pos->rgSquare[cPiece].pPiece))
- {
- bbTrueQueenAttacks |= _RookAttacksBB(cPiece, pos->bbOccupied) |
- _BishopAttacksBB(cPiece, pos->bbOccupied);
- }
- }
- ASSERT(((bbTrueQueenAttacks & sq) != 0) == (fResult != 0));
- }
-#endif
- return fResult;
-}
-
-//
-// Companion to _IsSquareAttackedByQueen. Unlike rook/bishop, queen's
-// own case table never x-rays through *any* enemy piece (QMOB_ENEMY_GE
-// always stops) -- only through a friendly queen, or a friendly rook
-// on an orthogonal ray / friendly bishop on a diagonal ray (the same
-// per-ray-family split _EvalQueen's own mobility walk uses). Chains
-// arbitrarily deep, same as rook's own x-ray (not single-hop).
-//
-static FLAG
-_IsSquareXrayedByQueen(IN POSITION *pos,
- IN ULONG uColor,
- IN COOR c)
-{
- BITBOARD sq = COOR_TO_BB(c);
- FLAG fResult = (pos->bbQueenXrayAttacks[uColor] & sq) != 0;
-#ifdef DEBUG
- {
- BITBOARD bbTrueXray = 0;
- ULONG u;
- for (u = 1; u < pos->uNonPawnCount[uColor][0]; u++)
- {
- COOR cQueen = pos->cNonPawns[uColor][u];
- ULONG d;
- if (!IS_QUEEN(pos->rgSquare[cQueen].pPiece))
- {
- continue;
- }
- for (d = 0; d < 8; d++)
- {
- COOR cWalk = cQueen + g_iQKDeltas[d];
- FLAG fOrthogonal;
-
- if (!IS_ON_BOARD(cWalk))
- {
- continue;
- }
- fOrthogonal = (((cWalk & 0xF0) == (cQueen & 0xF0)) ||
- ((cWalk & 0x0F) == (cQueen & 0x0F)));
- while (IS_ON_BOARD(cWalk) && IS_EMPTY(pos->rgSquare[cWalk].pPiece))
- {
- cWalk += g_iQKDeltas[d];
- }
- while (IS_ON_BOARD(cWalk))
- {
- PIECE pq = pos->rgSquare[cWalk].pPiece;
- FLAG fWorthy;
-
- if (GET_COLOR(pq) != uColor)
- {
- break;
- }
- fWorthy = IS_QUEEN(pq) ||
- (fOrthogonal ? IS_ROOK(pq) : IS_BISHOP(pq));
- if (!fWorthy)
- {
- break;
- }
- cWalk += g_iQKDeltas[d];
- while (IS_ON_BOARD(cWalk))
- {
- bbTrueXray |= COOR_TO_BB(cWalk);
- if (!IS_EMPTY(pos->rgSquare[cWalk].pPiece))
- {
- break;
- }
- cWalk += g_iQKDeltas[d];
- }
- }
- }
- }
- ASSERT(((bbTrueXray & sq) != 0) == (fResult != 0));
- }
-#endif
- return fResult;
-}
-
-//
-// King's turn to convert (board_representation/EVAL.md section 9,
-// 2026-09-05) -- the last piece type contributing to bvAttacks. Same
-// transitional-helper purpose as the others, but simpler: no mobility
-// computation, no x-ray (a king can't move through a blocker), so
-// there's no companion _IsSquareXrayedByKing.
-//
-static FLAG
-_IsSquareAttackedByKing(IN POSITION *pos,
- IN ULONG uColor,
- IN COOR c)
-{
- BITBOARD sq = COOR_TO_BB(c);
- FLAG fResult = (pos->bbKingAttacks[uColor] & sq) != 0;
-#ifdef DEBUG
- {
- BITBOARD bbTrueKingAttacks = g_KingAttacksBB[pos->cNonPawns[uColor][0]];
- ASSERT(((bbTrueKingAttacks & sq) != 0) == (fResult != 0));
- }
-#endif
- return fResult;
-}
-
static ULONG
_WhoControlsSquareFast(IN POSITION *pos,
IN COOR c)
@@ -1574,55 +1177,46 @@ Return value:
**/
{
//
- // bvAttacks no longer carries the pawn bit (pawns write
- // pos->bbPawnAttacks[2] directly instead, see
- // _PopulatePawnAttackBits) -- OR it back in at its usual bit
- // position (PAWN_BIT) so g_SwapTable's indexing below sees the
- // same bit shape it always has.
+ // board_representation/EVAL.md section 9: bvAttacks/ATTACK_BITV
+ // retired entirely now that king (the last piece to write it) has
+ // converted -- every side's presence at this square is read
+ // straight off its own bbXAttacks accumulator instead. Bit
+ // positions (PAWN_BIT/MINOR_BIT/ROOK_BIT/QUEEN_BIT/KING_BIT) are
+ // unchanged so g_SwapTable's indexing below still sees the same
+ // shape it always has; x-ray-only presence (bbMinorXrayAttacks/
+ // bbRookXrayAttacks/bbQueenXrayAttacks) ORs into the same
+ // byte-scale bit as its direct-attack counterpart, matching the
+ // old struct's .uSmall/.uXray sharing one bit position for
+ // "attacks or x-rays" (see g_SwapTable's own construction). King
+ // has no x-ray (can't move through a blocker). This function is
+ // only ever called after *both* kings finish evaluating (the
+ // passed-pawn re-check and trapped-piece/danger passes all run
+ // after Eval()'s king-eval block), so pos->bbKingAttacks is always
+ // fully populated for both colors here -- unlike _EvalKing's own,
+ // deliberately asymmetric internal bvAttack (see that function's
+ // comment), this is a plain, symmetric fact query.
//
- // .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, rook's, and
- // queen's xray contributions all moved to pos->bbMinorXrayAttacks/
- // bbRookXrayAttacks/bbQueenXrayAttacks. King has no x-ray (can't
- // move through a blocker), and its direct-attack contribution is
- // added symmetrically for both colors below via
- // _IsSquareAttackedByKing -- unlike _EvalKing's own internal
- // bvAttack (which deliberately drops the *enemy* king's
- // contribution, see that function's comment on the black-then-
- // white evaluation-order asymmetry this sidesteps), this function
- // is only ever called after *both* kings have finished evaluating
- // (the passed-pawn re-check runs after the king-eval block in
- // Eval()'s own sequencing), so pos->bbKingAttacks is always fully
- // populated for both colors by the time this runs -- no asymmetry
- // to worry about here, this is a plain, symmetric fact query.
- 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) |
- (_IsSquareAttackedByRook(pos, WHITE, c) ? ROOK_BIT : 0) |
- (_IsSquareXrayedByRook(pos, WHITE, c) ? ROOK_BIT : 0) |
- (_IsSquareAttackedByQueen(pos, WHITE, c) ? QUEEN_BIT : 0) |
- (_IsSquareXrayedByQueen(pos, WHITE, c) ? QUEEN_BIT : 0) |
- (_IsSquareAttackedByKing(pos, WHITE, c) ? KING_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) |
- (_IsSquareAttackedByRook(pos, BLACK, c) ? ROOK_BIT : 0) |
- (_IsSquareXrayedByRook(pos, BLACK, c) ? ROOK_BIT : 0) |
- (_IsSquareAttackedByQueen(pos, BLACK, c) ? QUEEN_BIT : 0) |
- (_IsSquareXrayedByQueen(pos, BLACK, c) ? QUEEN_BIT : 0) |
- (_IsSquareAttackedByKing(pos, BLACK, c) ? KING_BIT : 0);
+ BITBOARD sq = COOR_TO_BB(c);
+ ULONG uWhite = ((pos->bbPawnAttacks[WHITE] & sq) ? PAWN_BIT : 0) |
+ ((pos->bbMinorAttacks[WHITE] & sq) ? MINOR_BIT : 0) |
+ ((pos->bbMinorXrayAttacks[WHITE] & sq) ? MINOR_BIT : 0) |
+ ((pos->bbRookAttacks[WHITE] & sq) ? ROOK_BIT : 0) |
+ ((pos->bbRookXrayAttacks[WHITE] & sq) ? ROOK_BIT : 0) |
+ ((pos->bbQueenAttacks[WHITE] & sq) ? QUEEN_BIT : 0) |
+ ((pos->bbQueenXrayAttacks[WHITE] & sq) ? QUEEN_BIT : 0) |
+ ((pos->bbKingAttacks[WHITE] & sq) ? KING_BIT : 0);
+ ULONG uBlack = ((pos->bbPawnAttacks[BLACK] & sq) ? PAWN_BIT : 0) |
+ ((pos->bbMinorAttacks[BLACK] & sq) ? MINOR_BIT : 0) |
+ ((pos->bbMinorXrayAttacks[BLACK] & sq) ? MINOR_BIT : 0) |
+ ((pos->bbRookAttacks[BLACK] & sq) ? ROOK_BIT : 0) |
+ ((pos->bbRookXrayAttacks[BLACK] & sq) ? ROOK_BIT : 0) |
+ ((pos->bbQueenAttacks[BLACK] & sq) ? QUEEN_BIT : 0) |
+ ((pos->bbQueenXrayAttacks[BLACK] & sq) ? QUEEN_BIT : 0) |
+ ((pos->bbKingAttacks[BLACK] & sq) ? KING_BIT : 0);
ULONG u;
PIECE p;
CHAR ch;
- ASSERT((c + 8) == (c | 8));
ASSERT((uWhite & 0xFFFFFF00) == 0);
ASSERT((uBlack & 0xFFFFFF00) == 0);
@@ -1649,7 +1243,13 @@ Return value:
Routine description:
- Zero out the attack table before building it.
+ Zero out the Eval()-scoped attack-bitboard accumulators before
+ building them for this call. Used to clear the old per-square
+ rgSquare[c|8].bvAttacks/ATTACK_BITV structure too (a full-board
+ macro-unrolled loop, since every square's storage needed zeroing);
+ retired along with that structure (2026-09-05, board_
+ representation/EVAL.md section 9) -- nothing left to clear but the
+ bbXAttacks accumulators themselves.
Parameters:
@@ -1660,33 +1260,9 @@ Return value:
void
**/
-#define CLEAR_A_SQ \
- pos->rgSquare[c].bvAttacks[0].uWholeThing = 0; \
- pos->rgSquare[c].bvAttacks[1].uWholeThing = 0;
-
-#define CLEAR_A_RANK \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c += 9;
-
-#define CLEAR_SHORT_RANK \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c++; \
- CLEAR_A_SQ; c += 10;
-
static void
_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;
@@ -1694,33 +1270,6 @@ _ClearAttackTables(IN OUT POSITION *pos)
pos->bbQueenAttacks[WHITE] = pos->bbQueenAttacks[BLACK] = 0;
pos->bbQueenXrayAttacks[WHITE] = pos->bbQueenXrayAttacks[BLACK] = 0;
pos->bbKingAttacks[WHITE] = pos->bbKingAttacks[BLACK] = 0;
-#if 1
- CLEAR_A_SQ; c += 16;
- CLEAR_A_SQ; c += 16;
- CLEAR_A_SQ; c += 16;
- CLEAR_A_SQ; c += 16;
- CLEAR_A_SQ; c += 16;
- CLEAR_A_SQ; c += 16;
- CLEAR_A_SQ; c += 16;
- CLEAR_A_SQ; c = 9;
- CLEAR_SHORT_RANK;
- CLEAR_SHORT_RANK;
- CLEAR_SHORT_RANK;
- CLEAR_SHORT_RANK;
- CLEAR_SHORT_RANK;
- CLEAR_SHORT_RANK;
- CLEAR_SHORT_RANK;
- CLEAR_SHORT_RANK;
-#else
- CLEAR_A_RANK;
- CLEAR_A_RANK;
- CLEAR_A_RANK;
- CLEAR_A_RANK;
- CLEAR_A_RANK;
- CLEAR_A_RANK;
- CLEAR_A_RANK;
- CLEAR_A_RANK;
-#endif
}
@@ -1926,6 +1475,24 @@ Return value:
// IDEA: scale the candidate passer bonus based on rank AND on
// the distance the helper(s) have to go to get into position.
//
+ // KNOWN BUG, found 2026-09-05 while removing rgSquare[c|8].bvAttacks
+ // entirely (board_representation/EVAL.md section 9), NOT fixed
+ // here -- flagged for its own separate investigation instead of
+ // being bundled into a mechanical cleanup commit. This function
+ // runs from _EvalPawns, which is the *first* piece type evaluated
+ // each Eval() call -- every non-pawn piece (and, since commit
+ // 57502d6, pawns themselves) writes its attack bits later in the
+ // same call, so the "is c1 safe to advance a helper pawn into"
+ // check below has read an always-zero attack table for as long as
+ // bvAttacks has existed in its post-57502d6 form. The condition
+ // this used to gate on (skip a candidate passer if its helper
+ // square isn't actually safe to advance into) has therefore been
+ // unconditionally true -- a silent no-op -- since that commit,
+ // not something introduced by today's cleanup. Preserved exactly
+ // as that already-dead behavior (unconditional) rather than
+ // "fixed" here, since a real fix changes eval scoring and deserves
+ // its own before/after check, not one buried in a rename commit.
+ //
uHelpers = 0;
d1 = 16 * g_iAhead[uColor];
ASSERT(-d1 == 16 * g_iBehind[uColor]);
@@ -1933,39 +1500,33 @@ Return value:
if ((IS_ON_BOARD(c1)) && (pHash->uCountPerFile[uColor][FILE(c1) + 1]))
{
ASSERT(pos->bbPawns[uColor] & BBFILE[FILE(c1)]);
- if (!(pos->rgSquare[c1 + 8].bvAttacks[FLIP(uColor)].uWholeThing) ||
- (pos->rgSquare[c1 + 8].bvAttacks[uColor].uWholeThing))
+ //
+ // The square c1 the place a helper pawn must get to in
+ // order to aide the candidate past a sentry.
+ //
+ if (pos->rgSquare[c1].pPiece == pHelper)
+ {
+ uHelpers = 1;
+ goto do_left;
+ }
+
+ //
+ // There is no helper pawn in the support position yet.
+ // See if one can get there.
+ //
+ c1 = c1 - d1;
+ while (IS_ON_BOARD(c1))
{
- //
- // The square c1 the place a helper pawn must get to in
- // order to aide the candidate past a sentry.
- //
if (pos->rgSquare[c1].pPiece == pHelper)
{
uHelpers = 1;
- goto do_left;
+ break;
}
-
- //
- // There is no helper pawn in the support position yet.
- // See if one can get there.
- //
- c1 = c1 - d1;
- while (IS_ON_BOARD(c1) &&
- ((!(pos->rgSquare[c1+8].bvAttacks[FLIP(uColor)].uWholeThing)) ||
- (pos->rgSquare[c1+8].bvAttacks[uColor].uWholeThing)))
+ else if (pos->rgSquare[c1].pPiece == pSentry)
{
- if (pos->rgSquare[c1].pPiece == pHelper)
- {
- uHelpers = 1;
- break;
- }
- else if (pos->rgSquare[c1].pPiece == pSentry)
- {
- break;
- }
- c1 = c1 - d1;
+ break;
}
+ c1 = c1 - d1;
}
}
@@ -1975,39 +1536,33 @@ Return value:
{
ASSERT(pos->bbPawns[uColor] & BBFILE[FILE(c1)]);
- if (!(pos->rgSquare[c1 + 8].bvAttacks[FLIP(uColor)].uWholeThing) ||
- (pos->rgSquare[c1 + 8].bvAttacks[uColor].uWholeThing))
+ //
+ // The square c1 is the place a helper pawn must get to in
+ // order to aide the candidate.
+ //
+ if (pos->rgSquare[c1].pPiece == pHelper)
+ {
+ uHelpers++;
+ goto done_helpers;
+ }
+
+ //
+ // There is no pawn in the left support position yet. See
+ // if one can get there.
+ //
+ c1 -= d1;
+ while (IS_ON_BOARD(c1))
{
- //
- // The square c1 is the place a helper pawn must get to in
- // order to aide the candidate.
- //
if (pos->rgSquare[c1].pPiece == pHelper)
{
uHelpers++;
- goto done_helpers;
+ break;
}
-
- //
- // There is no pawn in the left support position yet. See
- // if one can get there.
- //
- c1 -= d1;
- while (IS_ON_BOARD(c1) &&
- ((!(pos->rgSquare[c1+8].bvAttacks[FLIP(uColor)].uWholeThing)) ||
- (pos->rgSquare[c1 + 8].bvAttacks[uColor].uWholeThing)))
+ else if (pos->rgSquare[c1].pPiece == pSentry)
{
- if (pos->rgSquare[c1].pPiece == pHelper)
- {
- uHelpers++;
- break;
- }
- else if (pos->rgSquare[c1].pPiece == pSentry)
- {
- break;
- }
- c1 -= d1;
+ break;
}
+ c1 -= d1;
}
}
@@ -2224,14 +1779,14 @@ Return value:
// diagonals need the *opposite* file excluded to prevent same-row
// wraparound) -- not repeated here.
//
-// Unlike the old version, this does NOT write PAWN_BIT into
-// rgSquare[c|8].bvAttacks -- pos->bbPawnAttacks[2] (chess.h) is now
-// the single source of truth for "does a pawn attack this square",
-// read directly by every consumer (UNSAFE_FOR_MINOR's old callers,
-// UNSAFE_FOR_ROOK/_QUEEN, _EvalKing's bvAttack/bvDefend). Knight/
-// bishop/rook/queen/king still populate/read the rest of bvAttacks
-// (uMinor/uRook/uQueen/uKing) the old way, so _ClearAttackTables(pos)
-// still needs to run here first.
+// Unlike the old version, this does NOT write PAWN_BIT into the old
+// rgSquare[c|8].bvAttacks/ATTACK_BITV mechanism (retired entirely as
+// of 2026-09-05, once king -- the last piece writing it -- converted
+// too) -- pos->bbPawnAttacks[2] (chess.h) is the single source of
+// truth for "does a pawn attack this square", read directly by every
+// consumer. _ClearAttackTables(pos) still needs to run here first, to
+// zero the other bbXAttacks accumulators knight/bishop/rook/queen/king
+// populate as they each evaluate.
static void
_PopulatePawnAttackBits(IN OUT POSITION *pos)
/**
@@ -4210,62 +3765,35 @@ Return value:
p = pos->rgSquare[cSquare].pPiece;
//
- // bvAttacks no longer carries the pawn bit (pawns write
- // pos->bbPawnAttacks[2] directly instead, see
- // _PopulatePawnAttackBits) -- OR it back in here from the
- // bitboard, keyed off the real board square (cSquare,
- // before the |8 below flips it into the invisible-half
- // storage index bvAttacks itself uses).
- //
- // No enemy-king contribution here, by direct instruction
- // (2026-09-05): the old mailbox version was already
- // asymmetric here (kings evaluate black-then-white, so
- // white's computation could see black's already-written
- // king bit but black's could never see white's, since
- // white hadn't run yet) -- rather than preserve or
- // "upgrade" that asymmetry now that both colors go through
- // an explicit helper either way, neither side sees the
- // enemy king as a threat here, matching the side that
- // already couldn't.
+ // board_representation/EVAL.md section 9: bvAttacks/
+ // ATTACK_BITV retired entirely -- every bit here now comes
+ // straight from a bbXAttacks accumulator read, no more
+ // c|8 shadow-index struct storage or per-square writes.
+ // No enemy-king contribution in bvAttack, by direct
+ // instruction (see this function's header comment on the
+ // black-then-white evaluation-order asymmetry this
+ // sidesteps). bvDefend's own-king bit is real, load-
+ // bearing signal (see the bvDefend &= ~8 below, which
+ // strips it back out when the square is x-rayed or
+ // multiply attacked -- "a lone king isn't adequate defense
+ // against that") -- not just self-consistency noise, so
+ // it keeps its own-color check.
//
- bvAttack = pos->rgSquare[cSquare|8].bvAttacks[ufColor].uSmall |
- ((pos->bbPawnAttacks[ufColor] & COOR_TO_BB(cSquare)) ?
- PAWN_BIT : 0) |
- (_IsSquareAttackedByMinor(pos, ufColor, cSquare) ?
- MINOR_BIT : 0) |
- (_IsSquareAttackedByRook(pos, ufColor, cSquare) ?
- ROOK_BIT : 0) |
- (_IsSquareAttackedByQueen(pos, ufColor, cSquare) ?
- QUEEN_BIT : 0);
{
- COOR cRealSquare = cSquare;
- cSquare |= 8;
- bvXray = pos->rgSquare[cSquare].bvAttacks[ufColor].uXray |
- (_IsSquareXrayedByMinor(pos, ufColor, cRealSquare) ?
- MINOR_BIT : 0) |
- (_IsSquareXrayedByRook(pos, ufColor, cRealSquare) ?
- ROOK_BIT : 0) |
- (_IsSquareXrayedByQueen(pos, ufColor, cRealSquare) ?
- QUEEN_BIT : 0);
- //
- // bvDefend's own-king bit is real, load-bearing signal
- // (see the bvDefend &= ~8 below, which strips it back
- // out when the square is x-rayed or multiply attacked
- // -- "a lone king isn't adequate defense against
- // that") -- not just self-consistency noise, so this
- // one keeps its own-color check.
- //
- bvDefend = pos->rgSquare[cSquare].bvAttacks[uColor].uSmall |
- ((pos->bbPawnAttacks[uColor] & COOR_TO_BB(cRealSquare)) ?
- PAWN_BIT : 0) |
- (_IsSquareAttackedByMinor(pos, uColor, cRealSquare) ?
- MINOR_BIT : 0) |
- (_IsSquareAttackedByRook(pos, uColor, cRealSquare) ?
- ROOK_BIT : 0) |
- (_IsSquareAttackedByQueen(pos, uColor, cRealSquare) ?
- QUEEN_BIT : 0) |
- (_IsSquareAttackedByKing(pos, uColor, cRealSquare) ?
- KING_BIT : 0);
+ BITBOARD sq = COOR_TO_BB(cSquare);
+
+ bvAttack = ((pos->bbPawnAttacks[ufColor] & sq) ? PAWN_BIT : 0) |
+ ((pos->bbMinorAttacks[ufColor] & sq) ? MINOR_BIT : 0) |
+ ((pos->bbRookAttacks[ufColor] & sq) ? ROOK_BIT : 0) |
+ ((pos->bbQueenAttacks[ufColor] & sq) ? QUEEN_BIT : 0);
+ bvXray = ((pos->bbMinorXrayAttacks[ufColor] & sq) ? MINOR_BIT : 0) |
+ ((pos->bbRookXrayAttacks[ufColor] & sq) ? ROOK_BIT : 0) |
+ ((pos->bbQueenXrayAttacks[ufColor] & sq) ? QUEEN_BIT : 0);
+ bvDefend = ((pos->bbPawnAttacks[uColor] & sq) ? PAWN_BIT : 0) |
+ ((pos->bbMinorAttacks[uColor] & sq) ? MINOR_BIT : 0) |
+ ((pos->bbRookAttacks[uColor] & sq) ? ROOK_BIT : 0) |
+ ((pos->bbQueenAttacks[uColor] & sq) ? QUEEN_BIT : 0) |
+ ((pos->bbKingAttacks[uColor] & sq) ? KING_BIT : 0);
}
//