summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/chess.h23
-rwxr-xr-xsrc/eval.c106
2 files changed, 104 insertions, 25 deletions
diff --git a/src/chess.h b/src/chess.h
index 6b09840..aababc0 100755
--- a/src/chess.h
+++ b/src/chess.h
@@ -614,6 +614,10 @@ ATTACK_BITV;
#define ROOK_XRAY_BIT 0x20000000UL
#define QUEEN_BIT 0x00000010UL
#define QUEEN_XRAY_BIT 0x10000000UL
+// King never x-rays (it can't move through a blocker), so there's no
+// KING_XRAY_BIT to go with this -- matches struct _ATTACK_BITV's
+// .small.uKing bit position (byte 0, bit 3).
+#define KING_BIT 0x00000008UL
#define INVALID_PIECE_INDEX (17)
#define IS_VALID_PIECE_INDEX(x) ((x) < INVALID_PIECE_INDEX)
@@ -770,6 +774,25 @@ typedef struct _POSITION
BITBOARD bbQueenAttacks[2];
BITBOARD bbQueenXrayAttacks[2];
+ // King's turn to convert (board_representation/EVAL.md section 9,
+ // 2026-09-05) -- the last piece type contributing to the old
+ // bvAttacks/ATTACK_BITV mechanism. Just g_KingAttacksBB[c]
+ // (generate.c's precomputed table, already used by move
+ // generation), no mobility computation involved and no x-ray
+ // (a king can't move through a blocker). Same Eval()-scoped/
+ // cleared-per-call lifetime as every other bbXAttacks accumulator
+ // above -- this also reproduces, for free, an existing
+ // order-dependent asymmetry _EvalKing's mailbox version already
+ // had: kings are evaluated black-then-white (Eval()'s fixed
+ // order), so white's king-safety computation can see black's
+ // already-written attack bits but not vice versa. Once this
+ // lands, nothing writes bvAttacks/ATTACK_BITV any more -- see
+ // EVAL.md section 9 for the planned follow-up that deletes the
+ // whole mechanism and simplifies the transitional
+ // _IsSquareAttackedByX/_IsSquareXrayedByX helpers into plain
+ // bitboard reads.
+ BITBOARD bbKingAttacks[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 12783ba..6adf27b 100755
--- a/src/eval.c
+++ b/src/eval.c
@@ -1526,6 +1526,29 @@ _IsSquareXrayedByQueen(IN POSITION *pos,
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)
@@ -1559,10 +1582,20 @@ 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, rook's, and now
- // queen's contributions all moved to pos->bbMinorXrayAttacks/
- // bbRookXrayAttacks/bbQueenXrayAttacks; only king hasn't converted
- // yet, still valid straight off .uXray.
+ // 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)) ?
@@ -1572,7 +1605,8 @@ Return value:
(_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);
+ (_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)) ?
@@ -1582,7 +1616,8 @@ Return value:
(_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);
+ (_IsSquareXrayedByQueen(pos, BLACK, c) ? QUEEN_BIT : 0) |
+ (_IsSquareAttackedByKing(pos, BLACK, c) ? KING_BIT : 0);
ULONG u;
PIECE p;
CHAR ch;
@@ -1658,6 +1693,7 @@ _ClearAttackTables(IN OUT POSITION *pos)
pos->bbRookXrayAttacks[WHITE] = pos->bbRookXrayAttacks[BLACK] = 0;
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;
@@ -4132,18 +4168,12 @@ Return value:
ASSERT(u >= VALUE_KING);
if (u < DO_KING_SAFETY_THRESHOLD)
{
- u = 0;
- ASSERT(g_iQKDeltas[u] != 0);
- do
- {
- cSquare = c + g_iQKDeltas[u];
- if (IS_ON_BOARD(cSquare))
- {
- pos->rgSquare[cSquare|8].bvAttacks[uColor].small.uKing = 1;
- }
- u++;
- }
- while(g_iQKDeltas[u] != 0);
+ // board_representation/EVAL.md section 9: g_KingAttacksBB[c]
+ // (generate.c's precomputed table, already used by move
+ // generation) is exactly the old g_iQKDeltas walk's
+ // destination set, IS_ON_BOARD baked in at table-build time --
+ // one OR instead of an 8-iteration loop.
+ pos->bbKingAttacks[uColor] |= g_KingAttacksBB[c];
goto skip_safety;
}
@@ -4159,6 +4189,16 @@ Return value:
Trace("%s KS Counter after pieces pointing: %u\n",
COLOR_NAME(uColor), uCounter);
#endif
+ // board_representation/EVAL.md section 9: written once, before the
+ // loop below, instead of per-square inside it -- also the bugfix
+ // agreed on for this conversion. The old per-square write used
+ // KingSafetyDeltas (11 entries) rather than the real 8-square king
+ // move pattern, so it spuriously marked two squares 2 files away
+ // on the same rank (KingSafetyDeltas' -2/+2 entries, present only
+ // for this loop's own file-distance bookkeeping) as
+ // "king-attacked" too. g_KingAttacksBB[c] is the real pattern.
+ pos->bbKingAttacks[uColor] |= g_KingAttacksBB[c];
+
uFlightSquares = 0;
u = 0;
ASSERT(KingSafetyDeltas[u] != 0);
@@ -4175,11 +4215,18 @@ Return value:
// _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). bvDefend's OR-in
- // (and the .small.uKing = 1 write) must stay after this
- // point, same order as before -- bvDefend is meant to
- // include this king's own just-set attack/defend bit on
- // the square.
+ // 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.
//
bvAttack = pos->rgSquare[cSquare|8].bvAttacks[ufColor].uSmall |
((pos->bbPawnAttacks[ufColor] & COOR_TO_BB(cSquare)) ?
@@ -4200,7 +4247,14 @@ Return value:
ROOK_BIT : 0) |
(_IsSquareXrayedByQueen(pos, ufColor, cRealSquare) ?
QUEEN_BIT : 0);
- pos->rgSquare[cSquare].bvAttacks[uColor].small.uKing = 1;
+ //
+ // 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) |
@@ -4209,7 +4263,9 @@ Return value:
(_IsSquareAttackedByRook(pos, uColor, cRealSquare) ?
ROOK_BIT : 0) |
(_IsSquareAttackedByQueen(pos, uColor, cRealSquare) ?
- QUEEN_BIT : 0);
+ QUEEN_BIT : 0) |
+ (_IsSquareAttackedByKing(pos, uColor, cRealSquare) ?
+ KING_BIT : 0);
}
//