summaryrefslogtreecommitdiff
path: root/src/eval.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-20 09:26:44 -0700
committerScott Gasch <[email protected]>2026-10-20 09:26:44 -0700
commitc8c6bbc2c1a103e5b07b079a0f8bdf66c7724d80 (patch)
treeb6c2137c9ac577fa83b1e948fa0311d53dfe445d /src/eval.c
parent3b5d3677bb14ae14728a1c4b221176bb83667d92 (diff)
Wire NNUE eval into search, build the training pipeline, and add a fast
move/eval-agreement benchmark against Stockfish (tests/lop) Implements the architecture decisions in src/docs/NNUE.md: HalfKAv2_hm feature set, int16/int8 quantization, per-ply accumulator backup for king moves, and a lazy-eval-ready integration into search.c/eval.c. Adds the full annotate_pgns/ training pipeline (extraction, K-fit/ labeling, quiet filtering, tensor building, training, export) plus an end-to-end orchestrator (run_nnue_pipeline.py) and validation tooling (sanity_check_net.py, compare_vs_stockfish_net.py). Adds tests/lop: a 10k-position list-of-positions benchmark comparing engine eval rank and move choice against pre-computed Stockfish/lc0 references, much cheaper than a full self-play match for day-to-day progress checks. First sd10-matched self-play result against classic HCE: LLR +2.28 (H1=+2.94), -72=119+169 over 360 games, confirming the net is "smarter per node" at matched depth. A real-time (--st 4) match is currently weaker (candidate loses ~3 plies of depth to a ~3.3-3.7x nps gap against an unrecalibrated search), consistent with NNUE.md's adoption-criterion section -- pruning/reduction constants still need retuning for the new eval's score scale before a fair real-time comparison is possible. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/eval.c')
-rw-r--r--src/eval.c347
1 files changed, 322 insertions, 25 deletions
diff --git a/src/eval.c b/src/eval.c
index 76b332c..de59dd3 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -3275,6 +3275,12 @@ Return value:
static UINT64 g_u64SuperLazyEvalsByBucket[BASE_MARGIN_MAT_BUCKETS];
static UINT64 g_u64LazyEvalsByBucket[BASE_MARGIN_MAT_BUCKETS];
static UINT64 g_u64FullEvalsByBucket[BASE_MARGIN_MAT_BUCKETS];
+// Same idea, for the two NNUE exits -- added 2026-10-16 alongside the
+// EVAL_POSITIONAL_SOURCE_NNUE_FULL/NNUE_LAZY tiers, specifically to
+// answer "what fraction of real NNUE eval calls does the lazy probe
+// gate actually resolve" with a real measurement instead of a guess.
+static UINT64 g_u64NnueLazyEvalsByBucket[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_u64NnueFullEvalsByBucket[BASE_MARGIN_MAT_BUCKETS];
void
DumpEvalExitRateByBucket(void)
@@ -3324,6 +3330,29 @@ Return value:
100.0 * (double)u64L / (double)u64Total,
100.0 * (double)u64F / (double)u64Total);
}
+
+ Trace("NNUE eval exit rate by material bucket (lazy probe gate vs full "
+ "forward pass):\n");
+ for (m = 0; m < BASE_MARGIN_MAT_BUCKETS; m++)
+ {
+ UINT64 u64NL = g_u64NnueLazyEvalsByBucket[m];
+ UINT64 u64NF = g_u64NnueFullEvalsByBucket[m];
+ UINT64 u64Total = u64NL + u64NF;
+ if (0 == u64Total)
+ {
+ Trace(" bucket %u (scaler %u-%u): no samples\n",
+ m, m * BASE_MARGIN_MAT_WIDTH,
+ (m * BASE_MARGIN_MAT_WIDTH) + BASE_MARGIN_MAT_WIDTH - 1);
+ continue;
+ }
+ Trace(" bucket %u (scaler %2u-%2u): n=%12" COMPILER_LONGLONG_UNSIGNED_FORMAT
+ " nnue_lazy=%5.2f%% nnue_full=%5.2f%%\n",
+ m, m * BASE_MARGIN_MAT_WIDTH,
+ (m * BASE_MARGIN_MAT_WIDTH) + BASE_MARGIN_MAT_WIDTH - 1,
+ u64Total,
+ 100.0 * (double)u64NL / (double)u64Total,
+ 100.0 * (double)u64NF / (double)u64Total);
+ }
}
@@ -3494,6 +3523,37 @@ static UINT64 g_uSuperLazyMarginSwingSumSq[BASE_MARGIN_MAT_BUCKETS];
static UINT64 g_uSuperLazyMarginExceeded[BASE_MARGIN_MAT_BUCKETS];
static UINT64 g_uSuperLazyMarginExceededFenSamples[BASE_MARGIN_MAT_BUCKETS];
+// Same again, for the experimental NNUE lazy probe (docs/NNUE.md's
+// "Lazy-evaluation tiering plan") -- |NnueForward() - NnueProbeScore()|
+// swing, same bucketing (_MaterialBucket), same harness, own counter
+// set so this doesn't mix with either HCE tier's numbers. No real
+// margin exists for this tier yet -- this is purely measurement, not
+// gating anything -- so RecordNnueLazyMarginSafetySwing is always
+// called with an effectively-infinite iActualMarginUsed (see its call
+// site in Eval()) until a real margin is derived from this data.
+static UINT64 g_uNnueLazyMarginSwingMax[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueLazyMarginSwingCount[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueLazyMarginSwingSum[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueLazyMarginSwingHist[BASE_MARGIN_MAT_BUCKETS][SWING_HIST_BINS];
+static UINT64 g_uNnueLazyMarginSwingSumSq[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueLazyMarginExceeded[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueLazyMarginExceededFenSamples[BASE_MARGIN_MAT_BUCKETS];
+
+// Material-only "super lazy" tier -- |NnueForward() -
+// pos->iMaterialBalance[uToMove]|, no fit/regression at all (unlike
+// the accumulator probe above): VALUE_PAWN=100 means iMaterialBalance
+// is already in centipawns, the same units NnueForward returns, so
+// raw material balance is directly usable as the "center" estimate --
+// exactly how classic Eval()'s own super-lazy exit already uses it.
+// Same bucketing/harness as the lazy tier above.
+static UINT64 g_uNnueSuperLazyMarginSwingMax[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueSuperLazyMarginSwingCount[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueSuperLazyMarginSwingSum[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueSuperLazyMarginSwingHist[BASE_MARGIN_MAT_BUCKETS][SWING_HIST_BINS];
+static UINT64 g_uNnueSuperLazyMarginSwingSumSq[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueSuperLazyMarginExceeded[BASE_MARGIN_MAT_BUCKETS];
+static UINT64 g_uNnueSuperLazyMarginExceededFenSamples[BASE_MARGIN_MAT_BUCKETS];
+
static void
_RecordMarginSafetySwingInto(IN OUT UINT64 *puMax,
IN OUT UINT64 *puCount,
@@ -3616,6 +3676,84 @@ Return value:
}
+static void
+RecordNnueLazyMarginSafetySwing(IN POSITION *pos,
+ IN SCORE iSwing,
+ IN SCORE iActualMarginUsed)
+/**
+
+Routine description:
+
+ Same shape as RecordSuperLazyMarginSafetySwing, but for the
+ experimental NNUE accumulator-based lazy probe (docs/NNUE.md's
+ "Lazy-evaluation tiering plan") -- |NnueForward() -
+ NnueProbeScore()|, own counter set. No margin is actually gated on
+ yet -- iActualMarginUsed is passed as an effectively-infinite
+ sentinel from Eval() until a real margin is derived from this
+ measurement, so puExceeded stays meaningless for now; only the
+ histogram/sum/sumSq/max/count fields are real data at this stage.
+
+Parameters:
+
+ POSITION *pos
+ SCORE iSwing : >= 0
+ SCORE iActualMarginUsed : sentinel (no real margin chosen yet)
+
+Return value:
+
+ void
+
+**/
+{
+ _RecordMarginSafetySwingInto(g_uNnueLazyMarginSwingMax,
+ g_uNnueLazyMarginSwingCount,
+ g_uNnueLazyMarginSwingSum,
+ g_uNnueLazyMarginSwingSumSq,
+ g_uNnueLazyMarginSwingHist,
+ g_uNnueLazyMarginExceeded,
+ g_uNnueLazyMarginExceededFenSamples,
+ "nnue_lazy",
+ pos, iSwing, iActualMarginUsed);
+}
+
+
+static void
+RecordNnueSuperLazyMarginSafetySwing(IN POSITION *pos,
+ IN SCORE iSwing,
+ IN SCORE iActualMarginUsed)
+/**
+
+Routine description:
+
+ Same shape as RecordNnueLazyMarginSafetySwing, but for the
+ material-only "super lazy" signal (|NnueForward() -
+ pos->iMaterialBalance[uToMove]|) -- own counter set, same
+ sentinel-until-a-real-margin-exists convention.
+
+Parameters:
+
+ POSITION *pos
+ SCORE iSwing : >= 0
+ SCORE iActualMarginUsed : sentinel (no real margin chosen yet)
+
+Return value:
+
+ void
+
+**/
+{
+ _RecordMarginSafetySwingInto(g_uNnueSuperLazyMarginSwingMax,
+ g_uNnueSuperLazyMarginSwingCount,
+ g_uNnueSuperLazyMarginSwingSum,
+ g_uNnueSuperLazyMarginSwingSumSq,
+ g_uNnueSuperLazyMarginSwingHist,
+ g_uNnueSuperLazyMarginExceeded,
+ g_uNnueSuperLazyMarginExceededFenSamples,
+ "nnue_superlazy",
+ pos, iSwing, iActualMarginUsed);
+}
+
+
// Plain Newton's-method sqrt, no <math.h> -- chess.h's own VALUE_KING
// #defines INFINITY_SCORE as a board-eval sentinel, which collides with
// <math.h>'s own INFINITY_SCORE macro, so libm's sqrt() isn't available
@@ -3729,6 +3867,20 @@ Return value:
g_uSuperLazyMarginSwingSumSq,
g_uSuperLazyMarginSwingMax,
g_uSuperLazyMarginSwingHist);
+ _PersistMarginSwingHistogramToFile(f, "nnue_lazy",
+ g_uNnueLazyMarginSwingCount,
+ g_uNnueLazyMarginExceeded,
+ g_uNnueLazyMarginSwingSum,
+ g_uNnueLazyMarginSwingSumSq,
+ g_uNnueLazyMarginSwingMax,
+ g_uNnueLazyMarginSwingHist);
+ _PersistMarginSwingHistogramToFile(f, "nnue_superlazy",
+ g_uNnueSuperLazyMarginSwingCount,
+ g_uNnueSuperLazyMarginExceeded,
+ g_uNnueSuperLazyMarginSwingSum,
+ g_uNnueSuperLazyMarginSwingSumSq,
+ g_uNnueSuperLazyMarginSwingMax,
+ g_uNnueSuperLazyMarginSwingHist);
fclose(f);
Trace("Raw margin-safety histograms written to %s\n", szPath);
}
@@ -3828,6 +3980,18 @@ Return value:
g_uSuperLazyMarginSwingSum,
g_uSuperLazyMarginSwingSumSq,
g_uSuperLazyMarginExceeded);
+ _DumpMarginSafetyCalibrationInto("NNUE lazy probe margin safety",
+ g_uNnueLazyMarginSwingMax,
+ g_uNnueLazyMarginSwingCount,
+ g_uNnueLazyMarginSwingSum,
+ g_uNnueLazyMarginSwingSumSq,
+ g_uNnueLazyMarginExceeded);
+ _DumpMarginSafetyCalibrationInto("NNUE super lazy (material-only) margin safety",
+ g_uNnueSuperLazyMarginSwingMax,
+ g_uNnueSuperLazyMarginSwingCount,
+ g_uNnueSuperLazyMarginSwingSum,
+ g_uNnueSuperLazyMarginSwingSumSq,
+ g_uNnueSuperLazyMarginExceeded);
PersistMarginSafetyHistogramsToFile(szHistogramPathOverride);
}
#endif // CALIBRATE_MARGIN_SAFETY
@@ -4104,7 +4268,7 @@ static const SCORE iKingP98ByAttackerArmy[8] =
// p98/p2 pessimistic/optimistic pairing margin_a/margin_b use --
// p50 is the right statistic for a bias correction, p98/p2 is the
// right statistic for a soundness bound. iPositional[color] =
-// iKingP50ByAttackerArmyOnly[opponent's army bucket] (opponent is the
+// iKingP50ByAttackerArmy[opponent's army bucket] (opponent is the
// attacker of this side's own king) + iResidualP50ByOwnArmy[this
// side's own army bucket] (or the super-lazy-scoped sibling, for the
// super-lazy exit).
@@ -7043,22 +7207,18 @@ Return value:
// intentionally the least-specific answer that's still safe, not
// an attempt to approximate what a real positional estimate would
// say.
- if ((TRUE == g_fUseNnueEval) && (TRUE == g_fNnueWeightsLoaded))
- {
- SCORE iNnueScore = NnueForward(ctx);
- if (NULL != piPositional)
- {
- (*piPositional)[WHITE] = 0;
- (*piPositional)[BLACK] = 0;
- }
- return iNnueScore;
- }
-
- // Initialize army scalers here (moved ahead of the super-lazy exit
- // point below, 2026-09-08 -- their only inputs, uNonPawnMaterial,
- // are already available this early, and the super-lazy margin
- // table needs a material bucket before it can run). We skip lazy
- // eval if we're too late into an endgame.
+ // Initialize army scalers here -- moved ahead of BOTH the NNUE
+ // short-circuit above and the super-lazy exit point below,
+ // 2026-10-15 (was already ahead of super-lazy only, since
+ // 2026-09-08; moved further to also cover the NNUE path). Their
+ // only inputs, uNonPawnMaterial, are already available this early.
+ // Side effect of this move: pos->uArmyScaler is now always fresh
+ // after Eval() returns, including on the NNUE path -- previously
+ // it was left stale (whatever a prior call last set) whenever
+ // Eval() took the NNUE short-circuit, since that return happened
+ // before this block ran at all. Nothing on the NNUE path
+ // previously depended on uArmyScaler being stale vs fresh, so this
+ // is strictly safer, not a behavior change worth gating.
pos->uArmyScaler[BLACK] = pos->uNonPawnMaterial[BLACK] - VALUE_KING;
pos->uArmyScaler[WHITE] = pos->uNonPawnMaterial[WHITE] - VALUE_KING;
pos->uArmyScaler[BLACK] /= VALUE_PAWN;
@@ -7076,6 +7236,143 @@ Return value:
(SystemReadTimeStampCounter() - u64MaterialSetupTimer);
#endif
+ if ((TRUE == g_fUseNnueEval) && (TRUE == g_fNnueWeightsLoaded))
+ {
+ // p98 of |NnueForward() - NnueProbeScore()| per material
+ // bucket. Superseded 2026-10-15: originally derived from a
+ // 91M-sample population over three curated tactical suites
+ // (sd 10) -- replaced same day by a far larger and more
+ // representative 3.09-BILLION-sample population, 16 parallel
+ // shards of 12,500 positions each (200,000 total) from
+ // tests/cross_sectional_200k.ep_ at sd 6. Per-suite tactical
+ // positions are a narrow, biased slice (selected for sharp
+ // tactics); the cross-sectional pool's much broader position
+ // variety surfaced real outlier swings the narrower sample
+ // never saw (max up to 534cp here vs ~472cp before) while
+ // landing close to the same per-bucket numbers overall (mostly
+ // 0-10cp different) -- see docs/NNUE.md's "Lazy-evaluation
+ // tiering plan". Same bucketing as _MaterialBucket (combined
+ // army scaler, width BASE_MARGIN_MAT_WIDTH).
+ static const SCORE iNnueLazyMarginByMatBucket[BASE_MARGIN_MAT_BUCKETS] =
+ {
+ 180, 200, 190, 180, 170, 160, 150, 150,
+ };
+
+#ifdef CALIBRATE_MARGIN_SAFETY
+ // Calibration build: ALWAYS run the real forward pass, never
+ // skip it -- so any candidate gate decision can keep being
+ // measured against ground truth before it's trusted live.
+ // When the gate is enabled, measure against the real chosen
+ // margin (making "exceeded" a meaningful, ongoing validation
+ // signal); when disabled, still measure against an
+ // effectively-infinite sentinel (pure observation, matches
+ // this harness's original pre-gate behavior).
+ SCORE iNnueScore = NnueForward(ctx);
+ if (TRUE == g_fNnueProbeLoaded)
+ {
+ SCORE iProbeScore = NnueProbeScore(ctx);
+ SCORE iSwing = (SCORE)abs((int)iNnueScore - (int)iProbeScore);
+ SCORE iMarginUsed = (TRUE == g_fUseNnueLazyProbe) ?
+ iNnueLazyMarginByMatBucket[_MaterialBucket(pos)] :
+ INFINITY_SCORE;
+ RecordNnueLazyMarginSafetySwing(pos, iSwing, iMarginUsed);
+ }
+ {
+ // Material-only "super lazy" measurement -- no fit, no
+ // probe, just the already-maintained material balance
+ // (same cp units as NnueForward's output, see
+ // RecordNnueSuperLazyMarginSafetySwing). No real margin
+ // chosen yet, so always the infinite sentinel for now.
+ SCORE iSuperLazyScore = pos->iMaterialBalance[pos->uToMove];
+ SCORE iSuperLazySwing =
+ (SCORE)abs((int)iNnueScore - (int)iSuperLazyScore);
+ RecordNnueSuperLazyMarginSafetySwing(pos, iSuperLazySwing, INFINITY_SCORE);
+ }
+ // This branch always returns the real forward pass (never
+ // skips, by design -- see the big comment above), so it's
+ // unconditionally the NNUE_FULL tier for futility-margin
+ // selection purposes.
+ ctx->sPlyInfo[ctx->uPly].uLastPositionalSource = EVAL_POSITIONAL_SOURCE_NNUE_FULL;
+ g_u64NnueFullEvalsByBucket[_MaterialBucket(pos)]++;
+ if (NULL != piPositional)
+ {
+ (*piPositional)[WHITE] = 0;
+ (*piPositional)[BLACK] = 0;
+ }
+ return iNnueScore;
+#else
+ // Production build: if the lazy probe gate is enabled and
+ // confidently resolves the fail-high/fail-low question
+ // relative to [iAlpha, iBeta], skip the real forward pass
+ // entirely -- this is the actual speed win. Otherwise (gate
+ // disabled, probe not loaded, or inconclusive), fall through
+ // to the full pass exactly as before this feature existed.
+ // Super-lazy pre-filter, experimental -- 2026-10-16: cheapest
+ // possible check, pure material balance, no net evaluation at
+ // all. Previously measurement-only (CALIBRATE_MARGIN_SAFETY
+ // above records the real swing but always used an infinite
+ // sentinel margin, i.e. never actually gated). Deliberately
+ // loose/uncalibrated here per the "safety doesn't win games"
+ // experiment -- g_iNnueSuperLazyMargin is a flat runtime
+ // override (see nnueeval.c), not a per-bucket fit.
+ if (TRUE == g_fUseNnueSuperLazy)
+ {
+ SCORE iSuperLazyScore = pos->iMaterialBalance[pos->uToMove];
+
+ if (((iSuperLazyScore - g_iNnueSuperLazyMargin) > iBeta) ||
+ ((iSuperLazyScore + g_iNnueSuperLazyMargin) < iAlpha))
+ {
+ // Feed the same aggregate counters root.c's "Eval exit
+ // breakdown" line reads -- otherwise that line stays
+ // 0/0/0 under NNUE eval since it was only ever wired
+ // to the HCE's own super-lazy/lazy/full exit sites.
+ INC(ctx->sCounters.tree.u64SuperLazyEvals);
+ ctx->sPlyInfo[ctx->uPly].uLastPositionalSource = EVAL_POSITIONAL_SOURCE_NNUE_LAZY;
+ g_u64NnueLazyEvalsByBucket[_MaterialBucket(pos)]++;
+ if (NULL != piPositional)
+ {
+ (*piPositional)[WHITE] = 0;
+ (*piPositional)[BLACK] = 0;
+ }
+ return iSuperLazyScore;
+ }
+ }
+ if ((TRUE == g_fUseNnueLazyProbe) && (TRUE == g_fNnueProbeLoaded))
+ {
+ SCORE iProbeScore = NnueProbeScore(ctx);
+ SCORE iMargin = (g_iNnueLazyMarginOverride >= 0) ?
+ (SCORE)g_iNnueLazyMarginOverride :
+ iNnueLazyMarginByMatBucket[_MaterialBucket(pos)];
+
+ if (((iProbeScore - iMargin) > iBeta) ||
+ ((iProbeScore + iMargin) < iAlpha))
+ {
+ INC(ctx->sCounters.tree.u64LazyEvals);
+ ctx->sPlyInfo[ctx->uPly].uLastPositionalSource = EVAL_POSITIONAL_SOURCE_NNUE_LAZY;
+ g_u64NnueLazyEvalsByBucket[_MaterialBucket(pos)]++;
+ if (NULL != piPositional)
+ {
+ (*piPositional)[WHITE] = 0;
+ (*piPositional)[BLACK] = 0;
+ }
+ return iProbeScore;
+ }
+ }
+ {
+ SCORE iNnueScore = NnueForward(ctx);
+ INC(ctx->sCounters.tree.u64FullEvals);
+ ctx->sPlyInfo[ctx->uPly].uLastPositionalSource = EVAL_POSITIONAL_SOURCE_NNUE_FULL;
+ g_u64NnueFullEvalsByBucket[_MaterialBucket(pos)]++;
+ if (NULL != piPositional)
+ {
+ (*piPositional)[WHITE] = 0;
+ (*piPositional)[BLACK] = 0;
+ }
+ return iNnueScore;
+ }
+#endif
+ }
+
#ifdef DUMP_RESIDUAL_SAMPLES
// Super-lazy's OWN baseline -- material only, before _EvalPawns
// even runs. Separate from iResidualBaselineWhite/Black below
@@ -7178,7 +7475,7 @@ Return value:
// 2026-09-17: no longer flat 0 -- same p50-based typical-
// value estimate as regular lazy, using the super-lazy-
// scoped residual table (material-only baseline). See
- // iKingP50ByAttackerArmyOnly/iSuperLazyResidualP50By-
+ // iKingP50ByAttackerArmy/iSuperLazyResidualP50By-
// OwnArmy (file-scope, above).
ULONG uWhiteBucket = MINU(7, pos->uArmyScaler[WHITE] / 4);
ULONG uBlackBucket = MINU(7, pos->uArmyScaler[BLACK] / 4);
@@ -7520,7 +7817,7 @@ Return value:
// terms (king safety + residual), matching full-eval's
// own iPositional convention (that side's own real
// positional score, not a relative-to-opponent
- // quantity). See iKingP50ByAttackerArmyOnly/
+ // quantity). See iKingP50ByAttackerArmy/
// iResidualP50ByOwnArmy (file-scope, above) for the
// full percentile charts this was derived from.
ULONG uWhiteBucket = MINU(7, pos->uArmyScaler[WHITE] / 4);
@@ -7546,10 +7843,10 @@ Return value:
iSavedLazyScore = iScoreForSideToMove;
iSavedLazyPositional = iAlphaMargin;
iSavedLazyPositionalWhite =
- iKingP50ByAttackerArmyOnly[MINU(7, pos->uArmyScaler[BLACK] / 4)] +
+ iKingP50ByAttackerArmy[MINU(7, pos->uArmyScaler[BLACK] / 4)] +
iResidualP50ByOwnArmy[MINU(7, pos->uArmyScaler[WHITE] / 4)];
iSavedLazyPositionalBlack =
- iKingP50ByAttackerArmyOnly[MINU(7, pos->uArmyScaler[WHITE] / 4)] +
+ iKingP50ByAttackerArmy[MINU(7, pos->uArmyScaler[WHITE] / 4)] +
iResidualP50ByOwnArmy[MINU(7, pos->uArmyScaler[BLACK] / 4)];
#else
#ifdef EVAL_TIME
@@ -7591,10 +7888,10 @@ Return value:
iSavedLazyScore = iScoreForSideToMove;
iSavedLazyPositional = iBetaMargin;
iSavedLazyPositionalWhite =
- iKingP50ByAttackerArmyOnly[MINU(7, pos->uArmyScaler[BLACK] / 4)] +
+ iKingP50ByAttackerArmy[MINU(7, pos->uArmyScaler[BLACK] / 4)] +
iResidualP50ByOwnArmy[MINU(7, pos->uArmyScaler[WHITE] / 4)];
iSavedLazyPositionalBlack =
- iKingP50ByAttackerArmyOnly[MINU(7, pos->uArmyScaler[WHITE] / 4)] +
+ iKingP50ByAttackerArmy[MINU(7, pos->uArmyScaler[WHITE] / 4)] +
iResidualP50ByOwnArmy[MINU(7, pos->uArmyScaler[BLACK] / 4)];
#else
#ifdef EVAL_TIME
@@ -8127,10 +8424,10 @@ Return value:
ULONG uWhiteBucket = MINU(7, pos->uArmyScaler[WHITE] / 4);
ULONG uBlackBucket = MINU(7, pos->uArmyScaler[BLACK] / 4);
(*piPositional)[WHITE] =
- iKingP50ByAttackerArmyOnly[uBlackBucket] +
+ iKingP50ByAttackerArmy[uBlackBucket] +
iSuperLazyResidualP50ByOwnArmy[uWhiteBucket];
(*piPositional)[BLACK] =
- iKingP50ByAttackerArmyOnly[uWhiteBucket] +
+ iKingP50ByAttackerArmy[uWhiteBucket] +
iSuperLazyResidualP50ByOwnArmy[uBlackBucket];
// CALIBRATE_MARGIN_SAFETY builds: the FULL tag set above
// reflects the fallthrough that just ran for measurement