diff options
| author | Scott Gasch <[email protected]> | 2026-09-22 13:04:39 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-22 13:04:39 -0700 |
| commit | b7e4cdfeb3c1aadb3f488f6794e6d1a748dc6c8f (patch) | |
| tree | ffc5bb4e2a4a4d9d027df38c9619739a3dbb478e /src/chess.h | |
| parent | f422daab223103bbd62a5611be0b73eaa6a5dc05 (diff) | |
eval.c: defer B/N/R mobility scoring near enemy king, queen king-adjacency fix, FLIP() -> precomputed xColor
Bishop/knight/rook mobility computed during their own eval pass only
sees enemy pawn (B/N) or pawn+minor (R) attacks -- a square right next
to the enemy king looks "safe" and counts toward mobility even though
it plainly isn't (e.g. a pawn push cutting off a bishop's retreat onto
squares next to the enemy king). Below EVAL_MOBILITY_CAREFULLY_BELOW
(3) candidate squares, defer scoring to _EvalTrappedPieces, which
re-derives the true safe-escape count once every piece type -- the
enemy king included -- has contributed to the attack tables. Genuinely
0-mobility pieces are scored immediately instead: more attack info can
only shrink an already-empty set, so there's nothing to gain by
waiting.
Queen doesn't need deferral (its own eval pass runs after every
smaller piece type, so it already has complete information) -- instead
bbUnsafeForQueen now also excludes squares adjacent to the enemy king
unless we already defend them.
Also: precompute xColor = FLIP(uColor) once per function/loop
iteration everywhere eval.c could hit FLIP() more than once evaluating
it, instead of recomputing it at each use site.
Verified: precommit_check.sh (self-test + DEBUG smoke test) clean.
Curated ECM suites at sd10 vs head_reference: ecm_ringers 4/11 (was
3/11), ecm_confident_quick 70/90 (flat), ecm_hard_quick 19/90 (was
18/90) -- no regressions. Overnight self-play SPRT trending positive
(370 games, llr=+0.20).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/chess.h')
| -rwxr-xr-x | src/chess.h | 37 |
1 files changed, 27 insertions, 10 deletions
diff --git a/src/chess.h b/src/chess.h index 1dcb517..8d9a8d8 100755 --- a/src/chess.h +++ b/src/chess.h @@ -749,16 +749,33 @@ typedef struct _POSITION // temporary storage space for use in eval // - // Small fixed-capacity list of zero-mobility piece candidates per - // color (bishop/knight/rook/queen evals each can push one), not - // just the single most-recently-evaluated one -- a single COOR - // here used to silently let a later piece's candidacy overwrite an - // earlier one on the same side, discarding it even if it was a - // genuinely trapped (zero mobility + actually attacked) piece. - // Capacity of 4 is deliberately generous; if a position somehow - // has more than 4 simultaneously zero-mobility non-pawn pieces on - // one side, the rest are just dropped, not a real concern. - COOR cTrapped[2][4]; + // Small fixed-capacity list of zero-mobility (or, for a deferred + // bishop/knight/rook -- see EVAL_MOBILITY_CAREFULLY_BELOW in + // eval.c -- low-mobility) piece candidates per color, not just the + // single most-recently-evaluated one -- a single COOR here used to + // silently let a later piece's candidacy overwrite an earlier one + // on the same side, discarding it even if it was a genuinely + // trapped (zero mobility + actually attacked) piece. Capacity of 6 + // is deliberately generous; if a position somehow has more than 6 + // simultaneously trapped-candidate non-pawn pieces on one side, the + // rest are just dropped, not a real concern. + // + // bbTrappedEscapes[side][u] is the nominating bishop/knight/rook's + // own bbMobility snapshot at nomination time, for a genuinely + // deferred candidate (see eval.c's _EvalBishop/_EvalKnight/ + // _EvalRook) -- necessarily computed from incomplete attack-table + // information (the enemy king's, and for bishop/knight any + // not-yet-evaluated enemy slider's, attacks aren't known yet). Zero + // for every non-deferred candidate (queen, which never has this + // partial-information problem in the first place, or a + // bishop/knight/rook nominated at already-zero mobility -- nothing + // left to re-derive there, see eval.c's comment on why 0-mobility + // pieces are never deferred) -- _EvalTrappedPieces uses this being + // nonzero as the signal that a deferred re-check (and its own + // "mobility (deferred)" EVAL_TERM, skipped at nomination time) is + // owed to this candidate. + COOR cTrapped[2][6]; + BITBOARD bbTrappedEscapes[2][6]; ULONG uNumTrapped[2]; ULONG uArmyScaler[2]; ULONG uClosedScaler; |
