diff options
| author | Scott Gasch <[email protected]> | 2026-10-05 22:20:10 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-05 22:20:10 -0700 |
| commit | 5fbd9353fa9b1a80f37bcf0339f5546ac88091b8 (patch) | |
| tree | 5ac60b40603fd8c6fd051a44a24fd0e5e21425a6 /src/eval.c | |
| parent | bb7cdcda4c8c30ad0ed71d0f60427daf05977b24 (diff) | |
Cache good/bad-bishop pawn-structure score in the pawn hash
iBishopColorScore[color][complex] is a pure function of pawn placement
(own/enemy, stationary/transient) on a bishop's color complex, so it
doesn't need rescanning per-bishop -- computed once per pawn-hash miss
in _EvalPawns and reused by every bishop of that color/complex in
_EvalBishop, mirroring the same cache-the-per-structure-invariant idea
already used elsewhere in eval.c.
Also disables two speculative check-extension/futility-gating
experiments in search.c (#if 0, not deleted -- kept for reference)
that didn't hold up, and adds the BBLEFT/BBRIGHT bitboard constants
eval.c's bishop-mobility rewrite needs.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/eval.c')
| -rw-r--r-- | src/eval.c | 506 |
1 files changed, 240 insertions, 266 deletions
@@ -565,8 +565,6 @@ static SCORE ROOK_LEADS_PASSER_BY_PASSER_RANK[2][9] = { +0, +0, -3, -5, -9, -13, -16, -22, +0 } // white }; -static SCORE KING_TRAPPING_ROOK = -40; - static SCORE ROOK_VALUE_AS_PAWNS_COME_OFF[17] = {// 0 1 2 3 4 5 6 7 8 +55, +51, +44, +38, +33, +27, +22, +16, +7, @@ -591,7 +589,7 @@ static SCORE ROOK_CONNECTED_HORIZ = +4; // x2 // static SCORE ROOK_MOBILITY_BY_SQUARES[15] = {// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - -28, -24, -20, -14, -7, -2, +0, +4, +8, +12, +15, +17, +19, +21, +22 + -32, -26, -20, -14, -7, -2, +0, +4, +8, +12, +15, +17, +19, +21, +22 };// ^ | @@ -962,7 +960,6 @@ static DNA_BASE_SIZE g_EvalDNA[] = { 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), DNA_ARRAY(ROOK_VALUE_AS_PAWNS_COME_OFF), DNA_VAR(ROOK_CONNECTED_VERT), DNA_VAR(ROOK_CONNECTED_HORIZ), @@ -1549,12 +1546,12 @@ Return value: bb &= BBPRECEEDING_RANKS[(c & 0x70) >> 4][uColor]; if (!bb) { - ASSERT(CountBits(bb) == 0); + ASSERT(FastCountBits(bb) == 0); // There are no enemy sentries so this pawn is a passer. pHash->bbPasserLocations[uColor] |= COOR_TO_BB(c); - ASSERT(CountBits(pHash->bbPasserLocations[uColor]) > 0); - ASSERT(CountBits(pHash->bbPasserLocations[uColor]) <= 8); + ASSERT(FastCountBits(pHash->bbPasserLocations[uColor]) > 0); + ASSERT(FastCountBits(pHash->bbPasserLocations[uColor]) <= 8); EVAL_TERM(uColor, PAWN, c, @@ -1574,7 +1571,7 @@ Return value: } return; } - uSentries = CountBits(bb); + uSentries = FastCountBits(bb); // There's one or more sentry pawns so we'll look for friendly // helpers to decide if this pawn is a candidate passer. First @@ -1894,7 +1891,7 @@ Return value: ASSERT(RANK(cRightmostPasser) > 1); ASSERT(RANK(cLeftmostPasser) < 8); ASSERT(RANK(cRightmostPasser) < 8); - if (CountBits(pHash->bbPasserLocations[uColor]) == 1) + if (FastCountBits(pHash->bbPasserLocations[uColor]) == 1) { ASSERT(cLeftmostPasser == cRightmostPasser); } @@ -2220,18 +2217,18 @@ Return value: // 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]); + pHash->uCountPerFile[uColor][A + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[A]); + pHash->uCountPerFile[uColor][B + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[B]); + pHash->uCountPerFile[uColor][C + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[C]); + pHash->uCountPerFile[uColor][D + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[D]); + pHash->uCountPerFile[uColor][E + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[E]); + pHash->uCountPerFile[uColor][F + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[F]); + pHash->uCountPerFile[uColor][G + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[G]); + pHash->uCountPerFile[uColor][H + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[H]); ASSERT(pHash->uCountPerFile[uColor][9] == 0); // Count number of unmoved pawns. - pHash->uNumUnmovedPawns[uColor] = CountBits(pos->bbPawns[uColor] & + pHash->uNumUnmovedPawns[uColor] = FastCountBits(pos->bbPawns[uColor] & BBRANK[RANK(uUnmovedRank[uColor])]); ASSERT(pHash->uNumUnmovedPawns[uColor] <= 8); @@ -2247,7 +2244,7 @@ Return value: (uColor == WHITE) ? (pos->bbPawns[WHITE] & (bbAllPawns << 8)) : (pos->bbPawns[BLACK] & (bbAllPawns >> 8)); - ASSERT(CountBits(pHash->bbStationaryPawns[uColor]) <= 8); + ASSERT(FastCountBits(pHash->bbStationaryPawns[uColor]) <= 8); // Give central pawns a small bonus and rook pawns a small // penalty. @@ -2269,9 +2266,86 @@ Return value: // (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)); + FastCountBits(pos->bbPawns[WHITE] & (pos->bbPawns[BLACK] << 8)); ASSERT(pHash->uNumRammedPawns <= 8); + // Good/bad bishop pawn-structure score, cached per (color, color- + // complex) instead of rescanned by every bishop of that color/ + // complex in _EvalBishop (board_representation/EVAL.md-style note: + // every input here -- bbStationaryPawns, bbPawns, bbPawnAttacks -- + // is pure pawn-structure data already keyed by this pawn-hash + // entry's signature, and the bishop's own square only ever selects + // *which* of these two precomputed totals to use, never feeds the + // computation itself. Semantics (corrected from the old per-bishop + // version, which silently ignored enemy stationary pawns on the + // bishop's complex entirely): + // - own stationary pawns on the complex: always count (full + // weight) -- they're not moving, they wall the bishop in for + // good. + // - enemy stationary pawns on the complex: count (full weight) + // only if defended by another enemy pawn -- an undefended + // stationary pawn is still a static target, not a durable + // obstruction, the same reasoning the transient case below + // already applied to enemy pawns. + // - own transient (non-stationary) pawns on the complex: always + // count (full weight) -- might move off later, hence the + // separate, lighter table. + // - enemy transient pawns on the complex: count (half weight) + // only if defended -- unsupported ones are likely to be traded + // off soon, not a durable obstruction. + { + static const BITBOARD bbColorSq[2] = { 0x55aa55aa55aa55aaULL, + 0xaa55aa55aa55aa55ULL }; + ULONG uComplex; + + FOREACH_COLOR(uColor) + { + ULONG xColor = FLIP(uColor); + BITBOARD bbNonStationary = ~(pHash->bbStationaryPawns[WHITE] | + pHash->bbStationaryPawns[BLACK]); + + for (uComplex = 0; uComplex < 2; uComplex++) + { + BITBOARD bbMask = bbColorSq[uComplex]; + SCORE i = 16; + BITBOARD bb; + COOR cSq; + + bb = pHash->bbStationaryPawns[uColor] & bbMask; + while (IS_ON_BOARD(cSq = CoorFromBitBoardRank8ToRank1(&bb))) + { + i += STATIONARY_PAWN_ON_BISHOP_COLOR[cSq]; + } + + bb = pHash->bbStationaryPawns[xColor] & bbMask; + while (IS_ON_BOARD(cSq = CoorFromBitBoardRank8ToRank1(&bb))) + { + if (pos->bbPawnAttacks[xColor] & COOR_TO_BB(cSq)) + { + i += STATIONARY_PAWN_ON_BISHOP_COLOR[cSq]; + } + } + + bb = pos->bbPawns[uColor] & bbNonStationary & bbMask; + while (IS_ON_BOARD(cSq = CoorFromBitBoardRank8ToRank1(&bb))) + { + i += TRANSIENT_PAWN_ON_BISHOP_COLOR[cSq]; + } + + bb = pos->bbPawns[xColor] & bbNonStationary & bbMask; + while (IS_ON_BOARD(cSq = CoorFromBitBoardRank8ToRank1(&bb))) + { + if (pos->bbPawnAttacks[xColor] & COOR_TO_BB(cSq)) + { + i += TRANSIENT_PAWN_ON_BISHOP_COLOR[cSq] / 2; + } + } + + pHash->iBishopColorScore[uColor][uComplex] = (SHORT)i; + } + } + } + // Second pass, requires pHash->uCountPerFile[] populated for both // colors. FOREACH_COLOR(uColor) @@ -2279,7 +2353,7 @@ Return value: ASSERT(IS_VALID_COLOR(uColor)); ULONG xColor = FLIP(uColor); ASSERT(IS_VALID_COLOR(xColor)); - ASSERT(CountBits(pos->bbPawns[uColor]) <= 8); + ASSERT(FastCountBits(pos->bbPawns[uColor]) <= 8); d1 = 16 * g_iAhead[uColor]; BITBOARD bbPawnsToWalk = pos->bbPawns[uColor]; @@ -2333,7 +2407,7 @@ Return value: uPawnFile = FILE(c) + 1; ASSERT((1 <= uPawnFile) && (uPawnFile <= 8)); - ASSERT(CountBits(pos->bbPawns[uColor] & BBFILE[FILE(c)]) + ASSERT(FastCountBits(pos->bbPawns[uColor] & BBFILE[FILE(c)]) == pHash->uCountPerFile[uColor][uPawnFile]); // @@ -2391,7 +2465,7 @@ Return value: // it is shielded or exposed. // pHash->bbStationaryPawns[uColor] |= COOR_TO_BB(c); - ASSERT(CountBits(pHash->bbStationaryPawns[uColor]) <= 8); + ASSERT(FastCountBits(pHash->bbStationaryPawns[uColor]) <= 8); if (pHash->uCountPerFile[xColor][uPawnFile]) { ASSERT(BACKWARD_SHIELDED_BY_LOCATION[c] < 0); @@ -4213,7 +4287,6 @@ Return value: BITBOARD bb; BITBOARD bbMask; BITBOARD bbPc; - COOR cSquare; SCORE i; ULONG u; ULONG uTotalMobility; @@ -4230,60 +4303,26 @@ Return value: pos->uMinorsAtHome[uColor] += (c == cBishopAtHome[uColor][1]); ASSERT(pos->uMinorsAtHome[uColor] <= 4); - // Good and bad bishops given stationary pawn structure. - i = 16; + // Good and bad bishops given pawn structure (own and enemy, + // stationary and transient) on this bishop's color complex -- + // cached per (color, complex) in the pawn hash, since none of it + // depends on which square of that complex the bishop actually + // sits on. See the cache population in the pawn-hash-miss path + // above (_EvalPawns's FOREACH_COLOR block) for the full semantics. bbMask = bbColorSq[IS_SQUARE_WHITE(c)]; - bb = pHash->bbStationaryPawns[uColor] & bbMask; - ASSERT(CountBits(bb) <= 16); - while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb))) - { - // Consider stationary (rammed or backward) pawns of the same - // color as the bishop. - ASSERT(pos->rgSquare[cSquare].pPiece); - ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == uColor); - i += STATIONARY_PAWN_ON_BISHOP_COLOR[cSquare]; - } + i = pHash->iBishopColorScore[uColor][IS_SQUARE_WHITE(c)]; EVAL_TERM(uColor, BISHOP, c, pos->iScore[uColor], i, - "good/bad stationary pawns ++"); + "good/bad bishop pawns"); - i = 0; + // Still needed below: "non-stationary pawns of either color on + // this color complex," used by the mobility walk's + // BMOB_FRIEND_PAWN case (see its comment just below). bbPc = ~(pHash->bbStationaryPawns[WHITE] | - pHash->bbStationaryPawns[BLACK]); - bbPc &= bbMask; - bb = pos->bbPawns[uColor] & bbPc; - while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb))) - { - ASSERT(pos->rgSquare[cSquare].pPiece); - ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == uColor); - ASSERT(pos->bbPawns[uColor] & COOR_TO_BB(cSquare)); - i += TRANSIENT_PAWN_ON_BISHOP_COLOR[cSquare]; - } - - bb = pos->bbPawns[xColor] & bbPc; - while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb))) - { - // N.B. Only count enemy pawns that are supported by another - // pawn. - ASSERT(pos->rgSquare[cSquare].pPiece); - ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == xColor); - ASSERT(pos->bbPawns[xColor] & COOR_TO_BB(cSquare)); - i += - ((pos->bbPawnAttacks[xColor] & COOR_TO_BB(cSquare)) != 0) * - TRANSIENT_PAWN_ON_BISHOP_COLOR[cSquare] / 2; - } - EVAL_TERM(uColor, - BISHOP, - c, - pos->iScore[uColor], - i, - "good/bad transient pawns"); + pHash->bbStationaryPawns[BLACK]) & bbMask; // // Bishop mobility (and update attack tables) -- board_representation/ @@ -4379,7 +4418,7 @@ Return value: } pos->bbMinorXrayAttacks[uColor] |= bbXrayAccum; - uTotalMobility = CountBits(bbMobility); + uTotalMobility = FastCountBits(bbMobility); ASSERT(uTotalMobility <= 13); @@ -4652,7 +4691,7 @@ Return value: bbMobility = (bbAttack & bbEnemyNonPawnOcc) | (bbAttack & ~bbFriendOcc & ~bbEnemyNonPawnOcc & ~bbUnsafeForMinor); - uMobilitySquares = CountBits(bbMobility); + uMobilitySquares = FastCountBits(bbMobility); } ASSERT(uMobilitySquares >= 0); ASSERT(uMobilitySquares <= 8); @@ -4708,7 +4747,6 @@ Return value: ULONG uColor; ULONG uTotalMobility; BITBOARD bbMobility; - COOR cSquare; SCORE i; ASSERT(IS_ON_BOARD(c)); @@ -4735,17 +4773,6 @@ Return value: "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 - // geometric pattern CountKingSafetyDefects' CHECK_VECTOR - // line-of-sight scan already picks up into - // uPiecesPointingAtKing/king-safety -- this was pricing the same - // king-danger fact a second time via a rook-specific bonus. Belongs - // in king safety, not here. (pFriendRook, only ever assigned in - // this block and never read, is gone too.) - // - - // // Rooks increase in value as pawns come off: // // "A further refinement would be to raise the knight's value by @@ -4759,9 +4786,12 @@ Return value: ROOK, c, pos->iScore[uColor], - ROOK_VALUE_AS_PAWNS_COME_OFF[FastCountBits(pos->bbPawns[uColor]) + - FastCountBits(pos->bbPawns[xColor])], + ROOK_VALUE_AS_PAWNS_COME_OFF[ + FastCountBits(pos->bbPawns[BLACK] | pos->bbPawns[WHITE]) + ], "increase in value/pawns"); + ASSERT((FastCountBits(pos->bbPawns[uColor]) + FastCountBits(pos->bbPawns[xColor])) == + (FastCountBits(pos->bbPawns[uColor] | pos->bbPawns[xColor]))); // // Rook mobility (and update attack tables) -- board_representation/ @@ -4788,106 +4818,103 @@ Return value: // credit at the blocker itself, matching the old table); a // friendly rook blocker also earns the connected-rook bonus. // - { - BITBOARD bbAttack = _RookAttacksBB(c, pos->bbOccupied); - BITBOARD bbEnemyOcc = _BuildFriendlySideBB(pos, xColor); - BITBOARD bbEnemySame = pos->bbPieces[xColor][ROOK]; - BITBOARD bbEnemyGEContinue = pos->bbPieces[xColor][QUEEN] | - COOR_TO_BB(KingPosition(pos, xColor)); - BITBOARD bbFriendRQ = pos->bbPieces[uColor][ROOK] | - pos->bbPieces[uColor][QUEEN]; - BITBOARD bbUnsafeForRook = pos->bbPawnAttacks[xColor] | - pos->bbMinorAttacks[xColor]; - BITBOARD bbExclude = 0; - BITBOARD bbSeen = 0; - BITBOARD bbLayer = bbAttack; - BITBOARD bbXrayAccum = 0; - BITBOARD bbFirstLayerMask = 0; - bbMobility = 0; - pos->bbRookAttacks[uColor] |= bbAttack; + BITBOARD bbAttack = _RookAttacksBB(c, pos->bbOccupied); + BITBOARD bbEnemyOcc = _BuildFriendlySideBB(pos, xColor); + BITBOARD bbEnemySame = pos->bbPieces[xColor][ROOK]; + BITBOARD bbEnemyGEContinue = pos->bbPieces[xColor][QUEEN] | + COOR_TO_BB(KingPosition(pos, xColor)); + BITBOARD bbFriendRQ = pos->bbPieces[uColor][ROOK] | + pos->bbPieces[uColor][QUEEN]; + BITBOARD bbUnsafeForRook = pos->bbPawnAttacks[xColor] | + pos->bbMinorAttacks[xColor]; + BITBOARD bbExclude = 0; + BITBOARD bbSeen = 0; + BITBOARD bbLayer = bbAttack; + BITBOARD bbXrayAccum = 0; + BITBOARD bbFirstLayerMask = 0; - // - // Unified chain walk over both mobility and x-ray population - // in one pass, board_representation/EVAL.md section 9. The - // old mailbox walk's RMOB_FRIEND_ROOK/_QUEEN and - // RMOB_ENEMY_GREATER cases don't just x-ray past their blocker - // for attack-bit purposes -- they keep walking (fStop=FALSE) - // and keep crediting mobility for whatever safe/empty squares - // and further captures lie beyond, however many such blockers - // are stacked on one ray. A first version of this conversion - // (committed, since fixed) only handled the near side (up to - // the first blocker) for mobility and treated the chain purely - // as an attack-bit population exercise -- caught by comparing - // against the old mailbox walk directly on a battery position - // (two same-color rooks on an open file: old code credited 13 - // mobility to the far rook's near companion via squares beyond - // it, the near-side-only version credited only 9). Fixed by - // reusing the exact same "recompute with blockers excluded" - // chain used for x-ray population to also accumulate mobility - // credit at every layer, not just the first. - // - // Each iteration classifies the newly-revealed blockers on - // each still-open ray: friendly rook/queen and enemy queen/ - // king continue the chain (recompute with them excluded too); - // everything else (friendly non-R/Q, enemy pawn/knight/bishop, - // enemy rook, or the board edge) stops that ray. Bounded: each - // iteration either terminates or consumes at least one new - // piece into the exclusion set, so this can loop at most - // once per piece on the board. - // - for (;;) - { - BITBOARD bbNew = bbLayer & ~bbSeen; - BITBOARD bbBlockersHere = bbNew & pos->bbOccupied; - BITBOARD bbEmptyHere = bbNew & ~pos->bbOccupied; - BITBOARD bbFriendRQHere = bbBlockersHere & bbFriendRQ; - BITBOARD bbEnemySameHere = bbBlockersHere & bbEnemySame; - BITBOARD bbEnemyGEHere = bbBlockersHere & bbEnemyGEContinue; - BITBOARD bbEnemyLEHere = bbBlockersHere & bbEnemyOcc & - ~bbEnemySameHere & ~bbEnemyGEHere; - BITBOARD bbFriendRookHere = bbBlockersHere & - pos->bbPieces[uColor][ROOK]; - BITBOARD bbContinueHere; + bbMobility = 0; + pos->bbRookAttacks[uColor] |= bbAttack; - bbSeen |= bbNew; - bbXrayAccum |= (bbNew & bbFirstLayerMask); - bbFirstLayerMask = ~(BITBOARD)0; + // + // Unified chain walk over both mobility and x-ray population + // in one pass, board_representation/EVAL.md section 9. The + // old mailbox walk's RMOB_FRIEND_ROOK/_QUEEN and + // RMOB_ENEMY_GREATER cases don't just x-ray past their blocker + // for attack-bit purposes -- they keep walking (fStop=FALSE) + // and keep crediting mobility for whatever safe/empty squares + // and further captures lie beyond, however many such blockers + // are stacked on one ray. A first version of this conversion + // (committed, since fixed) only handled the near side (up to + // the first blocker) for mobility and treated the chain purely + // as an attack-bit population exercise -- caught by comparing + // against the old mailbox walk directly on a battery position + // (two same-color rooks on an open file: old code credited 13 + // mobility to the far rook's near companion via squares beyond + // it, the near-side-only version credited only 9). Fixed by + // reusing the exact same "recompute with blockers excluded" + // chain used for x-ray population to also accumulate mobility + // credit at every layer, not just the first. + // + // Each iteration classifies the newly-revealed blockers on + // each still-open ray: friendly rook/queen and enemy queen/ + // king continue the chain (recompute with them excluded too); + // everything else (friendly non-R/Q, enemy pawn/knight/bishop, + // enemy rook, or the board edge) stops that ray. Bounded: each + // iteration either terminates or consumes at least one new + // piece into the exclusion set, so this can loop at most + // once per piece on the board. + // + for (;;) + { + BITBOARD bbNew = bbLayer & ~bbSeen; + BITBOARD bbBlockersHere = bbNew & pos->bbOccupied; + BITBOARD bbEmptyHere = bbNew & ~pos->bbOccupied; + BITBOARD bbFriendRQHere = bbBlockersHere & bbFriendRQ; + BITBOARD bbEnemySameHere = bbBlockersHere & bbEnemySame; + BITBOARD bbEnemyGEHere = bbBlockersHere & bbEnemyGEContinue; + BITBOARD bbEnemyLEHere = bbBlockersHere & bbEnemyOcc & + ~bbEnemySameHere & ~bbEnemyGEHere; + BITBOARD bbFriendRookHere = bbBlockersHere & + pos->bbPieces[uColor][ROOK]; + BITBOARD bbContinueHere; - bbMobility |= (bbEmptyHere & ~bbUnsafeForRook) | - bbEnemySameHere | bbEnemyGEHere | - (bbEnemyLEHere & ~bbUnsafeForRook); + bbSeen |= bbNew; + bbXrayAccum |= (bbNew & bbFirstLayerMask); + bbFirstLayerMask = ~(BITBOARD)0; - // - // Connected-rook bonus fires at every friendly rook found - // along the chain, not just the first (matches the old - // walk: each RMOB_FRIEND_ROOK hit its own EVAL_TERM call). - // - while (bbFriendRookHere) - { - COOR cBlocker = CoorFromBitBoardRank8ToRank1(&bbFriendRookHere); - FLAG fHoriz = (RANK(cBlocker) == RANK(c)); - EVAL_TERM(uColor, - ROOK, - cBlocker, - pos->iScore[uColor], - (ROOK_CONNECTED_HORIZ * fHoriz + - ROOK_CONNECTED_VERT * FLIP(fHoriz)), - "rook connected"); - } + bbMobility |= (bbEmptyHere & ~bbUnsafeForRook) | + bbEnemySameHere | bbEnemyGEHere | + (bbEnemyLEHere & ~bbUnsafeForRook); - bbContinueHere = bbFriendRQHere | bbEnemyGEHere; - if (!bbContinueHere) - { - break; - } - bbExclude |= bbContinueHere; - bbLayer = _RookAttacksBB(c, pos->bbOccupied & ~bbExclude); + // Connected-rook bonus fires at every friendly rook found + // along the chain, not just the first (matches the old + // walk: each RMOB_FRIEND_ROOK hit its own EVAL_TERM call). + while (bbFriendRookHere) + { + COOR cBlocker = CoorFromBitBoardRank8ToRank1(&bbFriendRookHere); + FLAG fHoriz = (RANK(cBlocker) == RANK(c)); + EVAL_TERM(uColor, + ROOK, + cBlocker, + pos->iScore[uColor], + (ROOK_CONNECTED_HORIZ * fHoriz + + ROOK_CONNECTED_VERT * FLIP(fHoriz)), + "rooks connected"); } - pos->bbRookXrayAttacks[uColor] |= bbXrayAccum; - uTotalMobility = CountBits(bbMobility); + bbContinueHere = bbFriendRQHere | bbEnemyGEHere; + if (!bbContinueHere) + { + break; + } + bbExclude |= bbContinueHere; + bbLayer = _RookAttacksBB(c, pos->bbOccupied & ~bbExclude); } + pos->bbRookXrayAttacks[uColor] |= bbXrayAccum; + + uTotalMobility = FastCountBits(bbMobility); ASSERT(uTotalMobility <= 14); ASSERT(pos->uArmyScaler[xColor] <= 31); @@ -4899,64 +4926,17 @@ Return value: // nonzero count here is still liable to be an overcount. if (uTotalMobility < EVAL_MOBILITY_CAREFULLY_BELOW) { - if (uTotalMobility == 0) - { - i = ROOK_MOBILITY_BY_SQUARES[0]; - i *= (int)(REDUCED_MATERIAL_UP_SCALER[pos->uArmyScaler[xColor]] + 1); - i /= 8; - EVAL_TERM(uColor, - ROOK, - c, - pos->iScore[uColor], - i, - "rook mobility"); - } _RecordTrappedCandidate(pos, uColor, c, bbMobility); - - // Is the rook trapped in the corner by a stupid friendly - // king? -- purely geometric (unrelated to the mobility - // deferral above), still gated on the same coarse "cramped" - // raw-mobility threshold -- color-indexed instead of a - // WHITE/BLACK branch over two mirrored blocks - // (board_representation/EVAL.md section 9). - ASSERT(IS_VALID_COLOR(uColor)); - ASSERT(IS_ON_BOARD(c)); - - static const ULONG BACK_RANK_MASK[2] = { 0x00, 0x70 }; // BLACK, WHITE - static const COOR E_CORNER[2] = { E8, E1 }; - static const COOR D_CORNER[2] = { D8, D1 }; - - if ((c & 0xF0) == BACK_RANK_MASK[uColor]) - { - cSquare = KingPosition(pos, uColor); - ASSERT(IS_ON_BOARD(cSquare)); - if ((cSquare & 0xF0) == BACK_RANK_MASK[uColor]) - { - if (((cSquare > E_CORNER[uColor]) && (c > cSquare)) || - ((cSquare < D_CORNER[uColor]) && (c < cSquare))) - { - EVAL_TERM(uColor, - ROOK, - c, - pos->iScore[uColor], - KING_TRAPPING_ROOK, - "king trapping rook"); - } - } - } - } - else // reasonable mobility - { - i = ROOK_MOBILITY_BY_SQUARES[uTotalMobility]; - i *= (int)(REDUCED_MATERIAL_UP_SCALER[pos->uArmyScaler[xColor]] + 1); - i /= 8; - EVAL_TERM(uColor, - ROOK, - c, - pos->iScore[uColor], - i, - "rook mobility"); } + i = ROOK_MOBILITY_BY_SQUARES[uTotalMobility]; + i *= (int)(REDUCED_MATERIAL_UP_SCALER[pos->uArmyScaler[xColor]] + 1); + i /= 8; + EVAL_TERM(uColor, + ROOK, + c, + pos->iScore[uColor], + i, + "rook mobility"); } @@ -5104,7 +5084,7 @@ Return value: pos->bbQueenAttacks[uColor] |= bbAttackTotal; pos->bbQueenXrayAttacks[uColor] |= bbXrayAccum; - uTotalMobility = CountBits(bbMobility); + uTotalMobility = FastCountBits(bbMobility); } ASSERT(uTotalMobility <= 27); @@ -5177,7 +5157,7 @@ Return value: // instruction, since this is the hottest inner loop in eval.c and // scaler*(A+B+C+...) is one multiply instead of scaler*A + scaler*B // + ... Ring-square attack/defense/flight terms below are computed - // via bitboard intersection + CountBits() against the whole front/ + // via bitboard intersection + FastCountBits() against the whole front/ // back/side bucket at once (g_KingRingFrontBB/g_KingRingBackBB/ // g_KingRingSideBB, data.c), not a per-square branchy loop -- a // fixed handful of AND+popcount ops regardless of how many ring @@ -5266,7 +5246,7 @@ Return value: (SCORE)KING_COUNTER_BY_ATTACK_PATTERN[bvPattern]; // Count how many ring squares are attacked by the enemy queen. - uQueenNearKing = CountBits( + uQueenNearKing = FastCountBits( bbRing & (pos->bbQueenAttacks[xColor] | pos->bbQueenXrayAttacks[xColor]) ); ASSERT(uQueenNearKing <= 8); @@ -5274,45 +5254,45 @@ Return value: // Minor attacks at front, side, back. iSafety += KING_RING_ATTACK_MINOR[0] * - (SCORE)CountBits(bbFront & pos->bbMinorAttacks[xColor]); + (SCORE)FastCountBits(bbFront & pos->bbMinorAttacks[xColor]); iSafety += KING_RING_ATTACK_MINOR[1] * - (SCORE)CountBits(bbBack & pos->bbMinorAttacks[xColor]); + (SCORE)FastCountBits(bbBack & pos->bbMinorAttacks[xColor]); iSafety += KING_RING_ATTACK_MINOR[2] * - (SCORE)CountBits(bbSide & pos->bbMinorAttacks[xColor]); + (SCORE)FastCountBits(bbSide & pos->bbMinorAttacks[xColor]); // Rook attacks at front, side, back. iSafety += KING_RING_ATTACK_ROOK[0] * - (SCORE)CountBits(bbFront & pos->bbRookAttacks[xColor]); + (SCORE)FastCountBits(bbFront & pos->bbRookAttacks[xColor]); iSafety += KING_RING_ATTACK_ROOK[1] * - (SCORE)CountBits(bbBack & pos->bbRookAttacks[xColor]); + (SCORE)FastCountBits(bbBack & pos->bbRookAttacks[xColor]); iSafety += KING_RING_ATTACK_ROOK[2] * - (SCORE)CountBits(bbSide & pos->bbRookAttacks[xColor]); + (SCORE)FastCountBits(bbSide & pos->bbRookAttacks[xColor]); // Pawn attacks. iSafety += KING_RING_ATTACK_PAWN * - (SCORE)CountBits(bbRing & pos->bbPawnAttacks[xColor]); + (SCORE)FastCountBits(bbRing & pos->bbPawnAttacks[xColor]); // Undefended squares in front, side, back. iSafety += KING_RING_UNDEFENDED[0] * - (SCORE)CountBits(bbFront & bbUndefended); + (SCORE)FastCountBits(bbFront & bbUndefended); iSafety += KING_RING_UNDEFENDED[1] * - (SCORE)CountBits(bbBack & bbUndefended); + (SCORE)FastCountBits(bbBack & bbUndefended); iSafety += KING_RING_UNDEFENDED[2] * - (SCORE)CountBits(bbSide & bbUndefended); + (SCORE)FastCountBits(bbSide & bbUndefended); // Only xray attack, no direct attack. iSafety += KING_RING_XRAY_BONUS * - (SCORE)CountBits(bbRing & bbEnemyXrayAny & ~bbEnemyAtkAny); + (SCORE)FastCountBits(bbRing & bbEnemyXrayAny & ~bbEnemyAtkAny); // Flight squares are empty, unattacked squares around the king. // In front of the king, though, these are gaps and a negative // whereas behind the king they are legitimately small bonuses. iSafety += KING_RING_FRONT_FLIGHT_PENALTY * - (SCORE)CountBits(bbFront & bbEmpty & ~bbEnemyAtkAny); + (SCORE)FastCountBits(bbFront & bbEmpty & ~bbEnemyAtkAny); // Small bonus for empty squares behind the king to retreat into. iSafety += KING_BACK_FLIGHT_BONUS * - (SCORE)CountBits(bbBack & bbEmpty & ~bbEnemyAtkAny); + (SCORE)FastCountBits(bbBack & bbEmpty & ~bbEnemyAtkAny); #ifdef KING_DEFECT_DUMP { @@ -5992,7 +5972,7 @@ Return value: bb = pHash->bbPasserLocations[uColor]; if (bb != 0) { - uNumPassers[uColor] = CountBits(bb); + uNumPassers[uColor] = FastCountBits(bb); ASSERT(uNumPassers[uColor] > 0); d1 = 16 * g_iAhead[uColor]; ASSERT((d1 * 2) == (32 * g_iAhead[uColor])); @@ -6379,7 +6359,7 @@ Return value: // EVAL_MOBILITY_CAREFULLY_BELOW's comment. bbUnsafeNow = pos->bbKingAttacks[xColor] & ~bbOwnDefend; bbStillSafe = pos->bbTrappedEscapes[uColor][u] & ~bbUnsafeNow; - uFinalMobility = CountBits(bbStillSafe); + uFinalMobility = FastCountBits(bbStillSafe); if (IS_BISHOP(p)) { @@ -7078,13 +7058,13 @@ Return value: **/ { POSITION *pos = &(ctx->sPosition); + ULONG uColor = pos->uToMove; + ULONG xColor = FLIP(uColor); SCORE iScoreForSideToMove; SCORE iAlphaMargin, iBetaMargin; PAWN_HASH_ENTRY *pHash; COOR c; ULONG u; - ULONG uColor = pos->uToMove; - ULONG xColor = FLIP(uColor); BITBOARD bb; FLAG fDeferred; // Computed at most once per Eval() call, on first need, by @@ -7129,17 +7109,8 @@ Return value: #ifdef EVAL_TIME UINT64 u64MaterialSetupTimer = SystemReadTimeStampCounter(); #endif - pos->iScore[uColor] = - (PawnMaterial(pos, uColor) + pos->uNonPawnMaterial[uColor]); - pos->iScore[xColor] = - (PawnMaterial(pos, xColor) + pos->uNonPawnMaterial[xColor]); #ifdef EVAL_DUMP EvalTraceClear(); - Trace("Material:\n%d\t\t%d\t\t(%d for %s)\n", - pos->iScore[WHITE], pos->iScore[BLACK], - (pos->iScore[uColor] - pos->iScore[xColor]), - COLOR_NAME(uColor) - ); #endif // Initialize army scalers here (moved ahead of the super-lazy exit @@ -7233,7 +7204,7 @@ Return value: #ifdef EVAL_TIME UINT64 uSuperLazyTimer = SystemReadTimeStampCounter(); #endif - iScoreForSideToMove = (pos->iScore[uColor] -pos->iScore[xColor]); + iScoreForSideToMove = pos->iMaterialBalance[uColor]; ASSERT(IS_VALID_SCORE(iScoreForSideToMove)); // We have a super-rough eval -- material only at this point. @@ -7302,6 +7273,7 @@ Return value: goto end; #endif } + if (iScoreForSideToMove - iSuperLazySelfPessEnemyOptMargin > iBeta) { ASSERT(iScoreForSideToMove > iBeta); @@ -7380,11 +7352,13 @@ Return value: #endif ASSERT(NULL != pHash); ASSERT(IS_VALID_FLAG(fDeferred)); - pos->iScore[WHITE] += pHash->iScore[WHITE]; - pos->iScore[BLACK] += pHash->iScore[BLACK]; + pos->iScore[uColor] = + (PawnMaterial(pos, uColor) + pos->uNonPawnMaterial[uColor] + pHash->iScore[uColor]); + pos->iScore[xColor] = + (PawnMaterial(pos, xColor) + pos->uNonPawnMaterial[xColor] + pHash->iScore[xColor]); ASSERT(IS_VALID_SCORE(pos->iScore[WHITE] - pos->iScore[BLACK])); #ifdef EVAL_DUMP - Trace("After pawns:\n%d\t\t%d\t\t(%d for %s)\n", + Trace("After pawns + material:\n%d\t\t%d\t\t(%d for %s)\n", pos->iScore[WHITE], pos->iScore[BLACK], (pos->iScore[uColor] - pos->iScore[xColor]), COLOR_NAME(uColor) |
