diff options
| -rwxr-xr-x | src/chess.h | 13 | ||||
| -rwxr-xr-x | src/eval.c | 185 |
2 files changed, 108 insertions, 90 deletions
diff --git a/src/chess.h b/src/chess.h index 7ff9994..2e9189a 100755 --- a/src/chess.h +++ b/src/chess.h @@ -716,6 +716,19 @@ typedef struct _POSITION // bbRookAttacks/bbQueenAttacks that will retire the rest of it. BITBOARD bbPawnAttacks[2]; + // First mover of the bbMinorAttacks pair (knight only for now -- + // bishop is a separate, later step; see board_representation/ + // EVAL.md section 2). "Which squares does this side's knight(s) + // attack" -- computed via g_KnightAttacksBB[c] and OR'd in per + // knight, Eval()-scoped scratch like bbPawnAttacks above, cleared + // once per Eval() call in _ClearAttackTables since (unlike + // bbPawnAttacks) it's accumulated across multiple pieces via |=, + // not assigned wholesale. Bishop still writes its own minor-bit + // contribution into the old rgSquare[c|8].bvAttacks structure for + // now -- every consumer that needs "any minor's attack," not just + // knight's, ORs both sources together until bishop converts too. + BITBOARD bbMinorAttacks[2]; + ULONG uWhiteSqBishopCount[2]; // num bishops on white squares SCORE iMaterialBalance[2]; // material balance @@ -1152,6 +1152,59 @@ Return value: } +// +// Transitional helper, board_representation/EVAL.md section 2: during +// the piece-by-piece migration off bvAttacks, knight now writes its +// attacks into pos->bbMinorAttacks directly while bishop (not +// converted yet) still writes the old per-square +// rgSquare[c|8].bvAttacks[color].small.uMinor bit. Every consumer +// that needs "does any minor (knight or bishop) attack this square" +// goes through this one function instead of each hand-rolling its own +// OR of the two sources -- once bishop converts too, this collapses +// to a plain pos->bbMinorAttacks[color] read and this whole function +// goes away; it is not meant to be a permanent fixture. +// +// DEBUG-only: cross-checked against an independent recomputation +// (g_KnightAttacksBB / _BishopAttacksBB, both already-trusted +// primitives used elsewhere in move generation, entirely separate +// code from either the ray-walk's uBit bookkeeping or the new +// bitboard population) so a bug in either mechanism fails loudly here +// instead of silently drifting into a wrong score several plies deep +// in search -- exactly the failure mode a prior, larger version of +// this same migration hit and had to be rolled back for. +// +static FLAG +_IsSquareAttackedByMinor(IN POSITION *pos, + IN ULONG uColor, + IN COOR c) +{ + BITBOARD sq = COOR_TO_BB(c); + FLAG fResult = ((pos->bbMinorAttacks[uColor] & sq) != 0) || + (pos->rgSquare[c|8].bvAttacks[uColor].small.uMinor != 0); +#ifdef DEBUG + { + BITBOARD bbTrueMinorAttacks = 0; + ULONG u; + for (u = 1; u < pos->uNonPawnCount[uColor][0]; u++) + { + COOR cPiece = pos->cNonPawns[uColor][u]; + PIECE p = pos->rgSquare[cPiece].pPiece; + if (IS_KNIGHT(p)) + { + bbTrueMinorAttacks |= g_KnightAttacksBB[cPiece]; + } + else if (IS_BISHOP(p)) + { + bbTrueMinorAttacks |= _BishopAttacksBB(cPiece, pos->bbOccupied); + } + } + ASSERT(((bbTrueMinorAttacks & sq) != 0) == (fResult != 0)); + } +#endif + return fResult; +} + + static ULONG _WhoControlsSquareFast(IN POSITION *pos, IN COOR c) @@ -1186,11 +1239,13 @@ Return value: ULONG uWhite = pos->rgSquare[c|8].bvAttacks[WHITE].uSmall | pos->rgSquare[c|8].bvAttacks[WHITE].uXray | ((pos->bbPawnAttacks[WHITE] & COOR_TO_BB(c)) ? - PAWN_BIT : 0); + PAWN_BIT : 0) | + (_IsSquareAttackedByMinor(pos, WHITE, c) ? MINOR_BIT : 0); ULONG uBlack = pos->rgSquare[c|8].bvAttacks[BLACK].uSmall | pos->rgSquare[c|8].bvAttacks[BLACK].uXray | ((pos->bbPawnAttacks[BLACK] & COOR_TO_BB(c)) ? - PAWN_BIT : 0); + PAWN_BIT : 0) | + (_IsSquareAttackedByMinor(pos, BLACK, c) ? MINOR_BIT : 0); ULONG u; PIECE p; CHAR ch; @@ -1260,6 +1315,7 @@ static void _ClearAttackTables(IN OUT POSITION *pos) { register COOR c = 8; + pos->bbMinorAttacks[WHITE] = pos->bbMinorAttacks[BLACK] = 0; #if 1 CLEAR_A_SQ; c += 16; CLEAR_A_SQ; c += 16; @@ -2840,41 +2896,6 @@ Return value: **/ { static const int iPawnStart[2] = { -17, +15 }; - static const UCHAR NMobCaseTable[2][14] = - { - {// (black) - NMOB_MOBILE_SQUARE, // EMPTY_SQUARE (0) - NMOB_INVALID, // INVALID_PIECE (1) - NMOB_FRIEND, // BLACK_PAWN (2) - NMOB_MOBILE_SQUARE, // WHITE_PAWN (3) - NMOB_FRIEND, // BLACK_KNIGHT (4) - NMOB_ENEMY_OTHER, // WHITE_KNIGHT (5) - NMOB_FRIEND, // BLACK_BISHOP (6) - NMOB_ENEMY_OTHER, // WHITE_BISHOP (7) - NMOB_FRIEND, // BLACK_ROOK (8) - NMOB_ENEMY_OTHER, // WHITE_ROOK (9) - NMOB_FRIEND, // BLACK_QUEEN (10) - NMOB_ENEMY_OTHER, // WHITE_QUEEN (11) - NMOB_FRIEND, // BLACK_KING (12) - NMOB_ENEMY_OTHER, // WHITE_KING (13) - }, - {// (white) - NMOB_MOBILE_SQUARE, // EMPTY_SQUARE (0) - NMOB_INVALID, // INVALID_PIECE (1) - NMOB_MOBILE_SQUARE, // BLACK_PAWN (2) - NMOB_FRIEND, // WHITE_PAWN (3) - NMOB_ENEMY_OTHER, // BLACK_KNIGHT (4) - NMOB_FRIEND, // WHITE_KNIGHT (5) - NMOB_ENEMY_OTHER, // BLACK_BISHOP (6) - NMOB_FRIEND, // WHITE_BISHOP (7) - NMOB_ENEMY_OTHER, // BLACK_ROOK (8) - NMOB_FRIEND, // WHITE_ROOK (9) - NMOB_ENEMY_OTHER, // BLACK_QUEEN (10) - NMOB_FRIEND, // WHITE_QUEEN (11) - NMOB_ENEMY_OTHER, // BLACK_KING (12) - NMOB_FRIEND, // WHITE_KING (13) - } - }; static const COOR cKnightAtHome[2][2] = { { B8, G8 }, // BLACK @@ -2885,7 +2906,6 @@ Return value: BITBOARD bb; ULONG uColor; ULONG uPawnsSupporting; - ULONG u; ULONG uMobilitySquares; SCORE i; ULONG uDist; @@ -3045,57 +3065,34 @@ Return value: // // - // Do mobilility and piece relevance. Also update attack tables. + // Do mobility and piece relevance. Also update attack tables. + // + // board_representation/EVAL.md section 1b/2: g_KnightAttacksBB[c] + // (generate.c's precomputed table, already used by move + // generation) is exactly the old per-square g_iNDeltas walk's + // destination set, IS_ON_BOARD baked in at table-build time -- no + // per-square branch, no mailbox read, no switch. Knights never + // x-ray or have a battery partner, so the old NMobCaseTable's four + // cases collapse to two bitboard masks: NMOB_ENEMY_OTHER (any + // enemy non-pawn -- count unconditionally) and NMOB_MOBILE_SQUARE + // (empty or enemy pawn -- count unless pawn-unsafe). NMOB_FRIEND + // needs no term at all, it's just "neither of the above." // - uMobilitySquares = 0; - u = 0; - ASSERT(g_iNDeltas[u] != 0); - do { - cSquare = c + g_iNDeltas[u]; - if (IS_ON_BOARD(cSquare)) - { - // - // Always update the attack bits - // - ASSERT((cSquare + 8) == (cSquare | 8)); - pos->rgSquare[cSquare|8].bvAttacks[uColor].small.uMinor = 1; - - // - // See what we hit. Dispatched via switch instead of an - // indirect call through a function pointer -- same - // rationale as the bishop ray-walk above. - // - p = pos->rgSquare[cSquare].pPiece; - switch (NMobCaseTable[uColor][p]) - { - case NMOB_MOBILE_SQUARE: - uMobilitySquares += - !(pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)); - break; + BITBOARD bbAttack = g_KnightAttacksBB[c]; + BITBOARD bbFriendOcc = _BuildFriendlySideBB(pos, uColor); + BITBOARD bbEnemyNonPawnOcc = _BuildFriendlySideBB(pos, FLIP(uColor)) & + ~pos->bbPawns[FLIP(uColor)]; + BITBOARD bbUnsafeForMinor = pos->bbPawnAttacks[FLIP(uColor)]; + BITBOARD bbMobility; - case NMOB_ENEMY_OTHER: - uMobilitySquares += 1; - break; - - case NMOB_FRIEND: - break; - - case NMOB_INVALID: - default: - UtilPanic(SHOULD_NOT_GET_HERE, - NULL, NULL, NULL, NULL, - __FILE__, __LINE__); - break; - } + pos->bbMinorAttacks[uColor] |= bbAttack; - // - // IDEA: bonus for hitting friendly pawn? - // - } - u++; + bbMobility = (bbAttack & bbEnemyNonPawnOcc) | + (bbAttack & ~bbFriendOcc & ~bbEnemyNonPawnOcc & + ~bbUnsafeForMinor); + uMobilitySquares = CountBits(bbMobility); } - while(g_iNDeltas[u] != 0); ASSERT(uMobilitySquares >= 0); ASSERT(uMobilitySquares <= 8); EVAL_TERM(uColor, @@ -3381,14 +3378,14 @@ Return value: case RMOB_EMPTY: uCurrentMobility += !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) || - UNSAFE_FOR_ROOK(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)])); + _IsSquareAttackedByMinor(pos, FLIP(uColor), cSquare)); fStop = FALSE; break; case RMOB_ENEMY_LESS: uCurrentMobility += !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) || - UNSAFE_FOR_ROOK(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)])); + _IsSquareAttackedByMinor(pos, FLIP(uColor), cSquare)); fStop = TRUE; break; @@ -3684,14 +3681,18 @@ Return value: case QMOB_EMPTY: uTotalMobility += !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) || - UNSAFE_FOR_QUEEN(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)])); + _IsSquareAttackedByMinor(pos, FLIP(uColor), cSquare) || + (pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)].uWholeThing & + ROOK_BIT)); fStop = FALSE; break; case QMOB_ENEMY_LESS: uTotalMobility += !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) || - UNSAFE_FOR_QUEEN(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)])); + _IsSquareAttackedByMinor(pos, FLIP(uColor), cSquare) || + (pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)].uWholeThing & + ROOK_BIT)); fStop = TRUE; break; @@ -3935,7 +3936,9 @@ Return value: // bvAttack = pos->rgSquare[cSquare|8].bvAttacks[ufColor].uSmall | ((pos->bbPawnAttacks[ufColor] & COOR_TO_BB(cSquare)) ? - PAWN_BIT : 0); + PAWN_BIT : 0) | + (_IsSquareAttackedByMinor(pos, ufColor, cSquare) ? + MINOR_BIT : 0); { COOR cRealSquare = cSquare; cSquare |= 8; @@ -3943,7 +3946,9 @@ Return value: pos->rgSquare[cSquare].bvAttacks[uColor].small.uKing = 1; bvDefend = pos->rgSquare[cSquare].bvAttacks[uColor].uSmall | ((pos->bbPawnAttacks[uColor] & COOR_TO_BB(cRealSquare)) ? - PAWN_BIT : 0); + PAWN_BIT : 0) | + (_IsSquareAttackedByMinor(pos, uColor, cRealSquare) ? + MINOR_BIT : 0); } // |
