summaryrefslogtreecommitdiff
path: root/src/searchsup.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-18 22:35:42 -0700
committerScott Gasch <[email protected]>2026-09-18 22:35:42 -0700
commit91dc4a9393204104c06fa64a05be24d08feca0ef (patch)
tree26933246f26a5acfe0590fe678bb805de4d9af1a /src/searchsup.c
parent73b82a01c6f1b15489608c7e2cf757180746f7b8 (diff)
Add SeeFastSign, a provably-exact sign-only shortcut for SEE(), and wire it into GetMoveSee's cache-miss path
GetMoveSee's only consumer, ComputeMoveExtension, reads iMoveSee in exactly two places -- both plain sign checks (`< 0` / `>= 0`), never the magnitude. On a cache miss GetMoveSee always paid for a full SEE() exchange walk (list-build, heap, x-ray discovery) just to throw away everything but the sign. SeeFastSign answers the sign question directly, without building either SEE_LIST: if the defending side has zero attackers on the target square, SEE()'s own exchange loop terminates on its first iteration anyway and returns exactly captured+promoted, which is never negative -- so "no defender" provably implies SEE >= 0, not an approximation. Falls back to real SEE() whenever a defender exists. Must check with mv.cFrom removed from occupancy, not the raw pre-move board -- the mover's own departure can unmask a defender that's currently blocked by the mover itself sitting on the same ray (exactly what SEE()'s own _AddXRaysMailbox discovers explicitly). A first version missed this and returned a wrong TRUE for a quiet bishop move that unmasked a queen's diagonal attack through the vacated square, caught by the correctness test below before it ever reached production code. testsee.c's TestSeeFastSign asserts SeeFastSign's sign matches real SEE() across 3000 random positions x every legal move, then benchmarks both: SeeFastSign runs at 0.40x-0.65x real SEE()'s cycles/call. Measured end-to-end (sd10 + sn5M, all three curated suites, before vs. after): node counts are bit-for-bit identical on every suite/depth pair, as expected from a provably sign-equivalent function -- zero search-behavior change, no self-play gate needed. Wall-clock improved on all 6 measurements, ~2.7% overall. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/searchsup.c')
-rw-r--r--src/searchsup.c14
1 files changed, 13 insertions, 1 deletions
diff --git a/src/searchsup.c b/src/searchsup.c
index 26856cd..49027db 100644
--- a/src/searchsup.c
+++ b/src/searchsup.c
@@ -465,6 +465,18 @@ Routine description:
Only the sign matters to any caller of ComputeMoveExtension, so the
cached value is coarsened to 0/-1.
+ On a cache miss, use SeeFastSign (see.c) instead of a real SEE()
+ call -- same reasoning as the cache-hit path above (only the sign
+ ever reaches ComputeMoveExtension, both of its own iMoveSee checks
+ are plain `< 0`/`>= 0`), but for the miss case there was no cached
+ value to fall back to at all, so this always used to pay for a
+ full SEE() exchange walk (list-build, heap, x-ray discovery) just
+ to throw away everything but the sign. SeeFastSign is
+ correctness-tested against real SEE()'s sign across thousands of
+ random positions (testsee.c's TestSeeFastSign) and measured at
+ 0.40x-0.65x SEE()'s own cycles/call, coarsened to the same 0/-1
+ convention as the cache-hit path for consistency.
+
Parameters:
SEARCHER_THREAD_CONTEXT *ctx : searcher thread context
@@ -491,7 +503,7 @@ Return value:
return((bvFlags & MVF_SEE_NONNEGATIVE) ? 0 : -1);
}
}
- return(SEE(&ctx->sPosition, mv));
+ return(SeeFastSign(&ctx->sPosition, mv) ? 0 : -1);
}