diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/chess.h | 23 | ||||
| -rwxr-xr-x | src/eval.c | 106 |
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 @@ -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); } // |
