diff options
| author | Scott Gasch <[email protected]> | 2026-09-05 11:11:58 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-05 11:11:58 -0700 |
| commit | 53694883d63674a97a3960514dd4e0a4e1b67f01 (patch) | |
| tree | daac5cb340a1395bd2b91182b2cbe691dbf9bded /src | |
| parent | 5405191f8c519333006417a5a3c31bc3e186e42e (diff) | |
Fix _EvaluateCandidatePasser's helper-pawn safety gate (real no-op since 57502d6)
Flagged during the ATTACK_BITV/bvAttacks cleanup (5405191) but left
unfixed there deliberately, per direct instruction, since a real fix
changes eval scoring and deserves its own before/after check rather
than being buried in a mechanical rename commit.
The gate ("is this square safe to advance a helper pawn into, i.e.
not enemy-attacked or already friend-defended") used to read the old
combined bvAttacks word for both colors at the target square. This
function runs from _EvalPawns, the first piece type Eval() evaluates
each call -- no non-pawn piece (and, since commit 57502d6, not even
pawns themselves) has written any attack data yet at this point. That
word has therefore been unconditionally zero, and the gate
unconditionally true (silently disabled), since 57502d6 landed.
Fixed using pos->bbPawnAttacks -- the one piece-type bitboard that
actually is valid this early in Eval()'s sequence (populated at the
top of _EvalPawns, before this function runs). Narrower than whatever
the original gate covered (pawns only, not every piece type), but a
real, correct check instead of a fake one, and pawns are the dominant
real-world case for contesting a helper-pawn's advance square anyway.
Applied to both the right- and left-side helper searches (the initial
target-square check and the backward walk-and-search loop each need
their own copy, since the loop's own square changes every iteration).
Verified via precommit_check.sh, then all three curated suites at
sd10 (not just ringers, since this is a real eval-scoring change, not
a mechanical one): net +2/191 (113->115), concentrated in
ecm_hard_quick (18->22) with a small ecm_confident_quick give-back
(85->83) and ringers unchanged (10/11) -- the same "gains lopsided
toward hard_quick" shape this session's other real eval changes have
shown, consistent with a genuine (if modest) positional improvement
rather than suite-specific noise.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01XxmVi2sTMwpPp4i6WYFjan
Diffstat (limited to 'src')
| -rwxr-xr-x | src/eval.c | 126 |
1 files changed, 69 insertions, 57 deletions
@@ -1475,23 +1475,23 @@ 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. + // FIXED 2026-09-05 (found while removing rgSquare[c|8].bvAttacks + // entirely, board_representation/EVAL.md section 9; landed as its + // own change with its own before/after check, not bundled into + // that mechanical cleanup commit). This function runs from + // _EvalPawns, which is the *first* piece type evaluated each + // Eval() call, so the only attack data that actually exists yet is + // pos->bbPawnAttacks -- every non-pawn piece, and (since commit + // 57502d6) pawns' own old bvAttacks write, happens later in the + // same call. The original condition here read the old combined + // bvAttacks word (all piece types), which had been silently + // always-zero -- and this gate silently always-true -- since + // 57502d6; narrowed to what's actually valid at this point: + // pos->bbPawnAttacks only. This is narrower than the original + // presumably intended (any piece type, not just pawns), but it's + // a real, correct check instead of a fake one, and pawns are the + // dominant real-world case for contesting a helper-pawn square + // anyway. // uHelpers = 0; d1 = 16 * g_iAhead[uColor]; @@ -1500,33 +1500,39 @@ Return value: if ((IS_ON_BOARD(c1)) && (pHash->uCountPerFile[uColor][FILE(c1) + 1])) { ASSERT(pos->bbPawns[uColor] & BBFILE[FILE(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; - } - - // - // There is no helper pawn in the support position yet. - // See if one can get there. - // - c1 = c1 - d1; - while (IS_ON_BOARD(c1)) + if (!(pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(c1)) || + (pos->bbPawnAttacks[uColor] & COOR_TO_BB(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; - break; + goto do_left; } - else if (pos->rgSquare[c1].pPiece == pSentry) + + // + // 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->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(c1))) || + (pos->bbPawnAttacks[uColor] & COOR_TO_BB(c1)))) { - break; + if (pos->rgSquare[c1].pPiece == pHelper) + { + uHelpers = 1; + break; + } + else if (pos->rgSquare[c1].pPiece == pSentry) + { + break; + } + c1 = c1 - d1; } - c1 = c1 - d1; } } @@ -1536,33 +1542,39 @@ Return value: { ASSERT(pos->bbPawns[uColor] & BBFILE[FILE(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; - } - - // - // There is no pawn in the left support position yet. See - // if one can get there. - // - c1 -= d1; - while (IS_ON_BOARD(c1)) + if (!(pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(c1)) || + (pos->bbPawnAttacks[uColor] & COOR_TO_BB(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++; - break; + goto done_helpers; } - else if (pos->rgSquare[c1].pPiece == pSentry) + + // + // There is no pawn in the left support position yet. See + // if one can get there. + // + c1 -= d1; + while (IS_ON_BOARD(c1) && + ((!(pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(c1))) || + (pos->bbPawnAttacks[uColor] & COOR_TO_BB(c1)))) { - break; + if (pos->rgSquare[c1].pPiece == pHelper) + { + uHelpers++; + break; + } + else if (pos->rgSquare[c1].pPiece == pSentry) + { + break; + } + c1 -= d1; } - c1 -= d1; } } |
