Removes the temporary `and not move.isEnPassant()` guards that kept en
passant out of capture history, capture-history LMR, ttCapture and the
failed-capture malus. En passant now scores and updates capture history
like any other capture, with the victim looked up via captureSquare().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
isCapture() now returns true for en passant moves; the new captureSquare()
helper disambiguates the to-be-captured square (targetSquare for normal
captures, targetSquare xor 8 for en passant). movegen and eval route the
captured-piece lookup through it, removing the bespoke ep square math and
the separate ep-removal block.
En passant is deliberately kept out of the search histories for now via
explicit `and not move.isEnPassant()` guards, so bench is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The history tables and eval state are several megabytes of randomly-accessed
data hammered on every search node. Allocate them on 2MB huge pages to cut TLB
misses: HistoryTablesObj/EvalState storage is owned by the SearchManager via
HugePtr handles, with auto-dereferencing accessors so existing call sites keep
working unchanged.
Bench unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Under multiple search threads the engine would flakily crash with either a
shifted-by-one `response == expected` AssertionDefect or a DeadThreadDefect
when a pool-mutating UCI command (ucinewgame / setoption Threads) was sent
right after a `go`. This is a longstanding race (predates 1.4.3), not a
regression from the NUMA work.
Root cause: the UCI reader thread dispatches each search to the asynchronous
search-worker thread and immediately returns to reading stdin, but the
`searching` flag was only published inside search() on the worker thread. That
left a window where the next command observed searching=false, passed its
guard, and drove the per-worker command/response channels concurrently with
the search thread's own startSearch / post-search ping barrier. Concurrent
send+recv on the same channels breaks the one-send-one-recv pairing
(manifestation B); with a Threads change it shuts down workers the barrier is
still pinging (manifestation A).
Fixes:
- Publish the `searching` flag synchronously at `go` dispatch on the UCI thread
(new SearchManager.markSearching) so the guard is race-free.
- Make the SetOption/Set guard actually skip (continue) when searching, instead
of printing an error and falling through to mutate the pool.
- Defensively wrap the worker Go-body search in try/except so a future throwing
search can't silently kill a worker and desync the protocol.
Verified with the repro loops (both manifestations) over 200 runs with zero
failures; bench unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>