# setup-chess.sh ## Overview Single-shot installer for a chess analysis stack on Arch Linux: the En Croissant GUI, Stockfish, Lc0, and the nine Maia personality engines (1100–1900 Elo). All artifacts land under `$HOME/.local/`; no root-owned files. The script is idempotent — re-runs replace broken components, preserve user-tuned settings in `engines.json`, and keep custom engine IDs stable across invocations. Run it once on a fresh Arch install to get a working chess setup, or run it again later (with `--force` or `--component `) to repair a single component without rebuilding the rest. ## Usage ``` ./setup-chess.sh # full install / refresh ./setup-chess.sh --force # re-download and rebuild everything ./setup-chess.sh --component lc0 # repair just lc0 ./setup-chess.sh --component lc0 --force # force re-install of just lc0 ``` Valid `--component` names: `prereqs`, `en-croissant`, `lc0`, `maia`, `stockfish`, `engines-json`, `desktop`. ## What the script does Each section below maps to one numbered block in `setup-chess.sh`. ### 1. Prerequisites Checks for the tools and libraries used downstream and installs missing pacman packages with `pacman -S --needed --noconfirm` via `sudo` when not running as root. Always-required: `git`, `curl`, `tar`, `coreutils`, `grep`, `desktop-file-utils`, `fuse2`. The build-toolchain deps (`base-devel`, `meson`, `ninja`, `python`, `pkgconf`, `protobuf`, `zlib`, `openblas`, `eigen`) are installed when a healthy system `lc0` is unavailable, since the AUR path can still fail and fall back to a source build. ### 2. En Croissant AppImage Downloads the pinned version of the En Croissant AppImage to `~/.local/bin/en-croissant.AppImage`, verifies its SHA256, and marks it executable. Skipped when the AppImage already exists, unless `--force` is set. ### 3. Lc0 Tries paths in order of speed and durability, gating each on a UCI handshake smoke test (`echo uci | timeout 5 | grep '^id name Lc0'`): 1. **Existing `~/.local/bin/lc0` passes** — keep it as-is. 2. **System `/usr/bin/lc0` passes** — symlink it into `~/.local/bin/lc0`. This is the steady state for an AUR install. 3. **AUR helper available** — `yay -S --needed --noconfirm lc0 lc0-network-sm` (or `paru` if that's what's installed), then symlink. Pacman-managed binaries get re-resolved automatically when Abseil, Protobuf, or OpenBLAS bump SONAMEs — see the BLAS conflict note below. 4. **Source build** — clone the upstream `v0.32.1` tag and build with the project's own `build.sh`. Slower (several minutes), and the resulting binary is dynamically linked, so a later library bump can break it. Used only as the last-resort fallback. The final binary is run through the smoke test again. If it fails, the script aborts with a pointer to running `lc0` manually to read the load-time error. ### 4. Maia weights and wrappers Downloads the nine `maia-{1100..1900}.pb.gz` files from the CSSLab Maia release `v1.0` into `~/.local/share/maia/`, verifying each against a pinned SHA256. Existing files are re-validated against the pin; a mismatch triggers a re-download. Then generates the nine wrapper scripts at `~/.local/bin/maia-{1100..1900}`. Each is a one-line shell script that execs the lc0 binary with the matching `--weights=` flag. Wrappers are regenerated unconditionally so they always point at the current `~/.local/bin/lc0` (handy if step 3 swapped the binary out from under them). ### 5. Stockfish Probes `/proc/cpuinfo` and selects the best Stockfish binary for the CPU — in order, `vnni512`, `avx512`, `avxvnni`, `avx2`, `bmi2`, `sse41-popcnt`, `x86-64`. Downloads the matching tarball from the pinned Stockfish release (`sf_18`), verifies its SHA256, extracts it into `~/.local/share/org.encroissant.app/engines/stockfish/`, and runs a UCI handshake to confirm the binary works. ### 6. engines.json Writes `~/.local/share/org.encroissant.app/engines/engines.json` with Stockfish plus the nine Maia entries. Before writing, backs up the existing file to `engines.json.bak.`. The Python that builds the JSON does three things worth knowing: - **Engine IDs are preserved by name.** If an entry named `Stockfish` already exists, its UUID is reused; otherwise a fresh one is generated. Same for each Maia engine. This keeps any per-engine state En Croissant tracks (analysis history, custom names) stable across re-runs. - **User-tuned settings are preserved.** The script ships conservative defaults (`Stockfish: Threads=1, Hash=16, MultiPV=1`), but if the existing entry has different settings, those are kept instead of clobbered. Re-running the script never resets a tuned engine to defaults. - **Unknown engines are preserved.** Any entry in the existing file whose name isn't one of the ten managed ones (Stockfish + 9 Maias) is appended to the new file as-is. Hand-added engines like Komodo or a second Stockfish build survive a re-run. ### 7. Desktop file Writes `~/.local/share/applications/en-croissant.desktop` so En Croissant appears in app launchers (rofi, fuzzel, Wayland app drawers, etc.) and runs `update-desktop-database` to refresh the cache. ## Known issues ### BLAS provider conflict The AUR `lc0` package depends on `openblas`, which on Arch is provided by the `blas-openblas` package. `blas-openblas` is mutually exclusive with the reference netlib BLAS bundle (`blas` + `cblas` + `lapack`). A system that has the netlib trio installed will see pacman refuse the lc0 transaction: ``` :: blas-openblas-0.3.33-1 and blas-3.12.1-2 are in conflict. Remove blas? [y/N] error: unresolvable package conflicts detected ``` The script's `lc0` section detects this state before invoking the AUR helper (because `--noconfirm` would just abort), prints a warning with the resolution command, and falls through to the source-build path for that run. To resolve once and for all: ``` sudo pacman -S blas-openblas ``` Pacman prompts to remove `blas`, `cblas`, and `lapack` (answer `y`), then installs `blas-openblas`. `blas-openblas` provides the same `libblas.so` / `libcblas.so` / `liblapack.so` ABIs, so any package linked against BLAS (NumPy, SciPy, R, Octave, GIMP G'MIC, OpenCV, etc.) keeps working — and typically runs faster, since OpenBLAS is heavily optimized while the netlib reference impl is not. The swap is reversible (`sudo pacman -S blas cblas lapack` flips it back), and most Arch users with scientific software already run on `blas-openblas` by default. ### En Croissant: Lc0/Maia hangs when playing engine as Black When you set up a "play vs engine" game in En Croissant with the engine as Black and yourself (the human) as White with **no clock**, En Croissant fills your `wtime` field with `4294967295` (UINT32_MAX) to signal "no time control." Lc0 parses `wtime` as a bounded integer and rejects anything above ~2.1 billion ms: ``` go wtime 4294967295 btime 30000 winc 0 binc 2000 error out of range value 4294967295 ``` The engine never issues `bestmove`. The UI sits there waiting forever. This affects every Maia engine and any other Lc0-based engine, since they all share the same UCI parser. **Workaround:** when setting up the game, give White a finite time control. Anything reasonable (60 min + 0 inc, even 1 day) is well below Lc0's limit. The `go: {t: "Nodes", c: 1}` setting that ships in `engines.json` only governs analysis mode, not play mode — the time control comes from the game dialog. This is an En Croissant bug; a fix would either omit `wtime` entirely when a side has no clock, or cap the sentinel to a value Lc0 accepts (e.g. `2147483647`). ## Files this script manages ``` ~/.local/bin/en-croissant.AppImage En Croissant GUI ~/.local/bin/lc0 Lc0 engine (binary or symlink to /usr/bin/lc0) ~/.local/bin/maia-{1100..1900} Maia wrapper scripts (shell, exec lc0) ~/.local/share/maia/maia-{1100..1900}.pb.gz Maia neural network weights ~/.local/share/org.encroissant.app/engines/ stockfish/stockfish-ubuntu-x86-64-* Stockfish binary engines.json Engine registry read by En Croissant ~/.local/share/applications/en-croissant.desktop Launcher entry ```