1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
#!/usr/bin/env bash
#
# stockfish_xboard -- thin wrapper so match_play.py's Engine class (which
# always spawns its path with hardcoded TYPHOON flags, "--hash 256m --cpus 1
# --logfile -" -- see match_play.py's engine_args) can drive Stockfish
# instead. Stockfish speaks UCI, not xboard, so this execs it through
# PolyGlot (the UCI<->xboard adapter already installed on this box,
# /usr/local/bin/polyglot) and deliberately DISCARDS every argument it's
# called with -- match_play.py's hardcoded typhoon-style flags are
# meaningless (and would error) if passed to polyglot/Stockfish directly.
#
# Same "ignore whatever the caller passes, always do the right thing"
# pattern as the NNUE_WEIGHTS shim in eval_tune/test_vs_head.sh.
#
# Verified manually (2026-10-17): PolyGlot's xboard handshake (feature
# negotiation ending "feature done=1") and a real "setboard ...; st 2; go"
# round-trip both work correctly when driven by a persistent subprocess
# that keeps stdin open (exactly how Engine class drives it) -- a one-shot
# shell pipe (`printf ... | polyglot`) does NOT work, stdin EOFs before
# Stockfish finishes thinking and no "move ..." line is ever printed. Do
# not "simplify" this to a plain pipe when debugging.
#
# Override the Stockfish binary/dir via env vars if needed:
# STOCKFISH_BIN defaults to "stockfish" (resolved via PATH, i.e.
# /usr/local/bin/stockfish on this box)
# STOCKFISH_DIR defaults to /usr/local/bin (polyglot's -ed)
# SYZYGY_PATH defaults to /zscratch/egtb (same dir typhoon's own
# --egtbpath default points at, see main.c). Stockfish 19's
# tbprobe.cpp is itself Fathom-derived, so it reads the
# exact same .rtbw/.rtbz files typhoon does -- no separate
# Stockfish-format tablebase or conversion needed.
# Verified 2026-10-18: `setoption name SyzygyPath value
# /zscratch/egtb` against a bare stockfish UCI session logs
# "info string Found 510 WDL and 510 DTZ tablebase files
# (up to 6-man)." PolyGlot forwards this through via its
# `-uci SyzygyPath=...` flag (any `-uci name=value` is
# passed straight to the engine as a UCI setoption at
# startup, before the xboard-side handshake). Set to ""
# to disable TB probing entirely.
SYZYGY_PATH="${SYZYGY_PATH:-/zscratch/egtb}"
#
# 2026-10-17: wrapped with `stdbuf -oL` -- when PolyGlot's stdout isn't
# a tty (true here: match_play.py spawns it via subprocess.Popen with a
# pipe), C stdio defaults to full block buffering instead of line
# buffering, so xboard protocol lines (including "move ...") can sit
# unflushed in PolyGlot's own buffer while match_play.py's readline()
# blocks waiting for them -- looks exactly like an I/O-bound engine
# from the outside (low measured CPU, long wall-clock per move, idle
# machine) even though the actual Stockfish search finished promptly.
# Same class of bug as this session's own repeated Python stdout-
# buffering gotchas, just at the C-stdio layer instead. `stdbuf -oL`
# forces line buffering regardless of tty status.
set -e
STOCKFISH_BIN="${STOCKFISH_BIN:-stockfish}"
STOCKFISH_DIR="${STOCKFISH_DIR:-/usr/local/bin}"
if [ -n "$SYZYGY_PATH" ]; then
exec stdbuf -oL polyglot -noini -ec "$STOCKFISH_BIN" -ed "$STOCKFISH_DIR" \
-uci "SyzygyPath=$SYZYGY_PATH"
else
exec stdbuf -oL polyglot -noini -ec "$STOCKFISH_BIN" -ed "$STOCKFISH_DIR"
fi
|