summaryrefslogtreecommitdiff
path: root/src/probe.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-25 08:44:25 -0700
committerScott Gasch <[email protected]>2026-09-25 08:44:25 -0700
commit9b28080f8b3dca4588cb2aaaf748451f45e3c626 (patch)
tree54e0aff5d6ee1520c590c7c04a5ae2f8299e7497 /src/probe.c
parent66740dc89c7351c120c25788d0079055034f4942 (diff)
Two ideas:
1. Nullmove R=3 + depth/6 along with some ShouldWeDoNullmoveSearch tweaking and calibrating. 2. Probe EGTB everywhere in the search as long as the table/position is resident in memory.
Diffstat (limited to 'src/probe.c')
-rw-r--r--src/probe.c194
1 files changed, 121 insertions, 73 deletions
diff --git a/src/probe.c b/src/probe.c
index 2bddf99..269a1b9 100644
--- a/src/probe.c
+++ b/src/probe.c
@@ -138,10 +138,25 @@ Routine description:
Build the set of bitboards Fathom's tb_probe_wdl wants out of our
POSITION. Fathom expects the standard tablebase square numbering
- (a1 == bit 0 ... h8 == bit 63), which is exactly what our TO64()
- macro produces -- note this is *not* the same square numbering as
- our engine's own internal BBSQUARE/COOR_TO_BB bitboards, so we
- must not use those macros here.
+ (a1 == bit 0 ... h8 == bit 63); our own internal bitboards
+ (bbPieces/bbPawns/bbOccupiedSide) instead index bit = rankTopDown*8
+ + file (rankTopDown 0 == rank 8 ... 7 == rank 1 -- see TO64's own
+ (7 - (x>>4)) flip). Those two numberings differ by exactly one
+ 64-bit byte-swap: internal bit r*8+f (byte r, offset f) lands at
+ Fathom bit (7-r)*8+f (byte 7-r, offset f) under bswap64, matching
+ TO64's formula term for term. So this can OR our own bitboards
+ together and byte-swap the result directly -- O(1) per output
+ bitboard -- instead of walking every piece one at a time through
+ CoorFromBitBoardRank8ToRank1/TO64. (2026-09-24: replaced the
+ original per-piece-popping version, which showed up as a real
+ self-play NPS/depth regression once EgtbProbeLikelyResident started
+ calling this from QSearch's hot path -- see CLAUDE.md.)
+
+ No bbPieces[color][KING] exists (deliberately -- see its own
+ comment), so the king bitboard is built from the single square in
+ cKingSq[color] instead; equivalent to bswapping a bitboard with only
+ that one bit set, since bswap64 is just a bit permutation and
+ commutes with OR.
Parameters:
@@ -154,75 +169,22 @@ Return value:
**/
{
- ULONG uColor;
- COOR c;
- UINT64 bb;
-
- *pu64White = *pu64Black = 0ULL;
- *pu64Kings = *pu64Queens = *pu64Rooks = 0ULL;
- *pu64Bishops = *pu64Knights = *pu64Pawns = 0ULL;
-
- for (uColor = BLACK; uColor <= WHITE; uColor++)
- {
- UINT64 *pu64Side = (uColor == WHITE) ? pu64White : pu64Black;
-
- {
- BITBOARD bbPawns = pos->bbPawns[uColor];
-
- while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawns)))
- {
- bb = (1ULL << (TO64(c)));
- *pu64Side |= bb;
- *pu64Pawns |= bb;
- }
- }
-
- {
- c = KingPosition(pos, uColor);
- ASSERT(IS_ON_BOARD(c));
- ASSERT(IS_KING(pos->rgSquare[c].pPiece));
- bb = (1ULL << (TO64(c)));
- *pu64Side |= bb;
- *pu64Kings |= bb;
- }
-
- {
- BITBOARD bbType;
- COOR cType;
-
- bbType = pos->bbPieces[uColor][KNIGHT];
- while (IS_ON_BOARD(cType = CoorFromBitBoardRank8ToRank1(&bbType)))
- {
- bb = (1ULL << (TO64(cType)));
- *pu64Side |= bb;
- *pu64Knights |= bb;
- }
+ UINT64 u64Kings = (1ULL << COOR_TO_BIT_NUMBER(pos->cKingSq[WHITE])) |
+ (1ULL << COOR_TO_BIT_NUMBER(pos->cKingSq[BLACK]));
- bbType = pos->bbPieces[uColor][BISHOP];
- while (IS_ON_BOARD(cType = CoorFromBitBoardRank8ToRank1(&bbType)))
- {
- bb = (1ULL << (TO64(cType)));
- *pu64Side |= bb;
- *pu64Bishops |= bb;
- }
-
- bbType = pos->bbPieces[uColor][ROOK];
- while (IS_ON_BOARD(cType = CoorFromBitBoardRank8ToRank1(&bbType)))
- {
- bb = (1ULL << (TO64(cType)));
- *pu64Side |= bb;
- *pu64Rooks |= bb;
- }
-
- bbType = pos->bbPieces[uColor][QUEEN];
- while (IS_ON_BOARD(cType = CoorFromBitBoardRank8ToRank1(&bbType)))
- {
- bb = (1ULL << (TO64(cType)));
- *pu64Side |= bb;
- *pu64Queens |= bb;
- }
- }
- }
+ *pu64White = __builtin_bswap64((UINT64)pos->bbOccupiedSide[WHITE]);
+ *pu64Black = __builtin_bswap64((UINT64)pos->bbOccupiedSide[BLACK]);
+ *pu64Kings = __builtin_bswap64(u64Kings);
+ *pu64Queens = __builtin_bswap64(
+ (UINT64)(pos->bbPieces[WHITE][QUEEN] | pos->bbPieces[BLACK][QUEEN]));
+ *pu64Rooks = __builtin_bswap64(
+ (UINT64)(pos->bbPieces[WHITE][ROOK] | pos->bbPieces[BLACK][ROOK]));
+ *pu64Bishops = __builtin_bswap64(
+ (UINT64)(pos->bbPieces[WHITE][BISHOP] | pos->bbPieces[BLACK][BISHOP]));
+ *pu64Knights = __builtin_bswap64(
+ (UINT64)(pos->bbPieces[WHITE][KNIGHT] | pos->bbPieces[BLACK][KNIGHT]));
+ *pu64Pawns = __builtin_bswap64(
+ (UINT64)(pos->bbPawns[WHITE] | pos->bbPawns[BLACK]));
}
@@ -324,6 +286,72 @@ Return value:
}
+FLAG
+EgtbProbeLikelyResident(SEARCHER_THREAD_CONTEXT *ctx)
+/**
+
+Routine description:
+
+ Cheap, non-blocking predictor for "would an EGTB probe of the
+ current position be low latency?" -- used to let deep search/qsearch
+ nodes probe the EGTB (which is normally reserved for shallow, near-
+ root plies to avoid stalling the tree search on a disk read) once
+ the specific pages this exact position's lookup needs are already
+ resident, whether from an earlier probe this run or (for 3-5 man
+ tables) because they were mlock()'d in whole at load time.
+
+ Does not itself probe the tablebase, take g_uEgtbLock, or read any
+ file data that isn't already confirmed resident first -- see
+ tb_wdl_probe_is_resident in fathom/tbprobe.c for the mincore()-gated
+ walk this performs (indexTable/sizeTable/data-block addresses for
+ 6-man tables; a single ready[] check for the mlock()'d 3-5 man
+ tier). Safe to call from every leaf-ish node without worrying about
+ search slowdown.
+
+ Same castling/fifty-move/cardinality gating as ProbeEGTB (a probe
+ that ProbeEGTB itself would refuse is never "likely resident").
+
+Parameters:
+
+ SEARCHER_THREAD_CONTEXT *ctx
+
+Return value:
+
+ FLAG -- TRUE iff a subsequent ProbeEGTB call for this exact position
+ is expected to hit already-resident tablebase data rather than pay a
+ first-touch mmap()/parse (and possibly disk-read) cost.
+
+**/
+{
+ POSITION *pos = &(ctx->sPosition);
+ UINT64 u64White, u64Black, u64Kings, u64Queens;
+ UINT64 u64Rooks, u64Bishops, u64Knights, u64Pawns;
+ ULONG wcount, bcount;
+
+ if (FALSE == g_fEgtbInitialized || 0 == TB_LARGEST)
+ {
+ return(FALSE);
+ }
+ if ((0 != pos->bvCastleInfo) || (0 != pos->uFifty))
+ {
+ return(FALSE);
+ }
+ wcount = TotalNonPawnCount(pos, WHITE) + FastCountBits(pos->bbPawns[WHITE]);
+ bcount = TotalNonPawnCount(pos, BLACK) + FastCountBits(pos->bbPawns[BLACK]);
+ if ((wcount + bcount) > TB_LARGEST)
+ {
+ return(FALSE);
+ }
+
+ _BuildFathomBitboards(pos, &u64White, &u64Black, &u64Kings, &u64Queens,
+ &u64Rooks, &u64Bishops, &u64Knights, &u64Pawns);
+ return(1 == tb_wdl_probe_is_resident(u64White, u64Black, u64Kings,
+ u64Queens, u64Rooks, u64Bishops,
+ u64Knights, u64Pawns,
+ (pos->uToMove == WHITE)));
+}
+
+
//
// Convert a Fathom tablebase square index (a1==0 ... h8==63) back into
// our own COOR encoding. Inverse of TO64().
@@ -487,8 +515,28 @@ Return value:
(mv.cTo == cTo) &&
(mv.pPromoted == pPromotes))
{
+ // Root-only: use the DTZ ply count tb_probe_root already
+ // packed into uResult (no extra probe) to report a real,
+ // ply-distance-aware mate score instead of the flat
+ // EGTB_WIN_SCORE/LOSS_SCORE used by in-search probes
+ // (ProbeEGTB/RecognLookup) -- those stay WDL-only and
+ // untouched. util.c's SCORE-to-string formatter already
+ // treats any |score| >= NMATE as a mate score and derives
+ // the move count as (INFINITY_SCORE - iScore) / 2, so
+ // INFINITY_SCORE -/+ uDtz round-trips through that
+ // unmodified as "+MATE<n>"/"-MATE<n>". Clamp defensively:
+ // a uDtz large enough to push the score below NMATE would
+ // just silently fall back to plain-decimal display, not
+ // corrupt anything, but keep it a real mate score at every
+ // legal DTZ value we could plausibly see (TB_MAX_PLY=256
+ // exceeds NMATE's 200-point margin in principle, however
+ // unlikely in practice for <=7-man tables).
+ ULONG uDtz = TB_GET_DTZ(uResult);
+ if (uDtz >= (+INFINITY_SCORE - NMATE)) uDtz = (+INFINITY_SCORE - NMATE - 1);
*pmv = mv;
- *piScore = (TB_WIN == uWdl) ? EGTB_WIN_SCORE : EGTB_LOSS_SCORE;
+ *piScore = (TB_WIN == uWdl) ?
+ (SCORE)(+INFINITY_SCORE - uDtz) :
+ (SCORE)(-INFINITY_SCORE + uDtz);
return(TRUE);
}
}