summaryrefslogtreecommitdiff
path: root/src/eval.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-13 09:07:23 -0700
committerScott Gasch <[email protected]>2026-10-13 09:07:30 -0700
commit78881b8e6422b7980e5f71780ae4bed862b93e72 (patch)
treea433fdce69fd27359679ad91e0ebedd52e2506cd /src/eval.c
parentf03bcac9ce3c6542310010bfbef2b126bb6ae861 (diff)
Revert "Minor changes; eval code was play tested as ~even."
This reverts commit f03bcac9ce3c6542310010bfbef2b126bb6ae861. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/eval.c')
-rw-r--r--src/eval.c201
1 files changed, 91 insertions, 110 deletions
diff --git a/src/eval.c b/src/eval.c
index c747a8e..a909953 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -123,7 +123,7 @@ static ULONG CASTLE_AND_DEVELOPMENT_SCALER[32] =
// 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m
// Q2R Q2Rm
- 1, 2, 3, 3, 4, 5, 6, 7,
+ 0, 1, 2, 3, 4, 5, 6, 7,
// na Q2R2m QR4m na Q2R3m na na full
7, 8, 8, 8, 8, 8, 8, 8,
@@ -304,8 +304,8 @@ static SCORE PASSER_BY_RANK[2][9] =
static SCORE CANDIDATE_PASSER_BY_RANK[2][9] =
{// 0 1 2 3 4 5 6 7 8
- { +0, +0, +0, +38, +26, +17, +10, +6, +0 }, // black
- { +0, +0, +6, +10, +17, +26, +38, +0, +0 } // white
+ { +0, +0, +0, +48, +34, +22, +13, +9, +0 }, // black
+ { +0, +0, +9, +13, +22, +34, +48, +0, +0 } // white
};
// Note: x2
@@ -414,21 +414,6 @@ static SCORE UNDEVELOPED_MINORS_IN_OPENING[5] =
// "Evaluation of Material Imbalance"
static SCORE BISHOP_PAIR_BONUS = 30;
-// Material-imbalance bonuses for a handful of "close looking but not
-// actually equal" non-pawn shapes that _EvalBadTrades special-cases
-// outright rather than running through its generic ahead/behind
-// trade-encouragement logic (see _EvalBadTrades for why: that logic
-// picks "ahead" by raw point value, which gets the direction or
-// magnitude wrong for these specific shapes). All three are flat,
-// position-independent nudges -- deliberately not scaled by pawn
-// count/game phase the way the generic terms are, since the whole
-// point is that these shapes are close enough that a big swing isn't
-// warranted. DNA-tunable; starting values are hand judgment pending a
-// tuning/match-play pass.
-static SCORE QUEEN_VS_TWO_ROOKS_BONUS = 25; // favors the 2 rooks
-static SCORE QUEEN_VS_THREE_MINORS_BONUS = 10; // favors the 3 minors
-static SCORE QUEEN_VS_ROOK_PLUS_MINOR_BONUS = 20; // favors the queen
-
static SCORE STATIONARY_PAWN_ON_BISHOP_COLOR[128] =
{
+0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0,
@@ -764,7 +749,7 @@ static SCORE KING_TO_CENTER[128] =
// the stay-home bonus now uses this same threshold, so "safety scored"
// and "stay-home bonus applies" are exactly the same condition.
#define DO_KING_SAFETY_THRESHOLD \
- (VALUE_ROOK + VALUE_ROOK + VALUE_BISHOP + VALUE_KING)
+ (VALUE_ROOK + VALUE_BISHOP + VALUE_BISHOP + VALUE_KING)
static ULONG KING_COUNTER_BY_ATTACK_PATTERN[32] =
{
@@ -832,7 +817,7 @@ static SCORE KING_QUEEN_PROXIMITY_DANGER[7] =
+0, -10, -18, -25, -32, -39, -55
};
-static SCORE KING_MISSING_ONE_CASTLE_OPTION = -25;
+static SCORE KING_MISSING_ONE_CASTLE_OPTION = -23;
// 2026-09-14: new term, not a retune of an existing one.
// king_safety_data/HANDOFF.md's regression found `back_flight` (an
@@ -974,9 +959,6 @@ static DNA_BASE_SIZE g_EvalDNA[] = {
DNA_VAR(RACER_WINS_RACE),
DNA_ARRAY(UNDEVELOPED_MINORS_IN_OPENING),
DNA_VAR(BISHOP_PAIR_BONUS),
- DNA_VAR(QUEEN_VS_TWO_ROOKS_BONUS),
- DNA_VAR(QUEEN_VS_THREE_MINORS_BONUS),
- DNA_VAR(QUEEN_VS_ROOK_PLUS_MINOR_BONUS),
DNA_ARRAY(STATIONARY_PAWN_ON_BISHOP_COLOR),
DNA_ARRAY(TRANSIENT_PAWN_ON_BISHOP_COLOR),
DNA_ARRAY(BISHOP_MOBILITY_BY_SQUARES),
@@ -1594,6 +1576,17 @@ Return value:
pHash->iScore[uColor],
PASSER_BY_RANK[uColor][RANK(c)],
"passed pawn");
+
+ // However, don't give doubled passers such a big bonus.
+ if (IS_PAWN(pos->rgSquare[c - 16 * g_iAhead[uColor]].pPiece))
+ {
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ -(PASSER_BY_RANK[uColor][RANK(c)] / 2),
+ "doubled passer");
+ }
return;
}
uSentries = FastCountBits(bb);
@@ -3060,14 +3053,16 @@ Return value:
// Out of action in KP-ish endgames, e.g.
// 8/8/1p6/p6k/P3K3/8/1P6/8 w - - 0 11 -- how close this king is
- // to the whole pawn mass.
+ // to the whole pawn mass. The *2 preserves the old formula's own
+ // scale (it used to be a differential, this is now a plain sum,
+ // but the per-pawn magnitude is unchanged).
i = 0;
bb = pos->bbPawns[WHITE] | pos->bbPawns[BLACK];
while (IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
{
i -= (SCORE)DISTANCE(cSquare, cKing);
}
- iTotal += i;
+ iTotal += i * 2;
pEntry->u64ActionKey = u64Key;
pEntry->iActionBonus = iTotal;
@@ -5384,11 +5379,13 @@ Return value:
SCORE i;
SCORE iKingScore = 0;
+ //
// Come out. KING_TO_CENTER applies unconditionally (no longer
// gated by uTotalNonPawnsBothSides<8 -- that was a different,
// combined-both-sides unit than this whole split now uses, and
// left a gap between "safety stops mattering" and "centralizing
// is actually encouraged"; see _EvalKing's header comment).
+ //
i = KING_TO_CENTER[c];
EVAL_TERM(uColor,
KING,
@@ -5398,7 +5395,15 @@ Return value:
"centralize king");
// Sharper late-game guidance once the enemy is down to at most
- // one non-king piece (lone king, K+minor, K+rook, even K+queen.
+ // one non-king piece (lone king, K+minor, K+rook, even K+queen
+ // -- though a lone queen's own material value already exceeds
+ // DO_KING_SAFETY_THRESHOLD on its own, VALUE_QUEEN=975 >
+ // VALUE_ROOK+VALUE_BISHOP=800, so that specific case can never
+ // actually reach this function at all; the count check below is
+ // correct on its own terms regardless). Per-call/per-king
+ // (xColor's own material), not the old combined-both-sides
+ // uTotalNonPawnsBothSides==2 -- the two kings' own enemies can
+ // legitimately disagree now.
if (TotalNonPawnCount(pos, xColor) <= 2)
{
// Supporting-own-passer and out-of-action-in-KP-endgames,
@@ -5417,7 +5422,7 @@ Return value:
c,
iKingScore,
i,
- "king/passer position");
+ "king activity (passer support + in/out of action)");
}
return iKingScore;
}
@@ -5851,7 +5856,7 @@ Return value:
static void
_ReEvalPassers(IN OUT POSITION *pos,
- IN PAWN_HASH_ENTRY *pHash)
+ IN PAWN_HASH_ENTRY *pHash)
/**
Routine description:
@@ -5872,13 +5877,15 @@ Return value:
ULONG xColor;
COOR c;
COOR cSquare;
- COOR cRank;
BITBOARD bb;
PIECE p;
SCORE i;
ULONG uNumPassers[2] = { 0, 0 };
int d1;
+ ASSERT(pHash->bbPasserLocations[WHITE] |
+ pHash->bbPasserLocations[BLACK]);
+
FOREACH_COLOR(uColor)
{
xColor = FLIP(uColor);
@@ -5892,34 +5899,46 @@ Return value:
while(IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bb)))
{
+ //
// Consider the control of the square in front of the passer
- cRank = RANK(c);
+ //
cSquare = c + d1;
ASSERT(IS_ON_BOARD(cSquare));
u = _WhoControlsSquareFast(pos, cSquare);
- EVAL_TERM(uColor,
- PAWN,
- c,
- pos->iScore[uColor],
- (u == xColor) *
- -((PASSER_BY_RANK[uColor][cRank] / 4)),
- "enemy controls sq ahead");
-
- // Square ahead is uncontrolled but occupied by xColor?
- p = pos->rgSquare[cSquare].pPiece;
- EVAL_TERM(uColor,
- PAWN,
- c,
- pos->iScore[uColor],
- (
- (u == (ULONG)-1) &&
- !IS_EMPTY(p) &&
- OPPOSITE_COLORS(p, uColor)
- ) *
- -(PASSER_BY_RANK[uColor][cRank] / 4),
- "enemy occupies sq ahead");
+ if (u == xColor)
+ {
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pos->iScore[uColor],
+ -((PASSER_BY_RANK[uColor][RANK(c)] / 4) +
+ (PASSER_BY_RANK[uColor][RANK(c)] / 16)),
+ "enemy controls sq ahead");
+ }
+ else if (u == (ULONG)-1)
+ {
+ p = pos->rgSquare[cSquare].pPiece;
+ if (!IS_EMPTY(p) && (OPPOSITE_COLORS(p, uColor)))
+ {
+ ASSERT(GET_COLOR(p) != uColor);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pos->iScore[uColor],
+ -(PASSER_BY_RANK[uColor][RANK(c)] / 4),
+ "enemy occupies sq ahead");
+ }
+ }
+#ifdef DEBUG
+ else
+ {
+ ASSERT(u == uColor);
+ }
+#endif
+ //
// Consider distance from friend king / enemy king
+ //
i = (8 - DISTANCE(c, KingPosition(pos, uColor)));
if (((uColor == WHITE) &&
((KingPosition(pos, BLACK) >> 4) <= (c >> 4))) ||
@@ -5945,6 +5964,26 @@ Return value:
pos->iScore[uColor],
i,
"passer distance to kings");
+
+ //
+ // If a side is a piece down then the other side's passers
+ // are even more dangerous.
+ //
+ if (TotalNonPawnCount(pos, uColor) >
+ TotalNonPawnCount(pos, xColor))
+ {
+ i = (PASSER_BY_RANK[uColor][RANK(c)] / 2);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pos->iScore[uColor],
+ i,
+ "passer and piece up");
+ }
+
+ //
+ // TODO: For connected passers vs KR, consult the oracle
+ //
}
}
}
@@ -6031,64 +6070,6 @@ Return value:
}
//
- // Q vs RR, Q vs MMM, and Q vs RM are three "close looking but not
- // actually equal" non-pawn shapes that this routine's generic
- // ahead/behind machinery below gets wrong -- it picks "ahead" by
- // raw point value and hands that side a position/phase-scaled
- // "keep trading" bonus (up to ~160 here), which is both sometimes
- // the wrong side and wildly too large an opinion for shapes this
- // close. Special-case all three to their own small flat
- // (unscaled, pawn/phase-independent) bonus instead, then return
- // before the generic logic ever runs.
- //
- // Q vs RR: rooks are a bit better (open files/ranks, strong in the
- // endgame) -- small flat bonus to the rook side. Queen is uBehind
- // here (975 < 2*500 makes the rooks uAhead by this routine's own
- // value-based metric).
- if ((1 == TotalNonPawnCount(pos, uBehind)) &&
- (1 == FastCountBits(pos->bbPieces[uBehind][QUEEN])) &&
- (2 == TotalNonPawnCount(pos, uAhead)) &&
- (2 == FastCountBits(pos->bbPieces[uAhead][ROOK])))
- {
- EVAL_TERM(uAhead, 0, ILLEGAL_COOR, pos->iScore[uAhead],
- QUEEN_VS_TWO_ROOKS_BONUS, "queen vs two rooks");
- return;
- }
-
- // Q vs MMM: three minors need heavy coordination to use their
- // nominal edge and are drawish in practice -- small flat bonus to
- // the minors side. Queen is uAhead here (975 > 900 makes the
- // queen uAhead by this routine's own value-based metric).
- if ((1 == TotalNonPawnCount(pos, uAhead)) &&
- (1 == FastCountBits(pos->bbPieces[uAhead][QUEEN])) &&
- (3 == TotalNonPawnCount(pos, uBehind)) &&
- (0 == FastCountBits(pos->bbPieces[uBehind][ROOK])) &&
- (0 == FastCountBits(pos->bbPieces[uBehind][QUEEN])))
- {
- EVAL_TERM(uBehind, 0, ILLEGAL_COOR, pos->iScore[uBehind],
- QUEEN_VS_THREE_MINORS_BONUS, "queen vs three minors");
- return;
- }
-
- // Q vs RM: queen's single-piece coordination (easier to combine
- // with the king than a rook+minor pair trying to work together)
- // and tactical resourcefulness outweighs the ~1 pawn point
- // deficit (975 vs 800) slightly more often than not -- small flat
- // bonus to the queen side. Queen is uAhead here (975 > 800 makes
- // the queen uAhead by this routine's own value-based metric).
- if ((1 == TotalNonPawnCount(pos, uAhead)) &&
- (1 == FastCountBits(pos->bbPieces[uAhead][QUEEN])) &&
- (2 == TotalNonPawnCount(pos, uBehind)) &&
- (1 == FastCountBits(pos->bbPieces[uBehind][ROOK])) &&
- (1 == (FastCountBits(pos->bbPieces[uBehind][KNIGHT]) +
- FastCountBits(pos->bbPieces[uBehind][BISHOP]))))
- {
- EVAL_TERM(uAhead, 0, ILLEGAL_COOR, pos->iScore[uAhead],
- QUEEN_VS_ROOK_PLUS_MINOR_BONUS, "queen vs rook+minor");
- return;
- }
-
- //
// Encourage the side that is ahead in piece material to continue
// trading pieces and not to trade pawns. This also has the
// effect of making the side that is behind in material want to
@@ -6141,7 +6122,7 @@ Return value:
0,
ILLEGAL_COOR,
pos->iScore[uAhead],
- (0 != uAheadPawnCount) ? (SCORE)(uAheadPawnCount * 4) :
+ (0 != uAheadPawnCount) ? (SCORE)(uAheadPawnCount * 2) :
(TRUE == fAheadLoneMinorNoMate) ? -30 : 0,
"don't trade pawns");
}