diff options
| author | Scott Gasch <[email protected]> | 2026-10-13 08:59:29 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-13 08:59:29 -0700 |
| commit | f03bcac9ce3c6542310010bfbef2b126bb6ae861 (patch) | |
| tree | fb30befe5cdd08d126d38009a5a62ca3ef40e9f9 /src/eval.c | |
| parent | 03545c4600df81b1a47dbc63fc606c065e857d8b (diff) | |
Minor changes; eval code was play tested as ~even.
Diffstat (limited to 'src/eval.c')
| -rw-r--r-- | src/eval.c | 201 |
1 files changed, 110 insertions, 91 deletions
@@ -123,7 +123,7 @@ static ULONG CASTLE_AND_DEVELOPMENT_SCALER[32] = // 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m // Q2R Q2Rm - 0, 1, 2, 3, 4, 5, 6, 7, + 1, 2, 3, 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, +48, +34, +22, +13, +9, +0 }, // black - { +0, +0, +9, +13, +22, +34, +48, +0, +0 } // white + { +0, +0, +0, +38, +26, +17, +10, +6, +0 }, // black + { +0, +0, +6, +10, +17, +26, +38, +0, +0 } // white }; // Note: x2 @@ -414,6 +414,21 @@ 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, @@ -749,7 +764,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_BISHOP + VALUE_BISHOP + VALUE_KING) + (VALUE_ROOK + VALUE_ROOK + VALUE_BISHOP + VALUE_KING) static ULONG KING_COUNTER_BY_ATTACK_PATTERN[32] = { @@ -817,7 +832,7 @@ static SCORE KING_QUEEN_PROXIMITY_DANGER[7] = +0, -10, -18, -25, -32, -39, -55 }; -static SCORE KING_MISSING_ONE_CASTLE_OPTION = -23; +static SCORE KING_MISSING_ONE_CASTLE_OPTION = -25; // 2026-09-14: new term, not a retune of an existing one. // king_safety_data/HANDOFF.md's regression found `back_flight` (an @@ -959,6 +974,9 @@ 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), @@ -1576,17 +1594,6 @@ 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); @@ -3053,16 +3060,14 @@ 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. 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). + // to the whole pawn mass. i = 0; bb = pos->bbPawns[WHITE] | pos->bbPawns[BLACK]; while (IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb))) { i -= (SCORE)DISTANCE(cSquare, cKing); } - iTotal += i * 2; + iTotal += i; pEntry->u64ActionKey = u64Key; pEntry->iActionBonus = iTotal; @@ -5379,13 +5384,11 @@ 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, @@ -5395,15 +5398,7 @@ 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 - // -- 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. + // one non-king piece (lone king, K+minor, K+rook, even K+queen. if (TotalNonPawnCount(pos, xColor) <= 2) { // Supporting-own-passer and out-of-action-in-KP-endgames, @@ -5422,7 +5417,7 @@ Return value: c, iKingScore, i, - "king activity (passer support + in/out of action)"); + "king/passer position"); } return iKingScore; } @@ -5856,7 +5851,7 @@ Return value: static void _ReEvalPassers(IN OUT POSITION *pos, - IN PAWN_HASH_ENTRY *pHash) + IN PAWN_HASH_ENTRY *pHash) /** Routine description: @@ -5877,15 +5872,13 @@ 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); @@ -5899,46 +5892,34 @@ 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); - 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 + 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"); - // // Consider distance from friend king / enemy king - // i = (8 - DISTANCE(c, KingPosition(pos, uColor))); if (((uColor == WHITE) && ((KingPosition(pos, BLACK) >> 4) <= (c >> 4))) || @@ -5964,26 +5945,6 @@ 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 - // } } } @@ -6070,6 +6031,64 @@ 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 @@ -6122,7 +6141,7 @@ Return value: 0, ILLEGAL_COOR, pos->iScore[uAhead], - (0 != uAheadPawnCount) ? (SCORE)(uAheadPawnCount * 2) : + (0 != uAheadPawnCount) ? (SCORE)(uAheadPawnCount * 4) : (TRUE == fAheadLoneMinorNoMate) ? -30 : 0, "don't trade pawns"); } |
