diff options
| author | Scott Gasch <[email protected]> | 2026-09-20 08:13:57 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-20 08:13:57 -0700 |
| commit | 8c0d66f13f08cb5e760d787a878260ad67d17c13 (patch) | |
| tree | f779fb622a18e0d676da76736d9826ddda5dfdb9 /src/eval.c | |
| parent | b70be6fde0fef0694e404e16d70f00c73dd971a2 (diff) | |
Makes recogn.c use g_iDrawScore. Some refactoring in the pawn
hash / _EvalPawn including some scoring changes. Self-play
positive.
Diffstat (limited to 'src/eval.c')
| -rw-r--r-- | src/eval.c | 514 |
1 files changed, 270 insertions, 244 deletions
@@ -156,6 +156,7 @@ COOR JUMPING_RANK[2] = { 7, 2 }; // Pawn eval terms // --------------------------------------------------------------------------- // + // "There is one case which can be treated as positional or material, // namely the rook's pawn, which differs from other pawns in that it // can only capture one way instead of two. Since this handicap cannot @@ -170,14 +171,14 @@ COOR JUMPING_RANK[2] = { 7, 2 }; // --Larry Kaufman, IM // "Evaluation of Material Imbalance" // -static SCORE PAWN_CENTRALITY_BONUS[128] = +static SCORE PAWN_CENTRALITY_BONUS[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0, -8, 0, 0, 0, 0, 0, 0, -8, 0,0,0,0,0,0,0,0, -8, 0, 5, 5, 5, 5, 0, -8, 0,0,0,0,0,0,0,0, - -8, 0, 5, 9, 9, 5, 0, -8, 0,0,0,0,0,0,0,0, + -8, 0, 5, 8, 8, 5, 0, -8, 0,0,0,0,0,0,0,0, // ------------------------------------------------------------------- - -8, 0, 5, 9, 9, 5, 0, -8, 0,0,0,0,0,0,0,0, + -8, 0, 5, 8, 8, 5, 0, -8, 0,0,0,0,0,0,0,0, -8, 0, 5, 5, 5, 5, 0, -8, 0,0,0,0,0,0,0,0, -8, 0, 0, 0, 0, 0, 0, -8, 0,0,0,0,0,0,0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0 @@ -336,8 +337,8 @@ static SCORE OUTSIDE_PASSER_BY_DISTANCE[9] = +0, +0, +2, +5, +7, +9, +11, +14, +18 }; -static SCORE PASSER_BONUS_AS_MATERIAL_COMES_OFF[32] = -{ +static SCORE PASSER_BONUS_AS_MATERIAL_COMES_OFF[32] = +{ // none na na m na R 2m na +60, -1, -1, +37, -1, +43, +19, -1, @@ -505,7 +506,7 @@ static SCORE KNIGHT_IN_CLOSED_POSITION[33] = // average bonus, not scaled by distance from the enemy king -- by direct // instruction. Each was a [8]-by-distance table; these are that table's // simple mean, rounded. -static SCORE ROOK_ON_FULL_OPEN = 16; +static SCORE ROOK_ON_FULL_OPEN = 18; static SCORE ROOK_ON_HALF_OPEN_WITH_ENEMY = 9; static SCORE ROOK_ON_HALF_OPEN_WITH_FRIEND = 10; // Cache of the three constants above, indexed [friend pawn on file?] @@ -524,6 +525,16 @@ static SCORE ROOK_FULL_HALF_OPEN_BONUS[2][2] = { { 16, 9 }, { 10, 0 } }; static SCORE ROOK_WITH_ENEMY_PASSER = 17; static SCORE ROOK_WITH_FRIEND_PASSER = 10; +// A rook on the 7th (White) or 2nd (Black) rank attacks along the +// enemy's back-ranks-minus-one, hitting undeveloped/unmoved pawns and +// cutting the enemy king off from that rank -- a small, classic bonus +// independent of the file-openness/passer bonuses above. BBRANK22/ +// BBRANK77 are compile-time constants (chess.h), so this table (unlike +// ROOK_FULL_HALF_OPEN_BONUS above) can be a real `static const`: it +// depends on the board geometry, not on any DNA-tunable value. +static const BITBOARD BBROOK_7TH_RANK[2] = { BBRANK22, BBRANK77 }; // [BLACK], [WHITE] +static SCORE ROOK_ON_SEVENTH_BONUS = 9; + static SCORE ROOK_BEHIND_PASSER_BY_PASSER_RANK[2][9] = {// 0 1 2 3 4 5 6 7 8 { +0, +0, +25, +17, +12, +6, +1, +0, +0 }, // black @@ -595,7 +606,7 @@ static SCORE QUEEN_MOBILITY_BY_SQUARES[28] = // point that queens are worth more then the standard nine "points". // Thus, this scale is biased upwards by about 0.15 pawn. // -static SCORE QUEEN_KING_TROPISM[8] = +static SCORE QUEEN_KING_TROPISM[8] = {// 0 1 2 3 4 5 6 7 0, +34, +28, +22, +19, +17, +16, +15 }; @@ -619,7 +630,7 @@ static SCORE QUEEN_KING_TROPISM[8] = static ULONG KING_INITIAL_COUNTER_BY_LOCATION[2][128] = { { - 1, 0, 0, 0, 1, 0, 0, 1, 0,0,0,0,0,0,0,0, + 2, 0, 0, 0, 1, 0, 0, 2, 0,0,0,0,0,0,0,0, 1, 1, 1, 1, 1, 1, 1, 1, 0,0,0,0,0,0,0,0, 3, 3, 3, 3, 3, 3, 3, 3, 0,0,0,0,0,0,0,0, 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0, @@ -638,7 +649,7 @@ static ULONG KING_INITIAL_COUNTER_BY_LOCATION[2][128] = 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0, 3, 3, 3, 3, 3, 3, 3, 3, 0,0,0,0,0,0,0,0, 1, 1, 1, 1, 1, 1, 1, 1, 0,0,0,0,0,0,0,0, - 1, 0, 0, 0, 1, 0, 0, 1, 0,0,0,0,0,0,0,0 + 2, 0, 0, 0, 1, 0, 0, 2, 0,0,0,0,0,0,0,0 } }; @@ -930,6 +941,7 @@ static DNA_BASE_SIZE g_EvalDNA[] = { DNA_VAR(ROOK_ON_HALF_OPEN_WITH_FRIEND), DNA_VAR(ROOK_WITH_ENEMY_PASSER), DNA_VAR(ROOK_WITH_FRIEND_PASSER), + DNA_VAR(ROOK_ON_SEVENTH_BONUS), DNA_MATRIX(ROOK_BEHIND_PASSER_BY_PASSER_RANK), DNA_MATRIX(ROOK_LEADS_PASSER_BY_PASSER_RANK), DNA_VAR(KING_TRAPPING_ROOK), @@ -1966,6 +1978,151 @@ Return value: } +static INLINE FLAG +_FileCanSupportPawn(IN POSITION *pos, + IN PAWN_HASH_ENTRY *pHash, + IN ULONG uColor, + IN ULONG uPawnFile, + IN COOR c, + IN BITBOARD bbBlockingRanks) +/** + +Routine description: + + Can a friendly pawn on uPawnFile ever defend the pawn on square c + -- either right now (already within defending range) or later + (nothing permanently blocks it from advancing into range)? + + NOTE: on a doubled adjacent file, this only looks at whether *a* + helper pawn on uPawnFile can reach c -- if the nearest one's only + route there is an unsafe one-step push, this returns FALSE even + though a second, more distant pawn on the same file might still + reach c safely later. Rare (requires doubled pawns on the adjacent + file specifically) and consistent with the rest of this function's + approximate, snapshot-in-time nature. + +Parameters: + + POSITION *pos + PAWN_HASH_ENTRY *pHash + ULONG uColor : color of the pawn on c (and of the potential helper) + ULONG uPawnFile : the adjacent file to test (0-7) + COOR c : the pawn being supported + BITBOARD bbBlockingRanks : c's rank plus every rank behind it, from + uColor's forward direction (see caller) + +Return value: + + FLAG + +**/ +{ + if (0 == pHash->uCountPerFile[uColor][uPawnFile + 1]) + { + return FALSE; // no friendly pawn on that file at all + } + if (pos->bbPawns[uColor] & BBFILE[uPawnFile] & BBADJACENT_RANKS[RANK(c)]) + { + return TRUE; // already defending + } + + // A friendly pawn one step short of defending range would have to + // push onto its destination square to get there. If a live enemy + // pawn attacks that destination and no friendly pawn attack of + // our own covers it too (pos->bbPawnAttacks[] is already + // populated by _PopulatePawnAttackBits before either _EvalPawns + // pass runs, so both are real, current facts, not a guess about + // some future position), pushing there just loses the pawn for + // nothing -- not a safe way to arrive as a defender. If we also + // attack the destination, the push is at worst an even trade, so + // it doesn't disqualify this file. WHITE pushes toward decreasing + // bitboard index (see _PopulatePawnAttackBits), so a pawn one + // push short of the defending band sits at (band << 8), and its + // destination is (that square >> 8) -- i.e. the original band + // itself; BLACK is mirrored. + { + BITBOARD bbOneStepShort = (uColor == WHITE) ? + (BBADJACENT_RANKS[RANK(c)] << 8) : + (BBADJACENT_RANKS[RANK(c)] >> 8); + BITBOARD bbHelper = pos->bbPawns[uColor] & BBFILE[uPawnFile] & bbOneStepShort; + if (bbHelper) + { + BITBOARD bbDestination = (uColor == WHITE) ? + (bbHelper >> 8) : (bbHelper << 8); + if ((bbDestination & pos->bbPawnAttacks[FLIP(uColor)]) && + !(bbDestination & pos->bbPawnAttacks[uColor])) + { + return FALSE; + } + } + } + + // A friendly pawn is on the file but not yet close enough to + // defend -- it can still march up and defend later, unless an + // enemy pawn on this same file already sits at c's rank or behind + // it (from our own forward direction), permanently blocking the + // way. + return (0 == (pos->bbPawns[FLIP(uColor)] & BBFILE[uPawnFile] & bbBlockingRanks)); +} + + +static INLINE FLAG +_IsBackwardPawn(IN POSITION *pos, + IN ULONG uColor, + IN COOR c, + IN COOR cSquare) +/** + +Routine description: + + Is the pawn on square c "backward"? See "Pawn Power in Chess" + pp. 25-27. The caller has already established the one condition + that needs pos->bbPawnAttacks[]: the enemy, not a friendly pawn, + controls c's stop square (cSquare). This checks the three + remaining, independently-necessary conditions: + + 1. cSquare isn't itself occupied by a pawn (either color) -- + otherwise this is a blocked/rammed pawn, not a backward one. + 2. c has advanced far enough for the classification to apply. + 3. Neither neighbor on an adjacent file, one rank ahead of c, + is a friendly pawn -- i.e. our neighbors have already + advanced past c, so they can no longer shield it. (0x88 + coordinates make cSquare +/- 1 safe to test directly: + IS_ON_BOARD() catches the file-edge case for free, unlike a + bitboard shift which would need explicit BBFILE[0]/BBFILE[7] + masking.) + +Parameters: + + POSITION *pos + ULONG uColor : color of the pawn on c + COOR c : the pawn being tested + COOR cSquare : c's stop square (one square ahead of c) + +Return value: + + FLAG + +**/ +{ + PIECE p; + + if (IS_PAWN(pos->rgSquare[cSquare].pPiece)) + { + return FALSE; // stop square is blocked by a pawn + } + if (!(((WHITE == uColor) && (RANK(c) < 4)) || + ((BLACK == uColor) && (RANK(c) > 5)))) + { + return FALSE; // not advanced enough yet + } + + p = (BLACK_PAWN | uColor); // uColor's own pawn, regardless of color + return ((IS_ON_BOARD(cSquare - 1) && (pos->rgSquare[cSquare - 1].pPiece == p)) || + (IS_ON_BOARD(cSquare + 1) && (pos->rgSquare[cSquare + 1].pPiece == p))); +} + + static PAWN_HASH_ENTRY * _EvalPawns(IN OUT SEARCHER_THREAD_CONTEXT *ctx, OUT FLAG *pfDeferred) @@ -1989,22 +2146,19 @@ Return value: POSITION *pos = &ctx->sPosition; PAWN_HASH_ENTRY *pHash; COOR c, cSquare; - PIECE p; ULONG uIsolated[2]; ULONG uDoubled[2]; SCORE iDuos[2]; ULONG u, uPawnFile; ULONG uColor; - ULONG uUnsupportable; BITBOARD bb; + BITBOARD bbBlockingRanks; int d1; #ifdef DEBUG SCORE t; #endif - // - // Now, look up the hash entry for this position. - // + // Look up the hash entry for this position. INC(ctx->sCounters.pawnhash.u64Probes); pHash = PawnHashLookup(ctx); ASSERT(NULL != pHash); @@ -2015,11 +2169,9 @@ Return value: return(pHash); } - // // We need the attack table to evaluate the pawns... and we missed // the hash table so we have to populate it now. Can't defer this - // one. :( - // + // one. *pfDeferred = FALSE; _InitializePawnHashEntry(pHash, pos); _PopulatePawnAttackBits(pos); @@ -2027,201 +2179,102 @@ Return value: uDoubled[BLACK] = uDoubled[WHITE] = 0; iDuos[BLACK] = iDuos[WHITE] = 0; - // // First pass -- everything here except the per-square centrality // bonus is a closed-form aggregate over pos->bbPawns[uColor] // (uCountPerFile, uNumUnmovedPawns, bbStationaryPawns/ // uNumRammedPawns), so none of it needs a per-pawn mailbox walk; // only the centrality lookup genuinely needs one (it's a per-square - // table, not summable any other way). Replaces the old single loop - // over pos->cPawns[uColor][] that did all of these one pawn at a - // time, including a "counted rammed pawns twice, divide by 2 to fix - // it" hack that a closed-form popcount doesn't need in the first - // place (see below). - // - { - BITBOARD bbAllPawns = pos->bbPawns[WHITE] | pos->bbPawns[BLACK]; + // table, not summable any other way). + BITBOARD bbAllPawns = pos->bbPawns[WHITE] | pos->bbPawns[BLACK]; - FOREACH_COLOR(uColor) - { - ASSERT(IS_VALID_COLOR(uColor)); + FOREACH_COLOR(uColor) + { + ASSERT(IS_VALID_COLOR(uColor)); - for (u = A; u <= H; u++) - { - pHash->uCountPerFile[uColor][u + 1] = - CountBits(pos->bbPawns[uColor] & BBFILE[u]); - ASSERT(pHash->uCountPerFile[uColor][u + 1] < 6); - } + // Pre-count number of pawns per file, unrolled. + ASSERT(pHash->uCountPerFile[uColor][0] == 0); + pHash->uCountPerFile[uColor][A + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[A]); + pHash->uCountPerFile[uColor][B + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[B]); + pHash->uCountPerFile[uColor][C + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[C]); + pHash->uCountPerFile[uColor][D + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[D]); + pHash->uCountPerFile[uColor][E + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[E]); + pHash->uCountPerFile[uColor][F + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[F]); + pHash->uCountPerFile[uColor][G + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[G]); + pHash->uCountPerFile[uColor][H + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[H]); + ASSERT(pHash->uCountPerFile[uColor][9] == 0); - pHash->uNumUnmovedPawns[uColor] = - CountBits(pos->bbPawns[uColor] & - BBRANK[RANK(uUnmovedRank[uColor])]); - ASSERT(pHash->uNumUnmovedPawns[uColor] <= 8); + // Count number of unmoved pawns. + pHash->uNumUnmovedPawns[uColor] = CountBits(pos->bbPawns[uColor] & + BBRANK[RANK(uUnmovedRank[uColor])]); + ASSERT(pHash->uNumUnmovedPawns[uColor] <= 8); - // - // Detect rammed and other stationary pawns. A pawn is - // "stationary" if *any* pawn (either color -- e.g. a - // doubled own-pawn block counts too) sits on its stop - // square; it's specifically "rammed" only when that - // blocker is the enemy's. WHITE's stop square is one rank - // toward rank8 (bit index -8 in the linear 0-63 bitboard - // numbering -- see _PopulatePawnAttackBits' >>8/<<8 shifts - // for the same convention), BLACK's is +8. - // - pHash->bbStationaryPawns[uColor] = - (uColor == WHITE) ? - (pos->bbPawns[WHITE] & (bbAllPawns << 8)) : - (pos->bbPawns[BLACK] & (bbAllPawns >> 8)); - ASSERT(CountBits(pHash->bbStationaryPawns[uColor]) <= 8); + // Detect rammed and other stationary pawns. A pawn is + // "stationary" if *any* pawn (either color -- e.g. a doubled + // own-pawn block counts too) sits on its "stop square". It's + // specifically "rammed" only when that blocker is the + // enemy's. WHITE's stop square is one rank toward rank8 (bit + // index -8 in the linear 0-63 bitboard numbering -- see + // _PopulatePawnAttackBits' >>8/<<8 shifts for the same + // convention), BLACK's is +8. + pHash->bbStationaryPawns[uColor] = + (uColor == WHITE) ? + (pos->bbPawns[WHITE] & (bbAllPawns << 8)) : + (pos->bbPawns[BLACK] & (bbAllPawns >> 8)); + ASSERT(CountBits(pHash->bbStationaryPawns[uColor]) <= 8); - // - // Give central pawns a small bonus; penalize rook pawns. - // This is the one term above that's genuinely per-square - // and can't be folded into a popcount. - // - bb = pos->bbPawns[uColor]; - while(IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bb))) - { - EVAL_TERM(uColor, - PAWN, - c, - pHash->iScore[uColor], - PAWN_CENTRALITY_BONUS[c], - "centrality"); - } + // Give central pawns a small bonus and rook pawns a small + // penalty. + bb = pos->bbPawns[uColor]; + while(IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bb))) + { + EVAL_TERM(uColor, + PAWN, + c, + pHash->iScore[uColor], + PAWN_CENTRALITY_BONUS[c], + "location"); } - - // - // Rammed pairs (an enemy pawn, not just any pawn, on the stop - // square): each pair shows up as exactly one bit in each - // side's own stationary-pawn count, so no double-counting - // (and no /2 fixup) is needed the way the old per-pawn, - // per-color loop required. - // - pHash->uNumRammedPawns = - CountBits(pos->bbPawns[WHITE] & (pos->bbPawns[BLACK] << 8)); - ASSERT(pHash->uNumRammedPawns <= 8); } - // - // Second pass - // + // Rammed pairs (an enemy pawn, not just any pawn, on the stop + // square): each pair shows up as exactly one bit in each + // side's own stationary-pawn count, so no double-counting + // (and no /2 fixup) is needed the way the old per-pawn, + // per-color loop required. + pHash->uNumRammedPawns = + CountBits(pos->bbPawns[WHITE] & (pos->bbPawns[BLACK] << 8)); + ASSERT(pHash->uNumRammedPawns <= 8); + + // Second pass, requires pHash->uCountPerFile[] populated for both + // colors. FOREACH_COLOR(uColor) { ASSERT(IS_VALID_COLOR(uColor)); + ULONG xColor = FLIP(uColor); + ASSERT(IS_VALID_COLOR(xColor)); ASSERT(CountBits(pos->bbPawns[uColor]) <= 8); d1 = 16 * g_iAhead[uColor]; - { - BITBOARD bbPawnsToWalk = pos->bbPawns[uColor]; + BITBOARD bbPawnsToWalk = pos->bbPawns[uColor]; - while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) - { - ASSERT(IS_ON_BOARD(c)); + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) + { ASSERT(IS_PAWN(pos->rgSquare[c].pPiece)); ASSERT((1 < RANK(c)) && (RANK(c) < 8)); - // // Find weak pawns... pawns that are isolated are weak as // are pawns that have advanced beyond their support. // Both of these can tie up one or more pieces in order to - // defend them. - // - uUnsupportable = 0; - uPawnFile = FILE(c) - 1; - if (pHash->uCountPerFile[uColor][uPawnFile + 1] > 0) - { - bb = (pos->bbPawns[uColor] & - BBFILE[uPawnFile] & - BBADJACENT_RANKS[RANK(c)]); - if (!bb) - { - bb = (pos->bbPawns[FLIP(uColor)] & - BBFILE[uPawnFile]); - while(IS_ON_BOARD(cSquare = - CoorFromBitBoardRank8ToRank1(&bb))) - { - ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece)); - if (uColor == WHITE) - { - if ((cSquare & 0xF0) >= (c & 0xF0)) - { - ASSERT(RANK(cSquare) <= RANK(c)); - uUnsupportable = 1; - break; - } - } - else - { - ASSERT(uColor == BLACK); - if ((cSquare & 0xF0) <= (c & 0xF0)) - { - ASSERT(RANK(cSquare) >= RANK(c)); - uUnsupportable = 1; - break; - } - } - } - } - } - else - { - uUnsupportable = 1; // no friend pawn on that file - } - if (0 == uUnsupportable) - { - goto fast_skip; - } - ASSERT(1 == uUnsupportable); - - uPawnFile = FILE(c) + 1; - if (pHash->uCountPerFile[uColor][uPawnFile + 1] > 0) - { - bb = (pos->bbPawns[uColor] & - BBFILE[uPawnFile] & - BBADJACENT_RANKS[RANK(c)]); - if (!bb) - { - bb = (pos->bbPawns[FLIP(uColor)] & - BBFILE[uPawnFile]); - while(IS_ON_BOARD(cSquare = - CoorFromBitBoardRank8ToRank1(&bb))) - { - ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece)); - if (uColor == WHITE) - { - if ((cSquare & 0xF0) >= (c & 0xF0)) - { - ASSERT(RANK(cSquare) <= RANK(c)); - uUnsupportable = 2; - break; - } - } - else - { - ASSERT(uColor == BLACK); - if ((cSquare & 0xF0) <= (c & 0xF0)) - { - ASSERT(RANK(cSquare) >= RANK(c)); - uUnsupportable = 2; - break; - } - } - } - } - } - else - { - uUnsupportable = 2; - } - - // - // If this pawn is not supportable from either side then - // it is either isolated or has been pushed too far ahead - // of its support. Either way it's a target -- penalize - // it. - // - if (2 == uUnsupportable) + // defend them. A pawn is a target unless a friendly pawn + // on *either* adjacent file can defend it, now or later + // (see _FileCanSupportPawn). bbBlockingRanks is c's rank + // plus every rank behind it (from uColor's forward + // direction) -- an enemy pawn there on an adjacent file + // permanently blocks that file from ever producing a + // defender. + bbBlockingRanks = BBRANK[RANK(c)] | BBPRECEEDING_RANKS[c >> 4][uColor]; + if (!_FileCanSupportPawn(pos, pHash, uColor, FILE(c) - 1, c, bbBlockingRanks) && + !_FileCanSupportPawn(pos, pHash, uColor, FILE(c) + 1, c, bbBlockingRanks)) { // // Single consolidated isolated-pawn penalty: base @@ -2244,14 +2297,13 @@ Return value: c, pHash->iScore[uColor], (ISOLATED_PAWN_BY_PAWNFILE[uPawnFile] + - ((pHash->uCountPerFile[FLIP(uColor)][uPawnFile]==0) * + ((pHash->uCountPerFile[xColor][uPawnFile]==0) * ISOLATED_EXPOSED_PAWN) + ((pHash->uCountPerFile[uColor][uPawnFile] > 1) * ISOLATED_DOUBLED_PAWN)), "isolated pawn"); } - fast_skip: uPawnFile = FILE(c) + 1; ASSERT((1 <= uPawnFile) && (uPawnFile <= 8)); ASSERT(CountBits(pos->bbPawns[uColor] & BBFILE[FILE(c)]) @@ -2285,7 +2337,7 @@ Return value: // #ifdef DEBUG t = ((uColor * RANK(cSquare)) + - (FLIP(uColor) * (9 - RANK(cSquare)))); + (xColor * (9 - RANK(cSquare)))); if (uColor == WHITE) { ASSERT(t == RANK(cSquare)); @@ -2297,54 +2349,41 @@ Return value: } #endif iDuos[uColor] += ((uColor * RANK(cSquare)) + - (FLIP(uColor) * (9 - RANK(cSquare)))); + (xColor * (9 - RANK(cSquare)))); ASSERT(iDuos[uColor] <= (7 * 8)); } - else if (pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) + else if (pos->bbPawnAttacks[xColor] & COOR_TO_BB(cSquare)) { // // Detect backwards pawns. See "Pawn Power in Chess" pp 25-27 // - if (!IS_PAWN(pos->rgSquare[cSquare].pPiece)) + if (_IsBackwardPawn(pos, uColor, c, cSquare)) { - p = (BLACK_PAWN | uColor); - if (((WHITE == uColor) && (RANK(c) < 4)) || - ((BLACK == uColor) && (RANK(c) > 5))) + // + // The pawn at c is backward. Determine whether + // it is shielded or exposed. + // + pHash->bbStationaryPawns[uColor] |= COOR_TO_BB(c); + ASSERT(CountBits(pHash->bbStationaryPawns[uColor]) <= 8); + if (pHash->uCountPerFile[xColor][uPawnFile]) { - if ((IS_ON_BOARD(cSquare - 1) && - pos->rgSquare[cSquare - 1].pPiece == p) || - (IS_ON_BOARD(cSquare + 1) && - pos->rgSquare[cSquare + 1].pPiece == p)) - { - // - // The pawn at c is backward. Determine - // whether it is shielded or exposed. - // - pHash->bbStationaryPawns[uColor] |= COOR_TO_BB(c); - ASSERT(CountBits(pHash->bbStationaryPawns[uColor]) - <= 8); - if (pHash->uCountPerFile[FLIP(uColor)][uPawnFile]) - { - // Tuning can (and has) flipped this sign; not a real invariant. - //ASSERT(BACKWARD_SHIELDED_BY_LOCATION[c] < 0); - EVAL_TERM(uColor, - PAWN, - c, - pHash->iScore[uColor], - BACKWARD_SHIELDED_BY_LOCATION[c], - "shielded backward pawn"); - } - else - { - //ASSERT(BACKWARD_EXPOSED_BY_LOCATION[c] < 0); - EVAL_TERM(uColor, - PAWN, - c, - pHash->iScore[uColor], - BACKWARD_EXPOSED_BY_LOCATION[c], - "exposed backward pawn"); - } - } + ASSERT(BACKWARD_SHIELDED_BY_LOCATION[c] < 0); + EVAL_TERM(uColor, + PAWN, + c, + pHash->iScore[uColor], + BACKWARD_SHIELDED_BY_LOCATION[c], + "shielded backward pawn"); + } + else + { + ASSERT(BACKWARD_EXPOSED_BY_LOCATION[c] < 0); + EVAL_TERM(uColor, + PAWN, + c, + pHash->iScore[uColor], + BACKWARD_EXPOSED_BY_LOCATION[c], + "exposed backward pawn"); } } // @@ -2357,7 +2396,6 @@ Return value: // Handle passers / candidate passers // _EvaluateCandidatePasser(pos, pHash, c); - } } } @@ -2412,13 +2450,6 @@ Return value: ASSERT(uIsolated[WHITE] <= 8); ASSERT(uIsolated[BLACK] >= 0); ASSERT(uIsolated[BLACK] <= 8); - // - // Removed: a redundant "exponential isolated" whole-position term - // keyed by uIsolated[color] used to be added here on top of the - // per-pawn "isolated pawn" term above, which already sums once per - // isolated pawn and so already scales with the same count. Having - // both double-counted the same structural fact. - // // // Look for connected, supported and outside passed pawns to give @@ -4572,22 +4603,17 @@ Return value: BITBOARD bbFile = BBFILE[FILE(c)]; BITBOARD bbFriendPawns = pos->bbPawns[uColor] & bbFile; BITBOARD bbEnemyPawns = pos->bbPawns[xColor] & bbFile; - // Collapsed 2026-09-06: friend-passer bonus used to distinguish - // rook-behind-passer (good, rank-scaled +1..+25) from rook-in- - // front-of-passer (bad, -3..-22) -- now a single flat bonus - // regardless of which side of the pawn the rook is on, by - // direct instruction. SCORE bonus = ROOK_FULL_HALF_OPEN_BONUS[bbFriendPawns != 0][bbEnemyPawns != 0]; bonus += (SCORE)((bbEnemyPawns & pHash->bbPasserLocations[xColor]) != 0) * ROOK_WITH_ENEMY_PASSER; bonus += (SCORE)((bbFriendPawns & pHash->bbPasserLocations[uColor]) != 0) * ROOK_WITH_FRIEND_PASSER; - + bonus += (SCORE)((COOR_TO_BB(c) & BBROOK_7TH_RANK[uColor]) != 0) * + ROOK_ON_SEVENTH_BONUS; EVAL_TERM(uColor, ROOK, c, pos->iScore[uColor], bonus, "file/passer bonus"); - // // Removed 2026-08-30 ("rook trapping enemy king"): a rook on the // 7th/8th rank aligned with the enemy king is exactly the |
