diff options
| author | Scott Gasch <[email protected]> | 2026-10-10 10:29:48 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-10 10:29:48 -0700 |
| commit | 03545c4600df81b1a47dbc63fc606c065e857d8b (patch) | |
| tree | a433fdce69fd27359679ad91e0ebedd52e2506cd /src/eval.c | |
| parent | fbdcb9d76dd2444d8bc631331eaeb9435cc247f8 (diff) | |
Eval mobility/pseudo-mobility tweaks; drop unproductive move-ordering experiments.
eval.c: add bishop/rook x-ray pseudo-mobility terms (squares seen past a
directly-attacked higher-value piece), trim pawn-centrality edge penalty
and bishop-pair bonus.
chess.h/dynamic.c/generate.c/search.c/searchsup.c: revert three move-ordering
experiments (recapture bonus, flee-to-safety GOOD_MOVE promotion, ply-2
move-pair bonus) that calibrated with real fail-high signal but never
converted into a measurable node-count or solve-rate win on the curated
suites -- see MOVE_ORDERING_IDEAS.md for the evidence and reasoning behind
each drop. Add MOVE_ORDERING_EVIDENCE_COLLECTION as its own GNUmakefile
flag, independent of PERF_COUNTERS, for any future such harness.
command.c: remove the KING_DEFECT_DUMP-gated evalkingdump command, dead
since the king-safety remine work moved to eval_tune/king_safety_data/.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/eval.c')
| -rw-r--r-- | src/eval.c | 890 |
1 files changed, 340 insertions, 550 deletions
@@ -192,13 +192,13 @@ COOR JUMPING_RANK[2] = { 7, 2 }; static SCORE PAWN_CENTRALITY_BONUS[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0, - -8, 0, 0, 0, 0, 0, 0, -8, 0,0,0,0,0,0,0,0, - -8, 0, 5, 5, 5, 5, 0, -8, 0,0,0,0,0,0,0,0, - -8, 0, 5, 8, 8, 5, 0, -8, 0,0,0,0,0,0,0,0, + -5, 0, 0, 0, 0, 0, 0, -5, 0,0,0,0,0,0,0,0, + -5, 0, 5, 5, 5, 5, 0, -5, 0,0,0,0,0,0,0,0, + -5, 0, 5, 8, 8, 5, 0, -5, 0,0,0,0,0,0,0,0, // ------------------------------------------------------------------- - -8, 0, 5, 8, 8, 5, 0, -8, 0,0,0,0,0,0,0,0, - -8, 0, 5, 5, 5, 5, 0, -8, 0,0,0,0,0,0,0,0, - -8, 0, 0, 0, 0, 0, 0, -8, 0,0,0,0,0,0,0,0, + -5, 0, 5, 8, 8, 5, 0, -5, 0,0,0,0,0,0,0,0, + -5, 0, 5, 5, 5, 5, 0, -5, 0,0,0,0,0,0,0,0, + -5, 0, 0, 0, 0, 0, 0, -5, 0,0,0,0,0,0,0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0 }; @@ -412,7 +412,7 @@ static SCORE UNDEVELOPED_MINORS_IN_OPENING[5] = // pieces on the board except two minors each." // --Larry Kaufman, IM // "Evaluation of Material Imbalance" -static SCORE BISHOP_PAIR_BONUS = 40; +static SCORE BISHOP_PAIR_BONUS = 30; static SCORE STATIONARY_PAWN_ON_BISHOP_COLOR[128] = { @@ -449,6 +449,22 @@ static SCORE BISHOP_MOBILITY_BY_SQUARES[14] = };// ^ | +// +// Pseudo-mobility: squares this bishop can "see" past a directly-attacked +// higher-value enemy piece (rook/queen/king) -- real positional pressure +// (the squares a capture or a moving-away enemy piece would open up), not +// real mobility (the bishop cannot actually move there this ply). No +// attempt to model whether the bishop itself is defended -- that's a +// search-shaped question (SEE/exchange-accurate, whose-move-is-it), not +// an eval one; see MOBILITY_BITBOARD_REWRITE.md. Deliberately smaller +// than BISHOP_MOBILITY_BY_SQUARES throughout. +// +static SCORE BISHOP_XRAY_MOBILITY_BY_SQUARES[9] = +{// 0 1 2 3 4 5 6 7 8 + +0, +1, +1, +2, +2, +3, +3, +4, +4 +}; + + static SCORE BISHOP_UNASSAILABLE_BY_DIST_FROM_EKING[9] = {// 0 1 2 3 4 5 6 7 8 +0, +33, +28, +18, +8, +4, +0, -10, -16 @@ -582,6 +598,19 @@ static SCORE ROOK_MOBILITY_BY_SQUARES[15] = // +// Pseudo-mobility: squares this rook can "see" past a directly-attacked +// enemy queen or king -- same idea as BISHOP_XRAY_MOBILITY_BY_SQUARES +// above, same caveats (no defended/undefended modeling -- that's +// search's job). Deliberately smaller than ROOK_MOBILITY_BY_SQUARES +// throughout. +// +static SCORE ROOK_XRAY_MOBILITY_BY_SQUARES[9] = +{// 0 1 2 3 4 5 6 7 8 + +0, +1, +1, +2, +2, +3, +3, +4, +4 +}; + + +// // Queen eval terms // --------------------------------------------------------------------------- // @@ -933,6 +962,7 @@ static DNA_BASE_SIZE g_EvalDNA[] = { DNA_ARRAY(STATIONARY_PAWN_ON_BISHOP_COLOR), DNA_ARRAY(TRANSIENT_PAWN_ON_BISHOP_COLOR), DNA_ARRAY(BISHOP_MOBILITY_BY_SQUARES), + DNA_ARRAY(BISHOP_XRAY_MOBILITY_BY_SQUARES), DNA_ARRAY(BISHOP_UNASSAILABLE_BY_DIST_FROM_EKING), DNA_ARRAY(KNIGHT_CENTRALITY_BONUS), DNA_ARRAY(KNIGHT_KING_TROPISM_BONUS), @@ -951,6 +981,7 @@ static DNA_BASE_SIZE g_EvalDNA[] = { DNA_VAR(ROOK_CONNECTED_VERT), DNA_VAR(ROOK_CONNECTED_HORIZ), DNA_ARRAY(ROOK_MOBILITY_BY_SQUARES), + DNA_ARRAY(ROOK_XRAY_MOBILITY_BY_SQUARES), DNA_ARRAY(QUEEN_MOBILITY_BY_SQUARES), DNA_ARRAY(QUEEN_KING_TROPISM), DNA_MATRIX(KING_INITIAL_COUNTER_BY_LOCATION), @@ -2833,88 +2864,6 @@ Return value: } -#ifdef KING_DEFECT_DUMP -static void -_ComputeFileStormDefectsSplit(IN POSITION *pos, - IN const PAWN_HASH_ENTRY *pHash, - IN ULONG uColor, - OUT ULONG *puOpenFile, - OUT ULONG *puStormingPawn) -/** - -Routine description: - - Debug-only sibling of _ComputeFileStormDefects that reports its two - independent sub-ideas (open/half-open file bonus vs. storming-pawn - distance penalty) separately instead of pre-summed, so a regression - can weigh them independently -- they already live in two distinct - hand-picked tables (KingFileDefects / KingStormingPawnDefects) with - no structural change needed to retune them individually once this - tells us their relative importance. See CLAUDE.md's king-safety - discussion. - - 2026-09-15: DELIBERATELY DIVERGED from _ComputeFileStormDefects as - of the linear-SCORE rewrite (king_safety_data/HANDOFF.md's - 2026-09-15 addendum) -- the real path now returns signed centipawns - off a differently-shaped table, but this dump sibling must keep - reporting the OLD unsigned counter-style values so - king_features_v4.csv-shaped mining data stays interpretable/ - comparable across sessions (extract_king_features.py, this project's - whole regression pipeline, assumes these units). Traversal shape - (own file + two adjacent, same v/distance logic) still matches -- - only the two tables' actual values are now intentionally frozen at - their pre-rewrite originals, not kept in sync. - -**/ -{ - static ULONG KingStormingPawnDefects[8] = - { - +0, +7, +5, +2, +0, +0, +0, +0 - }; - static ULONG KingFileDefects[3] = - { - +4, +2, +0 - }; - ULONG ufColor = FLIP(uColor); - COOR c = KingPosition(pos, uColor); - COOR cFileSq, cSquare; - ULONG u, v; - BITBOARD bb; - int w; - - *puOpenFile = 0; - *puStormingPawn = 0; - for (w = -1; w <= 1; w++) - { - cFileSq = c + w; - if (!IS_ON_BOARD(cFileSq)) - { - continue; - } - u = FILE(cFileSq) + 1; - v = (pHash->uCountPerFile[WHITE][u] > 0) + - (pHash->uCountPerFile[BLACK][u] > 0); - ASSERT((v >= 0) && (v <= 2)); - *puOpenFile += (KingFileDefects[v] + ((v < 2) && ((u == 1) || (u == 8)))); - bb = pos->bbPawns[ufColor] & BBFILE[u - 1]; - if (bb) - { - if (uColor == WHITE) - { - cSquare = CoorFromBitBoardRank1ToRank8(&bb); - } - else - { - cSquare = CoorFromBitBoardRank8ToRank1(&bb); - } - ASSERT(IS_ON_BOARD(cSquare)); - *puStormingPawn += KingStormingPawnDefects[DISTANCE(cSquare, c)]; - } - } -} -#endif - - static SCORE _GetFileStormDefects(IN SEARCHER_THREAD_CONTEXT *ctx, IN POSITION *pos, @@ -4377,7 +4326,8 @@ Return value: static void _EvalBishop(IN OUT POSITION *pos, IN COOR c, - IN PAWN_HASH_ENTRY *pHash) + IN PAWN_HASH_ENTRY *pHash, + IN BITBOARD bbEnemyRQK) /** Routine description: @@ -4387,6 +4337,10 @@ Parameters: POSITION *pos, COOR c, PAWN_HASH_ENTRY *pHash, + BITBOARD bbEnemyRQK -- enemy rook|queen|king, precomputed once per + color by Eval()'s driver instead of rebuilt by every bishop of + that color (see MOBILITY_BITBOARD_REWRITE.md) -- depends only on + static piece locations, no eval-ordering dependency. Return value: @@ -4444,98 +4398,79 @@ Return value: pHash->bbStationaryPawns[BLACK]) & bbMask; // - // 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. + // Bishop mobility (and update attack tables) -- single magic-bitboard + // probe, no chain walk (see MOBILITY_BITBOARD_REWRITE.md). The old + // seven-case BMobCaseTable collapses to pure mask arithmetic over the + // one-shot first-blocker attack set: + // - terminal enemy non-pawn (minor/rook/queen/king): counts + // unconditionally -- this is a capture, always "real" mobility. + // - empty or terminal enemy pawn: counts unless pawn-unsafe -- both + // terminal-square categories together are exactly "not + // friend-occupied and not enemy-non-pawn-occupied," the same + // partition knight's reduction 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. + // color complex: still counts -- bbPc (already computed above for + // the good/bad transient-pawn scoring) is exactly "non-stationary + // pawns of either color on this color complex." + // - any other friendly piece (knight/rook/king): blocks, no count. + // + // Deliberately NOT replicated: mobility/connectivity credit for + // squares beyond a friendly bishop/queen or enemy rook/queen/king -- + // a bishop cannot physically move past a piece it would capture in + // one ply, so that was never "real mobility" to begin with (dropping + // it is a correctness fix, not just a simplification). See + // MOBILITY_BITBOARD_REWRITE.md for the king-ring x-ray replacement, + // which is the one piece of information this loses that's worth + // deliberately replacing below. // BITBOARD bbAttack = _BishopAttacksBB(c, pos->bbOccupied); BITBOARD bbEnemySame = pos->bbPieces[xColor][BISHOP] | pos->bbPieces[xColor][KNIGHT]; - BITBOARD bbEnemyGEContinue = pos->bbPieces[xColor][ROOK] | - pos->bbPieces[xColor][QUEEN] | - COOR_TO_BB(KingPosition(pos, xColor)); - BITBOARD bbFriendBQ = pos->bbPieces[uColor][BISHOP] | - pos->bbPieces[uColor][QUEEN]; BITBOARD bbUnsafeForMinor = pos->bbPawnAttacks[xColor]; - BITBOARD bbExclude = 0; - BITBOARD bbSeen = 0; - BITBOARD bbLayer = bbAttack; - BITBOARD bbMobility = 0; - BITBOARD bbXrayAccum = 0; - BITBOARD bbFirstLayerMask = 0; + BITBOARD bbEnemyPawnHere = bbAttack & pos->bbPawns[xColor]; + BITBOARD bbTransientHere = bbAttack & pos->bbPawns[uColor] & bbPc; + BITBOARD bbEnemyGEHere = bbAttack & bbEnemyRQK; + BITBOARD bbMobility; + BITBOARD bbXray; pos->bbMinorAttacks[uColor] |= bbAttack; + bbMobility = (bbAttack & ~pos->bbOccupied & ~bbUnsafeForMinor) | + (bbAttack & bbEnemySame) | + bbEnemyGEHere | + (bbEnemyPawnHere & ~bbUnsafeForMinor) | + bbTransientHere; + // - // Unified chain walk over both mobility and x-ray population - // in one pass -- same technique and same reason as rook's - // identical rewrite (board_representation/EVAL.md section 9): - // the old mailbox walk's BMOB_FRIEND_XRAY/BMOB_ENEMY_GREATER - // cases don't just x-ray past their blocker for attack-bit - // purposes, they keep walking (fStop=FALSE) and keep crediting - // mobility for whatever lies beyond. The originally-committed - // bitboard version (and its single-hop-then-chain-following - // x-ray fix) only ever handled the near side for *mobility*, - // an under-count caught the same way rook's was: a two-bishop - // battery on one diagonal with open squares beyond (old - // mailbox: 12/8 mobility for the two bishops; near-side-only - // bitboard version: 11/5). + // King-ring x-ray, replacing the old chain's attack-bit population: + // only pay for an extra magic probe when this bishop already + // directly attacks an enemy rook/queen/king square (bbEnemyGEHere, + // already computed above for mobility -- free to test). Every bit + // in bbEnemyGEHere is, by construction, the terminal blocker of a + // *different* diagonal ray, so masking all of them off at once and + // re-probing in a single magic lookup gives the exact same union a + // per-blocker loop would (removing one ray's blocker can't affect a + // different ray's result) -- no need to isolate and remove them one + // at a time. No deeper chaining needed either way, since the only + // consumer (_EvalKingSafety) only ever ANDs this against the king's + // ring, never walks further. // - for (;;) - { - BITBOARD bbNew = bbLayer & ~bbSeen; - BITBOARD bbBlockersHere = bbNew & pos->bbOccupied; - BITBOARD bbEmptyHere = bbNew & ~pos->bbOccupied; - BITBOARD bbFriendBQHere = bbBlockersHere & bbFriendBQ; - BITBOARD bbEnemySameHere = bbBlockersHere & bbEnemySame; - BITBOARD bbEnemyGEHere = bbBlockersHere & bbEnemyGEContinue; - BITBOARD bbFriendPawnHere = bbBlockersHere & pos->bbPawns[uColor]; - BITBOARD bbEnemyPawnHere = bbBlockersHere & - pos->bbPawns[xColor]; - BITBOARD bbTransientHere = bbFriendPawnHere & bbPc; - BITBOARD bbContinueHere; + bbXray = bbEnemyGEHere ? + _BishopAttacksBB(c, pos->bbOccupied & ~bbEnemyGEHere) : 0; + pos->bbMinorXrayAttacks[uColor] |= bbXray; - bbSeen |= bbNew; - bbXrayAccum |= (bbNew & bbFirstLayerMask); - bbFirstLayerMask = ~(BITBOARD)0; - - bbMobility |= (bbEmptyHere & ~bbUnsafeForMinor) | - bbEnemySameHere | bbEnemyGEHere | - (bbEnemyPawnHere & ~bbUnsafeForMinor) | - bbTransientHere; - - bbContinueHere = bbFriendBQHere | bbEnemyGEHere; - if (!bbContinueHere) - { - break; - } - bbExclude |= bbContinueHere; - bbLayer = _BishopAttacksBB(c, pos->bbOccupied & ~bbExclude); - } - pos->bbMinorXrayAttacks[uColor] |= bbXrayAccum; + // Pseudo-mobility bonus for squares seen past the higher-value + // blocker -- see BISHOP_XRAY_MOBILITY_BY_SQUARES's comment. bbXray + // already includes the blocker's own square (redundant with + // bbAttack, which already counts it as a capture), so subtracting + // bbAttack leaves exactly the net-new squares revealed behind it. + EVAL_TERM(uColor, + BISHOP, + c, + pos->iScore[uColor], + BISHOP_XRAY_MOBILITY_BY_SQUARES[ + MINU(FastCountBits(bbXray & ~bbAttack), 8)], + "xray pseudo-mobility"); uTotalMobility = FastCountBits(bbMobility); @@ -4845,7 +4780,9 @@ Return value: static void _EvalRook(IN OUT POSITION *pos, IN COOR c, - IN PAWN_HASH_ENTRY *pHash) + IN PAWN_HASH_ENTRY *pHash, + IN BITBOARD bbEnemyQK, + IN BITBOARD bbUnsafeForRook) /** Routine description: @@ -4855,6 +4792,12 @@ Parameters: IN POSITION *pos, IN COOR c, IN PAWN_HASH_ENTRY *pHash, + IN BITBOARD bbEnemyQK -- enemy queen|king, precomputed once per color + by Eval()'s driver (see MOBILITY_BITBOARD_REWRITE.md) -- depends + only on static piece locations. + IN BITBOARD bbUnsafeForRook -- enemy pawn|minor attacks, precomputed + once per color right before the rook loops start, valid because + both colors' knight+bishop passes are already complete by then. Return value: @@ -4911,125 +4854,88 @@ Return value: (FastCountBits(pos->bbPawns[uColor]) + FastCountBits(pos->bbPawns[xColor]))); // - // Rook mobility (and update attack tables) -- board_representation/ - // EVAL.md section 1b/9: _RookAttacksBB(c, pos->bbOccupied) - // (generate.c's magic-bitboard slider lookup) gives the whole - // ray-to-first-blocker attack set in one shot, same technique - // already landed for knight/bishop. The old RMobCaseTable's six - // live cases collapse to: - // - terminal enemy rook/queen/king (RMOB_ENEMY_SAME/_GREATER): - // counts unconditionally, no unsafe check -- capturing an - // equal-or-higher piece is never "unsafe" in the mobility - // sense. Queen/king additionally x-ray past (_GREATER); rook - // does not (_SAME). - // - empty or terminal enemy pawn/knight/bishop (RMOB_EMPTY / - // RMOB_ENEMY_LESS): counts unless the square is attacked by an - // enemy pawn or minor -- both terminal-square categories - // together are exactly "not friend-occupied and not - // enemy-rook/queen/king-occupied," the same partition - // bishop/knight's reductions already use with their own value - // thresholds. - // - any friendly piece (RMOB_FRIEND_BLOCK/_ROOK/_QUEEN): never - // counts, simply excluded by the friendly-occupied mask. - // Friendly rook/queen additionally x-ray past (no mobility - // credit at the blocker itself, matching the old table); a - // friendly rook blocker also earns the connected-rook bonus. + // Rook mobility (and update attack tables) -- single magic-bitboard + // probe, no chain walk (see MOBILITY_BITBOARD_REWRITE.md). The old + // RMobCaseTable's six cases collapse to mask arithmetic over the + // one-shot first-blocker attack set: + // - terminal enemy rook/queen/king: counts unconditionally -- a + // capture is always "real" mobility regardless of value. + // - empty or terminal enemy pawn/knight/bishop: counts unless the + // square is attacked by an enemy pawn or minor -- both + // terminal-square categories together are exactly "not + // friend-occupied and not enemy-rook/queen/king-occupied," the + // same partition bishop/knight's reductions use. + // - any friendly piece: blocks, no count. A friendly rook + // specifically also earns the connected-rook bonus (direct + // attack only now, not chained through a further blocker). + // + // Deliberately NOT replicated: mobility/connectivity credit for + // squares beyond a friendly rook/queen or enemy queen/king -- a + // rook cannot physically move past a piece it would capture in one + // ply, so that was never "real mobility" to begin with (dropping it + // is a correctness fix, not just a simplification). See + // MOBILITY_BITBOARD_REWRITE.md for the king-ring x-ray replacement, + // which is the one piece of information this loses that's worth + // deliberately replacing below. // - BITBOARD bbAttack = _RookAttacksBB(c, pos->bbOccupied); BITBOARD bbEnemyOcc = _BuildFriendlySideBB(pos, xColor); BITBOARD bbEnemySame = pos->bbPieces[xColor][ROOK]; - BITBOARD bbEnemyGEContinue = pos->bbPieces[xColor][QUEEN] | - COOR_TO_BB(KingPosition(pos, xColor)); - BITBOARD bbFriendRQ = pos->bbPieces[uColor][ROOK] | - pos->bbPieces[uColor][QUEEN]; - BITBOARD bbUnsafeForRook = pos->bbPawnAttacks[xColor] | - pos->bbMinorAttacks[xColor]; - BITBOARD bbExclude = 0; - BITBOARD bbSeen = 0; - BITBOARD bbLayer = bbAttack; - BITBOARD bbXrayAccum = 0; - BITBOARD bbFirstLayerMask = 0; + BITBOARD bbEnemyGEHere = bbAttack & bbEnemyQK; + BITBOARD bbEnemyLEHere = bbAttack & bbEnemyOcc & + ~bbEnemySame & ~bbEnemyGEHere; + BITBOARD bbFriendRookHere = bbAttack & pos->bbPieces[uColor][ROOK]; + BITBOARD bbXray; - bbMobility = 0; pos->bbRookAttacks[uColor] |= bbAttack; - // - // Unified chain walk over both mobility and x-ray population - // in one pass, board_representation/EVAL.md section 9. The - // old mailbox walk's RMOB_FRIEND_ROOK/_QUEEN and - // RMOB_ENEMY_GREATER cases don't just x-ray past their blocker - // for attack-bit purposes -- they keep walking (fStop=FALSE) - // and keep crediting mobility for whatever safe/empty squares - // and further captures lie beyond, however many such blockers - // are stacked on one ray. A first version of this conversion - // (committed, since fixed) only handled the near side (up to - // the first blocker) for mobility and treated the chain purely - // as an attack-bit population exercise -- caught by comparing - // against the old mailbox walk directly on a battery position - // (two same-color rooks on an open file: old code credited 13 - // mobility to the far rook's near companion via squares beyond - // it, the near-side-only version credited only 9). Fixed by - // reusing the exact same "recompute with blockers excluded" - // chain used for x-ray population to also accumulate mobility - // credit at every layer, not just the first. - // - // Each iteration classifies the newly-revealed blockers on - // each still-open ray: friendly rook/queen and enemy queen/ - // king continue the chain (recompute with them excluded too); - // everything else (friendly non-R/Q, enemy pawn/knight/bishop, - // enemy rook, or the board edge) stops that ray. Bounded: each - // iteration either terminates or consumes at least one new - // piece into the exclusion set, so this can loop at most - // once per piece on the board. - // - for (;;) - { - BITBOARD bbNew = bbLayer & ~bbSeen; - BITBOARD bbBlockersHere = bbNew & pos->bbOccupied; - BITBOARD bbEmptyHere = bbNew & ~pos->bbOccupied; - BITBOARD bbFriendRQHere = bbBlockersHere & bbFriendRQ; - BITBOARD bbEnemySameHere = bbBlockersHere & bbEnemySame; - BITBOARD bbEnemyGEHere = bbBlockersHere & bbEnemyGEContinue; - BITBOARD bbEnemyLEHere = bbBlockersHere & bbEnemyOcc & - ~bbEnemySameHere & ~bbEnemyGEHere; - BITBOARD bbFriendRookHere = bbBlockersHere & - pos->bbPieces[uColor][ROOK]; - BITBOARD bbContinueHere; - - bbSeen |= bbNew; - bbXrayAccum |= (bbNew & bbFirstLayerMask); - bbFirstLayerMask = ~(BITBOARD)0; + bbMobility = (bbAttack & ~pos->bbOccupied & ~bbUnsafeForRook) | + (bbAttack & bbEnemySame) | + bbEnemyGEHere | + (bbEnemyLEHere & ~bbUnsafeForRook); - bbMobility |= (bbEmptyHere & ~bbUnsafeForRook) | - bbEnemySameHere | bbEnemyGEHere | - (bbEnemyLEHere & ~bbUnsafeForRook); + // Connected-rook bonus -- direct attack only now; see the comment + // above. + while (bbFriendRookHere) + { + COOR cBlocker = CoorFromBitBoardRank8ToRank1(&bbFriendRookHere); + FLAG fHoriz = (RANK(cBlocker) == RANK(c)); + EVAL_TERM(uColor, + ROOK, + cBlocker, + pos->iScore[uColor], + (ROOK_CONNECTED_HORIZ * fHoriz + + ROOK_CONNECTED_VERT * FLIP(fHoriz)), + "rooks connected"); + } - // Connected-rook bonus fires at every friendly rook found - // along the chain, not just the first (matches the old - // walk: each RMOB_FRIEND_ROOK hit its own EVAL_TERM call). - while (bbFriendRookHere) - { - COOR cBlocker = CoorFromBitBoardRank8ToRank1(&bbFriendRookHere); - FLAG fHoriz = (RANK(cBlocker) == RANK(c)); - EVAL_TERM(uColor, - ROOK, - cBlocker, - pos->iScore[uColor], - (ROOK_CONNECTED_HORIZ * fHoriz + - ROOK_CONNECTED_VERT * FLIP(fHoriz)), - "rooks connected"); - } + // + // King-ring x-ray, replacing the old chain's attack-bit population: + // only pay for an extra magic probe when this rook already directly + // attacks the enemy queen or king square (bbEnemyGEHere, already + // computed above for mobility -- free to test). bbEnemyGEHere has + // at most two bits (queen, king), each the terminal blocker of a + // *different* ray -- masking both off at once and re-probing in a + // single magic lookup gives the same union a per-blocker loop would + // (one ray's blocker removal can't affect the other ray's result). + // No deeper chaining needed either way, since the only consumer + // (_EvalKingSafety) only ever ANDs this against the king's ring, + // never walks further. + // + bbXray = bbEnemyGEHere ? + _RookAttacksBB(c, pos->bbOccupied & ~bbEnemyGEHere) : 0; + pos->bbRookXrayAttacks[uColor] |= bbXray; - bbContinueHere = bbFriendRQHere | bbEnemyGEHere; - if (!bbContinueHere) - { - break; - } - bbExclude |= bbContinueHere; - bbLayer = _RookAttacksBB(c, pos->bbOccupied & ~bbExclude); - } - pos->bbRookXrayAttacks[uColor] |= bbXrayAccum; + // Pseudo-mobility bonus for squares seen past the queen/king blocker + // -- see ROOK_XRAY_MOBILITY_BY_SQUARES's comment, same reasoning as + // bishop's identical term. + EVAL_TERM(uColor, + ROOK, + c, + pos->iScore[uColor], + ROOK_XRAY_MOBILITY_BY_SQUARES[ + MINU(FastCountBits(bbXray & ~bbAttack), 8)], + "xray pseudo-mobility"); uTotalMobility = FastCountBits(bbMobility); ASSERT(uTotalMobility <= 14); @@ -5060,7 +4966,9 @@ Return value: static void _EvalQueen(IN OUT POSITION *pos, IN COOR c, - IN PAWN_HASH_ENTRY *pHash) + IN PAWN_HASH_ENTRY *pHash, + IN BITBOARD bbEnemyQK, + IN BITBOARD bbUnsafeForQueen) /** Parameters: @@ -5068,6 +4976,13 @@ Parameters: POSITION *pos, COOR c, PAWN_HASH_ENTRY *pHash, + BITBOARD bbEnemyQK -- enemy queen|king, precomputed once per color by + Eval()'s driver -- identical formula to _EvalRook's bbEnemyQK, so + the same precomputed value is reused for both (see + MOBILITY_BITBOARD_REWRITE.md). + BITBOARD bbUnsafeForQueen -- precomputed once per color right before + the queen loops start, valid because both colors' pawn/minor/rook + passes are already complete by then. Return value: @@ -5079,113 +4994,54 @@ Return value: ULONG uColor; ULONG xColor; ULONG uTotalMobility; - ULONG u; - COOR cKing; ASSERT(IS_ON_BOARD(c)); ASSERT(p && IS_QUEEN(p)); uColor = GET_COLOR(p); ASSERT(IS_VALID_COLOR(uColor)); xColor = FLIP(uColor); - cKing = KingPosition(pos, xColor); - ASSERT(IS_ON_BOARD(cKing)); - ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); + ASSERT(IS_ON_BOARD(KingPosition(pos, xColor))); + ASSERT(IS_KING(pos->rgSquare[KingPosition(pos, xColor)].pPiece)); // - // Do queen mobility (and update attack tables) -- two-pass - // rook-direction/bishop-direction magic lookups, board_ - // representation/EVAL.md section 1b/9: MOVEGEN_MIGRATION.md - // already found (in the stashed first bitboard-eval attempt's - // _EvalQueenOccupancyBB PoC) that a combined 8-ray table measures - // *slower* than reusing the rook/bishop tables in two passes -- - // reuse that structure here too, don't rediscover the regression. - // Each pass is its own unified chain walk, identical technique to - // rook's/bishop's own conversions (including their fix for - // crediting mobility beyond a battery partner, not just x-ray - // attack bits): continue through a friendly queen in both passes, - // a friendly rook only in the rook-direction pass, a friendly - // bishop only in the bishop-direction pass (QMOB_FRIEND_ROOK/ - // _BISHOP's old per-ray-family split falls out for free from - // *which* lookup a blocker shows up in -- no fOrthogonalRay flag - // needed). Unlike rook/bishop, queen never x-rays through *any* - // enemy piece (QMOB_ENEMY_GE always stopped in the old table, the - // one case that's a *fewer*-cases asymmetry versus bishop, not - // more), so the enemy side of each pass's continue-set is empty. + // Queen mobility (and update attack tables) -- two single magic + // probes (rook-direction + bishop-direction), unioned, then one + // shared mask pass. No chain walk (see MOBILITY_BITBOARD_REWRITE.md): + // - terminal enemy queen/king: counts unconditionally -- a capture + // is always "real" mobility. + // - empty or terminal enemy pawn/minor/rook: counts unless the + // square is attacked by an enemy pawn, minor, rook, or (modulo + // our own defense) the enemy king -- same partition + // bishop/knight/rook's reductions use, just with a bigger + // "cheaper piece" set since the queen is the most valuable. + // - any friendly piece: blocks, no count. // - BITBOARD bbEnemyOcc = _BuildFriendlySideBB(pos, xColor); - BITBOARD bbEnemyGE = pos->bbPieces[xColor][QUEEN] | - COOR_TO_BB(cKing); - // - // Enemy king attacks are also unsafe for the queen -- a free - // O(1) table lookup by king square, independent of eval order - // (unlike the enemy minor/rook attacks above, which do need - // both colors' knight/bishop/rook passes to finish first, and - // already have by queen's turn: see the eval call order in - // Eval() below). Only unsafe when the queen wouldn't be - // recaptured by something of our own first -- if a friendly - // pawn/minor/rook already covers that square, the king can't - // just take the queen for free. Every one of those tables (ours - // AND the enemy's) is fully populated by now: pawns, minors, - // and rooks all run their own eval pass before queen does. + // Deliberately NOT replicated: mobility credit for squares beyond a + // friendly rook/bishop/queen battery partner -- a queen cannot + // physically move past a piece it shares a square with in one ply, + // so (same as bishop/rook) that was never "real mobility" to begin + // with. Also NOT replicated, and needing no replacement: the old + // code's x-ray-attack population. Unlike bishop/rook, queen's old + // chain only ever continued through a FRIENDLY blocker (it never + // x-rayed through any enemy piece at all -- QMOB_ENEMY_GE always + // stopped the ray in the original mailbox table). By the same + // redundancy argument used to drop bishop/rook's friendly-blocker + // x-ray case, that information was always a duplicate of the + // blocking piece's own direct-attack pass -- there's no enemy-GE + // case here to replace, so _EvalKingSafety's queen-x-ray reads + // (pos->bbQueenXrayAttacks[xColor]) see nothing lost by simply never + // populating it. // - BITBOARD bbUnsafeForQueen = pos->bbPawnAttacks[xColor] | - pos->bbMinorAttacks[xColor] | - pos->bbRookAttacks[xColor] | - (g_KingAttacksBB[cKing] & - ~(pos->bbPawnAttacks[uColor] | - pos->bbMinorAttacks[uColor] | - pos->bbRookAttacks[uColor])); - BITBOARD bbAttackTotal = 0; - BITBOARD bbXrayAccum = 0; - BITBOARD bbMobility = 0; - - ULONG uPass; - for (uPass = 0; uPass < 2; uPass++) - { - BITBOARD bbFriendContinue = (uPass == 0) ? - (pos->bbPieces[uColor][ROOK] | pos->bbPieces[uColor][QUEEN]) : - (pos->bbPieces[uColor][BISHOP] | pos->bbPieces[uColor][QUEEN]); - BITBOARD bbInitial = (uPass == 0) ? - _RookAttacksBB(c, pos->bbOccupied) : - _BishopAttacksBB(c, pos->bbOccupied); - BITBOARD bbExclude = 0; - BITBOARD bbSeen = 0; - BITBOARD bbLayer = bbInitial; - BITBOARD bbFirstLayerMask = 0; - - bbAttackTotal |= bbInitial; - - for (;;) - { - BITBOARD bbNew = bbLayer & ~bbSeen; - BITBOARD bbBlockersHere = bbNew & pos->bbOccupied; - BITBOARD bbEmptyHere = bbNew & ~pos->bbOccupied; - BITBOARD bbFriendContinueHere = bbBlockersHere & - bbFriendContinue; - BITBOARD bbEnemyGEHere = bbBlockersHere & bbEnemyGE; - BITBOARD bbEnemyLEHere = bbBlockersHere & bbEnemyOcc & - ~bbEnemyGEHere; - - bbSeen |= bbNew; - bbXrayAccum |= (bbNew & bbFirstLayerMask); - bbFirstLayerMask = ~(BITBOARD)0; - - bbMobility |= (bbEmptyHere & ~bbUnsafeForQueen) | - bbEnemyGEHere | - (bbEnemyLEHere & ~bbUnsafeForQueen); + BITBOARD bbEnemyOcc = _BuildFriendlySideBB(pos, xColor); + BITBOARD bbAttackTotal = _RookAttacksBB(c, pos->bbOccupied) | + _BishopAttacksBB(c, pos->bbOccupied); + BITBOARD bbEnemyGEHere = bbAttackTotal & bbEnemyQK; + BITBOARD bbEnemyLEHere = bbAttackTotal & bbEnemyOcc & ~bbEnemyGEHere; + BITBOARD bbMobility = (bbAttackTotal & ~pos->bbOccupied & ~bbUnsafeForQueen) | + bbEnemyGEHere | + (bbEnemyLEHere & ~bbUnsafeForQueen); - if (!bbFriendContinueHere) - { - break; - } - bbExclude |= bbFriendContinueHere; - bbLayer = (uPass == 0) ? - _RookAttacksBB(c, pos->bbOccupied & ~bbExclude) : - _BishopAttacksBB(c, pos->bbOccupied & ~bbExclude); - } - } pos->bbQueenAttacks[uColor] |= bbAttackTotal; - pos->bbQueenXrayAttacks[uColor] |= bbXrayAccum; uTotalMobility = FastCountBits(bbMobility); ASSERT(uTotalMobility <= 27); @@ -5246,10 +5102,6 @@ Return value: { BITV bvPattern; ULONG uQueenNearKing; -#ifdef KING_DEFECT_DUMP - COOR cSquare; - KING_DEFECT_DUMP_INFO *pDump = &ctx->sKingDefectDump[uColor]; -#endif SCORE iKingScore = 0; // 2026-09-15: linear-SCORE rewrite -- see king_safety_data/ @@ -5378,105 +5230,9 @@ Return value: iSafety += KING_BACK_FLIGHT_BONUS * (SCORE)FastCountBits(bbBack & bbEmpty & ~bbEnemyAtkAny); -#ifdef KING_DEFECT_DUMP - { - // Debug-only, per-square breakdown for the mining pipeline - // (extract_king_features.py) -- kept fully independent of - // the bitboard computation above, same traversal shape as - // the pre-rewrite loop so king_features_v4.csv's column - // semantics stay unchanged. Does not feed iSafety. - static INT KingSafetyDeltas[9] = - { - -17, -16, -15, - -1, +1, - +15, +16, +17, 0 - }; - ULONG uSlot = 0; - BITV bvSlotAttack, bvSlotXray, bvSlotDefend; - - do - { - cSquare = c + KingSafetyDeltas[uSlot]; - if (IS_ON_BOARD(cSquare)) - { - BITBOARD sq = COOR_TO_BB(cSquare); - BITV bvEither; - - bvSlotAttack = ((pos->bbPawnAttacks[xColor] & sq) ? PAWN_BIT : 0) | - ((pos->bbMinorAttacks[xColor] & sq) ? MINOR_BIT : 0) | - ((pos->bbRookAttacks[xColor] & sq) ? ROOK_BIT : 0) | - ((pos->bbQueenAttacks[xColor] & sq) ? QUEEN_BIT : 0); - bvSlotXray = ((pos->bbMinorXrayAttacks[xColor] & sq) ? MINOR_BIT : 0) | - ((pos->bbRookXrayAttacks[xColor] & sq) ? ROOK_BIT : 0) | - ((pos->bbQueenXrayAttacks[xColor] & sq) ? QUEEN_BIT : 0); - bvSlotDefend = ((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); - - bvEither = bvSlotAttack | bvSlotXray; - ASSERT(uSlot < KING_RING_NUM_SLOTS); - pDump->fOnBoard[uSlot] = TRUE; - pDump->fAttackedByPieceType[uSlot][KING_RING_PAWN] = - ((bvEither & PAWN_BIT) != 0); - pDump->fAttackedByPieceType[uSlot][KING_RING_MINOR] = - ((bvEither & MINOR_BIT) != 0); - pDump->fAttackedByPieceType[uSlot][KING_RING_ROOK] = - ((bvEither & ROOK_BIT) != 0); - pDump->fAttackedByPieceType[uSlot][KING_RING_QUEEN] = - ((bvEither & QUEEN_BIT) != 0); - if (bvSlotAttack != 0) - { - BITV bvDefendForDump = bvSlotDefend; - if (bvSlotXray || (bvSlotAttack & (bvSlotAttack - 1))) - { - bvDefendForDump &= ~KING_BIT; - } - if (bvDefendForDump == 0) - { - pDump->fAttackedUndefended[uSlot] = TRUE; - } - else - { - pDump->fAttackedDefended[uSlot] = TRUE; - } - } - else - { - if (bvSlotXray != 0) - { - pDump->fXrayedUnattacked[uSlot] = TRUE; - } - if (!(pos->bbOccupied & sq)) - { - pDump->fEmptyFlight[uSlot] = TRUE; - } - } - } - uSlot++; - } - while (KingSafetyDeltas[uSlot] != 0); - } -#endif - - // // _ComputeFileStormDefects's comment. - // iSafety += _GetFileStormDefects(ctx, pos, pHash, uColor); -#ifdef KING_DEFECT_DUMP - _ComputeFileStormDefectsSplit(pos, pHash, uColor, - &pDump->uFileStormOpenFile, - &pDump->uFileStormStormingPawn); - pDump->uFileStormDefects = pDump->uFileStormOpenFile + - pDump->uFileStormStormingPawn; -#endif - iSafety += KING_INITIAL_LOCATION_SCALE * KING_EVAL_INITIAL_LOCATION[uColor][c]; -#ifdef KING_DEFECT_DUMP - pDump->uInitialCounterByLocation = - (ULONG)KING_EVAL_INITIAL_LOCATION[uColor][c]; -#endif // Danger-magnitude acceleration -- see KING_DANGER_ACCELERATOR's // comment above. Applied here (as part of the real per-king @@ -5513,21 +5269,6 @@ Return value: iSafety, "king safety"); -#ifdef KING_DEFECT_DUMP - pDump->iRealSafety = iSafety; - pDump->uQueenNearKing = uQueenNearKing; - // uCounterTotal/uSafetyTableValue no longer have a coherent - // meaning post-rewrite (no more shared uCounter/KING_SAFETY_ - // BY_COUNTER lookup) -- left zeroed by the memset above rather - // than populated with something misleading. Both were REFERENCE- - // only columns in the training pipeline (train_king_safety_ - // model.py explicitly excludes them from the feature set), so - // this doesn't affect the regression itself. - pDump->fCastled = pos->fCastled[uColor]; - pDump->uMinorsAtHome = pos->uMinorsAtHome[uColor]; - pDump->uAttackerArmyScaler = pos->uArmyScaler[xColor]; -#endif - // Bonus for castling / penalty for loss of castle. Also, if // side has not yet castled AND castling is still a live // option, be concerned with undeveloped minor pieces. @@ -5551,56 +5292,51 @@ Return value: // copy-pasted blocks -- one fewer per-piece branch for the // processor to mispredict (board_representation/EVAL.md // section 9). - if (FALSE == pos->fCastled[uColor]) + ULONG uDevelopmentScaler = CASTLE_AND_DEVELOPMENT_SCALER[pos->uArmyScaler[uColor]]; + if (uDevelopmentScaler > 0) { - ULONG uDevelopmentScaler = CASTLE_AND_DEVELOPMENT_SCALER[pos->uArmyScaler[uColor]]; - if (uDevelopmentScaler > 0) - { - static const BITV CASTLE_SHORT[2] = - { CASTLE_BLACK_SHORT, CASTLE_WHITE_SHORT }; - static const BITV CASTLE_LONG[2] = - { CASTLE_BLACK_LONG, CASTLE_WHITE_LONG }; - BITV bvBothRights = CASTLE_SHORT[uColor] | CASTLE_LONG[uColor]; - // One popcount answers both "can this side still - // castle at all" (>0, gates development below) and - // "how many options are missing" (2-count, scales - // the persistent penalty below) -- replaces two - // separate (==0) branches/multiplies/EVAL_TERM calls - // with one shared count. - ULONG uRightsRemaining = - FastCountBits(pos->bvCastleInfo & bvBothRights); - ASSERT(uRightsRemaining <= 2); + static const BITV CASTLE_SHORT[2] = + { CASTLE_BLACK_SHORT, CASTLE_WHITE_SHORT }; + static const BITV CASTLE_LONG[2] = + { CASTLE_BLACK_LONG, CASTLE_WHITE_LONG }; + static const BITV bvBothRights[2] = + { CASTLE_SHORT[BLACK] | CASTLE_LONG[BLACK], + CASTLE_SHORT[WHITE] | CASTLE_LONG[WHITE] }; - // Branchless: (uRightsRemaining > 0) is 0 or 1, multiplied - // straight into the term instead of an `if` -- nothing - // left to mispredict. - ASSERT(pos->uMinorsAtHome[uColor] <= 4); - EVAL_TERM(uColor, - 0, - ILLEGAL_COOR, - pos->iScore[uColor], - (SCORE)((UNDEVELOPED_MINORS_IN_OPENING[pos->uMinorsAtHome[uColor]] * - (SCORE)uDevelopmentScaler) / 8), - "development"); + ASSERT(pos->uMinorsAtHome[uColor] <= 4); + EVAL_TERM(uColor, + 0, + ILLEGAL_COOR, + pos->iScore[uColor], + (SCORE)((UNDEVELOPED_MINORS_IN_OPENING[pos->uMinorsAtHome[uColor]] * + (SCORE)uDevelopmentScaler) / 8), + "development"); - // Deliberately NOT gated on uRightsRemaining > 0 -- - // see this block's header comment. Fires at full - // weight (both options gone) even long after the - // rights were lost, for as long as uDevelopmentScaler - // itself stays nonzero. - EVAL_TERM(uColor, - KING, - c, - iKingScore, - (SCORE)((KING_MISSING_ONE_CASTLE_OPTION * - (SCORE)(2 - uRightsRemaining) * - (SCORE)uDevelopmentScaler) / 8), - "missing castle option(s)"); -#ifdef KING_DEFECT_DUMP - pDump->fMissingCastleShort = ((CASTLE_SHORT[uColor] & pos->bvCastleInfo) == 0); - pDump->fMissingCastleLong = ((CASTLE_LONG[uColor] & pos->bvCastleInfo) == 0); -#endif - } + // One popcount answers both "can this side still + // castle at all" (>0, gates development below) and + // "how many options are missing" (2-count, scales + // the persistent penalty below) -- replaces two + // separate (==0) branches/multiplies/EVAL_TERM calls + // with one shared count. + ULONG uRightsRemaining = + FastCountBits(pos->bvCastleInfo & bvBothRights[uColor]); + ASSERT(uRightsRemaining <= 2); + + // Deliberately NOT gated on uRightsRemaining > 0 -- + // see this block's header comment. Fires at full + // weight (both options gone) even long after the + // rights were lost, for as long as uDevelopmentScaler + // itself stays nonzero. + EVAL_TERM(uColor, + KING, + c, + iKingScore, + (SCORE)( + (FALSE == pos->fCastled[uColor]) * + (KING_MISSING_ONE_CASTLE_OPTION * + (SCORE)(2 - uRightsRemaining) * + (SCORE)uDevelopmentScaler) / 8), + "missing castle option(s)"); } return iKingScore; } @@ -5756,12 +5492,6 @@ Return value: // Update attack bitvs for squares around the king. BITBOARD bbRing = g_KingAttacksBB[c]; pos->bbKingAttacks[uColor] |= bbRing; -#ifdef KING_DEFECT_DUMP - { - KING_DEFECT_DUMP_INFO *pDump = &ctx->sKingDefectDump[uColor]; - memset(pDump, 0, sizeof(KING_DEFECT_DUMP_INFO)); - } -#endif // Safety-vs-activity split -- see this function's header comment // (2026-10-06 restructuring). Don't bother with king safety when @@ -7883,6 +7613,29 @@ Return value: // Evaluate individual pieces. // + // Precomputed once per color instead of rebuilt by every bishop/ + // rook/queen of that color (see MOBILITY_BITBOARD_REWRITE.md's + // "squeeze it" follow-up). bbEnemyRQK/bbEnemyQK depend only on + // static piece locations -- no ordering dependency, safe to build + // right here before any piece loop runs. bbUnsafeForRook/ + // bbUnsafeForQueen are filled in below, at the loop boundary where + // their inputs are guaranteed complete (both colors' knight+bishop + // passes before rook; both colors' pawn/minor/rook passes before + // queen) -- NOT incrementally mutated during a loop, which would + // let one piece's own attack leak into its same-color sibling's + // "unsafe" mask. + BITBOARD bbEnemyRQK[2]; + BITBOARD bbEnemyQK[2]; + BITBOARD bbUnsafeForRook[2]; + BITBOARD bbUnsafeForQueen[2]; + + bbEnemyQK[WHITE] = pos->bbPieces[WHITE][QUEEN] | + COOR_TO_BB(pos->cKingSq[WHITE]); + bbEnemyQK[BLACK] = pos->bbPieces[BLACK][QUEEN] | + COOR_TO_BB(pos->cKingSq[BLACK]); + bbEnemyRQK[WHITE] = bbEnemyQK[WHITE] | pos->bbPieces[WHITE][ROOK]; + bbEnemyRQK[BLACK] = bbEnemyQK[BLACK] | pos->bbPieces[BLACK][ROOK]; + // Knights. bb = pos->bbPieces[uColor][KNIGHT]; while (bb) @@ -7930,7 +7683,7 @@ Return value: bb &= (bb - 1); c = BIT_NUMBER_TO_COOR(u); TIMED_EVAL_CALL(ctx, u64CyclesEvalBishop, - _EvalBishop(pos, c, pHash)); + _EvalBishop(pos, c, pHash, bbEnemyRQK[xColor])); #ifdef EVAL_DUMP Trace("After our B at %s:\n%d\t\t%d\t\t(%d for %s)\n", CoorToString(c), @@ -7949,7 +7702,7 @@ Return value: bb &= (bb - 1); c = BIT_NUMBER_TO_COOR(u); TIMED_EVAL_CALL(ctx, u64CyclesEvalBishop, - _EvalBishop(pos, c, pHash)); + _EvalBishop(pos, c, pHash, bbEnemyRQK[uColor])); #ifdef EVAL_DUMP Trace("After enemy B at %s:\n%d\t\t%d\t\t(%d for %s)\n", CoorToString(c), @@ -7960,6 +7713,14 @@ Return value: #endif } + // Both colors' knight+bishop passes are complete now -- safe to + // build both colors' "unsafe for rook" masks once, here, instead of + // every rook rebuilding its own color's mask from scratch. + bbUnsafeForRook[WHITE] = pos->bbPawnAttacks[BLACK] | + pos->bbMinorAttacks[BLACK]; + bbUnsafeForRook[BLACK] = pos->bbPawnAttacks[WHITE] | + pos->bbMinorAttacks[WHITE]; + // Rooks. bb = pos->bbPieces[uColor][ROOK]; while (bb) @@ -7967,7 +7728,9 @@ Return value: u = FastFirstBit(bb) - 1; bb &= (bb - 1); c = BIT_NUMBER_TO_COOR(u); - TIMED_EVAL_CALL(ctx, u64CyclesEvalRook, _EvalRook(pos, c, pHash)); + TIMED_EVAL_CALL(ctx, u64CyclesEvalRook, + _EvalRook(pos, c, pHash, bbEnemyQK[xColor], + bbUnsafeForRook[uColor])); #ifdef EVAL_DUMP Trace("After our R at %s:\n%d\t\t%d\t\t(%d for %s)\n", CoorToString(c), @@ -7985,7 +7748,9 @@ Return value: u = FastFirstBit(bb) - 1; bb &= (bb - 1); c = BIT_NUMBER_TO_COOR(u); - TIMED_EVAL_CALL(ctx, u64CyclesEvalRook, _EvalRook(pos, c, pHash)); + TIMED_EVAL_CALL(ctx, u64CyclesEvalRook, + _EvalRook(pos, c, pHash, bbEnemyQK[uColor], + bbUnsafeForRook[xColor])); #ifdef EVAL_DUMP Trace("After enemy R at %s:\n%d\t\t%d\t\t(%d for %s)\n", CoorToString(c), @@ -7996,6 +7761,27 @@ Return value: #endif } + // Both colors' pawn/minor/rook passes are complete now -- safe to + // build both colors' "unsafe for queen" masks once, here, instead + // of every queen rebuilding its own color's mask from scratch (most + // positions only have one queen per side anyway, so this mainly + // pays off on promoted-extra-queen positions, but it's free either + // way). + bbUnsafeForQueen[WHITE] = pos->bbPawnAttacks[BLACK] | + pos->bbMinorAttacks[BLACK] | + pos->bbRookAttacks[BLACK] | + (g_KingAttacksBB[pos->cKingSq[BLACK]] & + ~(pos->bbPawnAttacks[WHITE] | + pos->bbMinorAttacks[WHITE] | + pos->bbRookAttacks[WHITE])); + bbUnsafeForQueen[BLACK] = pos->bbPawnAttacks[WHITE] | + pos->bbMinorAttacks[WHITE] | + pos->bbRookAttacks[WHITE] | + (g_KingAttacksBB[pos->cKingSq[WHITE]] & + ~(pos->bbPawnAttacks[BLACK] | + pos->bbMinorAttacks[BLACK] | + pos->bbRookAttacks[BLACK])); + // Our queen(s). bb = pos->bbPieces[uColor][QUEEN]; while (bb) @@ -8003,7 +7789,9 @@ Return value: u = FastFirstBit(bb) - 1; bb &= (bb - 1); c = BIT_NUMBER_TO_COOR(u); - TIMED_EVAL_CALL(ctx, u64CyclesEvalQueen, _EvalQueen(pos, c, pHash)); + TIMED_EVAL_CALL(ctx, u64CyclesEvalQueen, + _EvalQueen(pos, c, pHash, bbEnemyQK[xColor], + bbUnsafeForQueen[uColor])); #ifdef EVAL_DUMP Trace("After our Q at %s:\n%d\t\t%d\t\t(%d for %s)\n", CoorToString(c), @@ -8021,7 +7809,9 @@ Return value: u = FastFirstBit(bb) - 1; bb &= (bb - 1); c = BIT_NUMBER_TO_COOR(u); - TIMED_EVAL_CALL(ctx, u64CyclesEvalQueen, _EvalQueen(pos, c, pHash)); + TIMED_EVAL_CALL(ctx, u64CyclesEvalQueen, + _EvalQueen(pos, c, pHash, bbEnemyQK[uColor], + bbUnsafeForQueen[xColor])); #ifdef EVAL_DUMP Trace("After enemy Q at %s:\n%d\t\t%d\t\t(%d for %s)\n", CoorToString(c), |
