summaryrefslogtreecommitdiff
path: root/src/eval.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-05 11:11:58 -0700
committerScott Gasch <[email protected]>2026-09-05 11:11:58 -0700
commit53694883d63674a97a3960514dd4e0a4e1b67f01 (patch)
treedaac5cb340a1395bd2b91182b2cbe691dbf9bded /src/eval.c
parent5405191f8c519333006417a5a3c31bc3e186e42e (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/eval.c')
-rwxr-xr-xsrc/eval.c126
1 files changed, 69 insertions, 57 deletions
diff --git a/src/eval.c b/src/eval.c
index 1ca2beb..84e001e 100755
--- a/src/eval.c
+++ b/src/eval.c
@@ -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;
}
}