summaryrefslogtreecommitdiff
path: root/src/chess.h
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-07 08:17:51 -0700
committerScott Gasch <[email protected]>2026-10-07 08:17:51 -0700
commitd14dbe2a50c220b2adac758c3d4f1a1fa67ece0b (patch)
treecbf6d9928bcc41ef833b62b70479113f7d419925 /src/chess.h
parent447130db3df87b40d080dc26429d45e9213c19f7 (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-xsrc/chess.h135
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);