diff options
Diffstat (limited to 'src/eval.c')
| -rwxr-xr-x | src/eval.c | 106 |
1 files changed, 81 insertions, 25 deletions
@@ -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); } // |
