diff options
| author | Scott Gasch <[email protected]> | 2026-10-07 08:17:51 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-07 08:17:51 -0700 |
| commit | d14dbe2a50c220b2adac758c3d4f1a1fa67ece0b (patch) | |
| tree | cbf6d9928bcc41ef833b62b70479113f7d419925 /src/chess.h | |
| parent | 447130db3df87b40d080dc26429d45e9213c19f7 (diff) | |
Speed up eval/move-gen/SEE; remove EvalLookForDanger, overhaul _EvalKing
SPRT-confirmed improvement (1814 games, st 4.0): candidate nps=1498118
vs head=1378448, avg depth 14.30 vs 13.87, ELO=+24.2+/-16.0,
LOWER95=0.5118, LLR=+3.126 crossing the H1 bound.
Most of the diff is non-semantic speed work across eval.c, generate.c,
movesup.c, see.c, probe.c and friends. Two real semantic changes:
removed EvalLookForDanger and overhauled _EvalKing's king-safety scoring.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/chess.h')
| -rwxr-xr-x | src/chess.h | 135 |
1 files changed, 122 insertions, 13 deletions
diff --git a/src/chess.h b/src/chess.h index d637905..fc2ef85 100755 --- a/src/chess.h +++ b/src/chess.h @@ -778,7 +778,6 @@ typedef struct _POSITION BITBOARD bbTrappedEscapes[2][6]; ULONG uNumTrapped[2]; ULONG uArmyScaler[2]; - ULONG uClosedScaler; SCORE iScore[2]; SCORE iReducedMaterialDownScaler[2]; ULONG uMinorsAtHome[2]; @@ -833,7 +832,7 @@ typedef struct _KEY_POINTER } KEY_POINTER; -typedef union _GENERATOR_FLAGS +typedef union _GENERATOR_FLAGS { struct { UCHAR uMoveCount; @@ -868,6 +867,31 @@ typedef struct _MOVE_STACK // ULONG uUnblockedKeyValue[MAX_PLY_PER_SEARCH]; KEY_POINTER sUnblocked[MAX_PLY_PER_SEARCH][128]; + // _FindUnblockedSquaresBB's output: one bit per square unblocked + // for the opposing king this ply (membership only -- the + // direction-back-to-king half of the old sUnblocked table is + // recomputed on demand by DIRECTION_BETWEEN_SQUARES, a table + // lookup in release builds, so it needs no storage of its own). + // Kept unconditional (not #ifdef UNBLOCKED_BITBOARD-gated) so + // testgenerate.c's harness can exercise _FindUnblockedSquaresBB + // and compare it against the mailbox table regardless of which + // toggle this build was compiled with. + BITBOARD bbUnblocked[MAX_PLY_PER_SEARCH]; + // Which generation (uUnblockedKeyValue[uPly] value) bbUnblocked[uPly] + // was last actually built for -- WouldGiveCheck (generate.c) compares + // this against the live uUnblockedKeyValue[uPly] to decide whether + // the cached bitboard is still valid for the node now occupying this + // ply, or needs a lazy rebuild. GenerateMoves no longer builds + // bbUnblocked eagerly; it just bumps uUnblockedKeyValue[uPly], same + // as root.c's init comment there describes -- the actual build is + // deferred to WouldGiveCheck's first call this generation, so a node + // whose every candidate move gets rejected before ever needing + // WouldGiveCheck (e.g. every capture futility-pruned in QSearch) + // never pays for it at all. Starts zeroed; every real ticket in + // uUnblockedKeyValue starts at 1 or higher (root.c) and only + // increases, so 0 is a permanently safe "never built yet" sentinel + // -- no special seeding needed here, unlike that field. + ULONG uUnblockedBuiltFor[MAX_PLY_PER_SEARCH]; // // The main move list, a long series of moves, their values and some @@ -922,6 +946,20 @@ MOVE_STACK; #define GENERATE_CAPTURES_PROMS_CHECKS (3) #define GENERATE_CAPTURES_PROMS (4) #define GENERATE_DONT_SCORE (5) + +// generate.c's _GenerateCapturesAndPromotions (GENERATE_CAPTURES_PROMS's +// direct, non-filtered generator) exists only when every piece type is +// on its bitboard generator -- see that function's block comment in +// generate.c. Declared here, not in generate.c, so every translation +// unit that needs to know whether it exists (main.c's test-registration +// list, testgenerate.c's own tests) sees the same answer generate.c's +// GenerateMoves dispatch does. +#if defined(GENERATE_KNIGHT_BITBOARD) && defined(GENERATE_BISHOP_BITBOARD) && \ + defined(GENERATE_ROOK_BITBOARD) && defined(GENERATE_QUEEN_BITBOARD) && \ + defined(GENERATE_KING_BITBOARD) && defined(GENERATE_PAWN_BITBOARD) +#define CAPTURES_PROMS_BITBOARD_PATH 1 +#endif + #ifdef TEST #define GENERATE_ALL_MOVES_CHECK_OK (6) #endif @@ -1138,6 +1176,18 @@ typedef struct _COUNTERS } kingsafetyhash; + // Separate from kingsafetyhash above -- _GetKingActionBonus's + // cache (eval.c) is keyed and populated completely independently + // (see KING_SAFETY_HASH_ENTRY's u64ActionKey comment), so its own + // hit rate needs its own counters rather than conflating with + // _GetFileStormDefects'. + struct + { + UINT64 u64Probes; + UINT64 u64Hits; + } + kingactionhash; + struct { UINT64 u64TotalNodeCount; @@ -1746,6 +1796,21 @@ typedef struct _PAWN_HASH_ENTRY UCHAR uCountPerFile[2][10]; UCHAR uNumRammedPawns; UCHAR uNumUnmovedPawns[2]; + // Total pawns on the board, both colors -- _EvalRook indexes + // ROOK_VALUE_AS_PAWNS_COME_OFF with this once per rook (up to 4x + // per Eval() call); also feeds uClosedScaler's own formula below, + // in both cases replacing a fresh FastCountBits(bbPawns[WHITE] | + // bbPawns[BLACK]) with a value this entry already has cached. + UCHAR uTotalPawnCount; + // "How closed/busy is the board" scaler _EvalKnight indexes + // KNIGHT_IN_CLOSED_POSITION with (eval.c's EvalPawns computes the + // formula) -- used to live as a POSITION field recomputed from + // scratch on every Eval() call even though every input (total + // pawn count, uNumUnmovedPawns, uNumRammedPawns) is already pure + // pawn-structure data this same entry is keyed on. Same "finish + // moving a pawn-only term into the pawn hash" pattern as + // iBishopColorScore below and 5fbd935's original move. + UCHAR uClosedScaler; SHORT iBishopColorScore[2][2]; // [color][IS_SQUARE_WHITE(bishop's // square)] -- good/bad-bishop // pawn-structure score, pure @@ -1771,6 +1836,17 @@ typedef struct _KING_SAFETY_HASH_ENTRY // reports old counter units into KING_DEFECT_DUMP_INFO, unrelated // to this cache. SCORE iFileStormDefects[2]; + // Independent validity tag for iActionBonus below, decoupled from + // u64Key above on purpose: _GetKingActionBonus (eval.c) is called + // from the opposite half of _EvalKing's safety/activity split from + // _GetFileStormDefects (only when the ENEMY lacks mating material, + // where file-storm's own populate-on-miss never runs), so a hit on + // u64Key would say nothing about whether iActionBonus was ever + // actually computed for this key. Per-king (keyed on pawn sig + + // just THIS king's own square, not both kings'), unlike + // iFileStormDefects -- see _GetKingActionBonus's header comment. + UINT64 u64ActionKey; + SCORE iActionBonus; } KING_SAFETY_HASH_ENTRY; @@ -2799,7 +2875,7 @@ InCheck(POSITION *pos, ULONG uSide); FLAG IsAttackedBB(COOR cTest, POSITION *pos, ULONG uSide); -FLAG +FLAG FORCEINLINE InCheckBB(POSITION *pos, ULONG uSide); // ISATTACKED_BITBOARD defined -> the BB versions above resolve plain @@ -2828,10 +2904,10 @@ StripMove(CHAR *szMove); ULONG LooksLikeMove(CHAR *szData); -void FASTCALL +void FORCEINLINE SelectBestWithHistory(SEARCHER_THREAD_CONTEXT *ctx, ULONG u); -void FASTCALL +void FORCEINLINE SelectBestNoHistory(SEARCHER_THREAD_CONTEXT *ctx, ULONG u); void FASTCALL @@ -2942,6 +3018,24 @@ _RookAttacksBB(COOR c, BITBOARD bbOccupied); BITBOARD _BishopAttacksBB(COOR c, BITBOARD bbOccupied); +// generate.c's bitboard replacement for _FindUnblockedSquares, exposed +// non-static so testgenerate.c's correctness/speed harness can call it +// directly regardless of whether UNBLOCKED_BITBOARD is defined for +// this build. +void +_FindUnblockedSquaresBB(MOVE_STACK *pStack, + POSITION *pos); + +// The mailbox original, exposed non-static (compiled whenever +// !UNBLOCKED_BITBOARD, or under DEBUG/TEST for the cross-check and +// the test harness above) under the same build conditions generate.c +// itself uses -- see generate.c. +#if !defined(UNBLOCKED_BITBOARD) || defined(DEBUG) || defined(TEST) +void +_FindUnblockedSquares(MOVE_STACK *pStack, + POSITION *pos); +#endif + void _GenerateRookBB(MOVE_STACK *pStack, POSITION *pos, @@ -3057,7 +3151,7 @@ GenerateWhiteKing(MOVE_STACK *pStack, POSITION *pos, COOR cKing); -FLAG +FLAG FORCEINLINE WouldGiveCheck(IN SEARCHER_THREAD_CONTEXT *ctx, IN MOVE mv); @@ -3095,6 +3189,9 @@ TestGenerateQueenSpeed(void); void TestGenerateAllMovesSpeed(void); +void +TestFindUnblockedSquaresSpeed(void); + #endif // @@ -3148,6 +3245,7 @@ extern MOVE HASHMOVE; extern MOVE RECOGNMOVE; extern MOVE DRAWMOVE; extern MOVE MATEMOVE; +extern MOVE EGTBMOVE; extern FLAG g_fIsWhiteSquare[128]; extern BITBOARD BBFILE[8]; extern BITBOARD BBRANK[9]; @@ -3707,7 +3805,7 @@ CanWeReduceOrPruneMove(IN SEARCHER_THREAD_CONTEXT *ctx, IN FLAG fThreat, IN FLAG fIsLeftoverMove); -INT +INT FORCEINLINE GetLMRReduction(IN SEARCHER_THREAD_CONTEXT *ctx, IN ULONG uRemainingDepth, IN ULONG uLegalMoves, @@ -3789,7 +3887,7 @@ GetMoveSee(IN SEARCHER_THREAD_CONTEXT *ctx, IN MOVE mv, IN ULONG uMoveNum); -void +void FORCEINLINE ComputeMoveExtension(IN OUT SEARCHER_THREAD_CONTEXT *ctx, IN SCORE iAlpha, IN SCORE iBeta, @@ -3813,7 +3911,7 @@ MateDistancePruningCutoff(IN ULONG uPly, IN OUT SCORE *piAlpha, IN OUT SCORE *piBeta); -FLAG +FLAG FORCEINLINE CommonSearchInit(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piAlpha, IN OUT SCORE *piBeta, @@ -3849,14 +3947,25 @@ TestQSearchSanity(void); // #define SEE_HEAPS -SCORE +SCORE FORCEINLINE SEE(POSITION *pos, MOVE mv); +// The real exchange-walk SEE implementation, with no early exit -- +// see SEE()'s own header comment in see.c for the cheap no-defender +// check that now sits in front of this. Exposed non-static (unlike +// its historical merged-into-SEE status) so testsee.c's +// TestSEENoDefenderFastPath can call it directly by name for an +// exact-value correctness/speed comparison against SEE(), same +// convention as _GenerateAllMoves/_GenerateAllMovesBB. +SCORE +_SEEFull(POSITION *pos, + MOVE mv); + // Sign-only fast path for SEE(pos, mv) >= 0 -- see its own header // comment in see.c. Exposed non-static so GetMoveSee (searchsup.c) can // call it and testsee.c can diff it against real SEE(). -FLAG +FLAG FORCEINLINE SeeFastSign(POSITION *pos, MOVE mv); @@ -4449,7 +4558,7 @@ _GetAttacksBB(SEE_LIST *pList, // call it with a king-vacated occupancy bitboard. Deliberately // excludes pawns -- see its own header comment in see.c; callers // needing pawn attacks must check g_PawnAttackOriginBB separately. -BITBOARD +BITBOARD FORCEINLINE _WhoAttacksSquareBB(POSITION *pos, COOR cSquare, ULONG uSide, @@ -4807,7 +4916,7 @@ GenerateRandomLegalSymetricPosition(POSITION *pos); void InitializeInteriorNodeRecognizers(void); -ULONG +ULONG FORCEINLINE RecognLookup(SEARCHER_THREAD_CONTEXT *ctx, SCORE *piScore, ULONG uEgtbProbeMode); |
