What's inside
figrid-board provides two public roles:
- Library (
figrid_board) — board representation, rule logic, move generation, threat detection, transposition table, and learned-evaluator surfaces. Reusable from any Rust project that wants Gomoku game state and search primitives without an engine attached. - Engine binaries:
The reusable categorical-codebook layer now lives in the standalone
CB2Vec (cb2vec) package, published
on crates.io. It owns the game-independent
model, quantized artifact, scoring, and reversible token-journal primitives.
figrid-board keeps Pattern4 mapping, board updates, Gomoku policy, and
search integration. The dependency is optional and is activated only by the
codebook-eval feature; the default board/rules build does not pull it in.
Download
The latest release ships a Windows zip with two self-contained engines (embedded models, static C runtime, x86_64-v3 CPU required):
| File | Board | Rules |
|---|---|---|
pbrain-figrid.exe |
15×15 | Freestyle, Standard, Renju, Caro (dedicated Caro and Renju models) |
pbrain-figrid_20.exe |
20×20 | Freestyle, Fastgame |
Both pass GomocupJudge test_zip_rules.py in all six formats, and the judge picks the _20 executable for 20×20 games.
Load either one in Piskvork or any pbrain-compatible manager. The release binaries embed trained models that are not
part of the crate package; a build from source with embed-weights,codebook-eval embeds the older default models
instead, and FIGRID_CODEBOOK_WEIGHTS loads any other model file.
Features
- Pure Rust, no C dependencies. With embedded weights and a statically linked C runtime, the engine can be packaged as one self-contained binary.
- NNUE-based evaluation through noru, with incremental accumulator updates.
- Optional
codebook-evalthrough the standalonecb2veccrate. The embedded swap-closed model supplies the deployed quantized leaf evaluator whilefigrid-boardsupplies all game-specific token and search semantics. - α-β search with transposition table, threat-aware move ordering, killer/history heuristics, late-move pruning, and a quiescence layer for forcing sequences.
- Optional VCF / VCT tactical search at the search root.
- Rule support: Freestyle (
rule 0), Standard exact-five (rule 1), Renju (rule 4: black exact five with forbidden double-four / double-three / overline, white five or more), and Caro as Gomocup plays it (rule 9: exactly five, not blocked at both ends by stones; the board edge does not block). Continuous games and barerule 8are answered withERROR - unsupported rule. - Korean omok rule (
RuleSet::Omok,INFO rule 17— a figrid extension, not a Gomocup rule): both sides win only with exactly five, and neither side may play a double-three; double-fours and overlines are legal (an overline does not win). It uses the standard models. - Board sizes: 15×15 by default, 20×20 with the
board20cargo feature (Gomocup Freestyle / Fastgame). The side is a compile-time constant, so the two engines are separate builds of the same source. - Exact-length VCT analysis (
vct::search_vct_exact): only an actual five ends a line, and the first attack can be pinned. Iterating the depth gives the minimal number of attacker moves to a five against every defence the prover considers. This is useful for puzzles and move review. The engine's own prover is unchanged. - Per-rule models:
FIGRID_CODEBOOK_WEIGHTS_{STANDARD,CARO,RENJU}load a codebook used only under that rule, each with its ownNORU_CODEBOOK_EVAL_SCALE_{STANDARD,CARO,RENJU}; other rules keepFIGRID_CODEBOOK_WEIGHTS. - Optional
avx512cargo feature: opportunistic ~2× evaluation speedup on AVX-512 hardware, with automatic AVX-2 runtime fallback. Requires Rust ≥ 1.89; off by default so library users on older toolchains and crates.io itself can build. - Optional
embed-weightsfeature: bake the v52-lineage NNUE ordering weights into the binary at build time. Enablecodebook-evalseparately to embed the packed swap-closed codebook and use the quantized codebook evaluator. - Compact storage without a runtime representation change. The embedded CBF stores exact source weights plus a five-class base-and-i8-residual quantized payload. The normal product path reconstructs the established flat i16 table from the exact source weights. Direct factored evaluation remains an explicit experiment because it was exact but slower in end-to-end search.
- Built-in Freestyle White root quiet-move ordering for the embedded quantized codebook.
It refines only eligible quiet runs and leaves tactical/PV/killer boundaries
intact. Set
FIGRID_WHITE_ROOT_ORDER=offfor the 0.8.0 ordering path; custom and floating-point codebooks, plus non-Freestyle rules, disable it automatically. - Incremental packed Pattern4 windows and exact-order candidate-frontier
maintenance in
pbrain-figrid. They reduce repeated board scanning without changing evaluation or move order. Search acceleration lives in an optionalSearchersidecar, leaving the publicBoardlayout unchanged from 0.8.1; the shipped pbrain enables both paths by default. - Selective search (1.1). Pre-empting the square of an opponent three is no longer treated as forcing, and a small
per-move policy (a 213 → 16 → 1 MLP over the codebook evaluator's own state, embedded, 13.8 KB) orders quiet moves
and prunes late ones at nodes with two or more remaining plies. About 1.3 plies deeper at 2 s per move; positive
against every engine and rule set it was measured on (see the changelog). On by default in
pbrain-figrid; the policy applies to full-vocabulary dim-32 codebooks such as the release models. - Exact directional deltas for the quantized codebook evaluator. Make/undo
applies only changed
(cell, direction)embeddings, then one activation and region delta per affected cell. The shipped pbrain enables this 0.8.3 path by default; ordinary librarySearcherinstances remain opt-in.
How the engine developed
figrid was inherited as a small board library in April 2026 and rebuilt as an engine over one season:
- Search backbone (0.4–0.6). A NORU NNUE port with a root VCT/VCF prover, threat-tiered move ordering, a larger transposition table, internal iterative reduction and late-move pruning. Reductions and pruning never touch forcing moves; an early version that did lost most of its games.
- Codebook evaluator (0.7–0.8). The leaf evaluator moved from the flat NNUE to a categorical codebook over 11-cell line patterns. An early codebook matched the flat network at about 1/112 of its parameters; the current one uses the complete 199,827-pattern vocabulary, because truncating rare patterns turned out to hide exactly the tactical ones.
- Distilled labels (0.9–0.10). Value labels come from stronger engines, with a fail-closed rule that keeps a position only when two search budgets agree. A clean relabelling gave the largest single gain of the season, and rule-specific teachers carried it to Caro, Renju and 20×20.
- Speed (1.0). The whole state-update path stopped repeating work, with search results held identical; this converted into strength where an evaluation-only speedup earlier had not.
- Selectivity (1.1). Learned move policies had repeatedly failed to help. Counting where the search spends its nodes showed why: two thirds of the tree lies below forcing moves, which the policies never touched, and one kind of "forcing" move — pre-empting an opponent's three — was less useful than a quiet move. Narrowing the forcing set and letting a cheap policy prune quiet moves made the search about 1.3 plies deeper and stronger against every opponent measured.
Every step was gated the same way: refactors must reproduce evaluation dumps and fixed-depth searches exactly, and strength claims come from matched-seed matches against external engines, replicated on fresh seeds.
Measured state-update path
Same-binary engineering measurements of the incremental-update work. The last row also has a playing-strength check: against Pela at 2000 ms per move, the 1.0 search scored 335.5/600 vs 313/600 for 0.10.0 on the same seeds.
| Release | Change | Measured result | Correctness |
|---|---|---|---|
| 0.8.2 | Packed 11-cell Pattern4 windows | 21.11% less fixed-depth time than 0.8.1 | zero mismatches in a 100,000-operation rebuild audit |
| 0.8.2 | Exact-order candidate frontier on top of the packed windows | 1.31% further saving; 22.15% combined | identical decisions and node counts over 1,022 roots |
| 0.8.3 | Exact codebook directional deltas | 19.68% less time without root VCT, 9.25% with it | exact 100,000-operation and 100,000-transition audits; identical decisions and nodes over 1,022 roots |
| 1.0 | Full-vocabulary journal recomputes only the window direction that contains the move; qsearch scores an immediate five without making it | fixed-depth search 1.25–1.55× faster across rules 0/1/4/9 and 20×20 | bestmove, depth and eval identical to 0.10.0 at fixed depth; eval dumps bit-identical |
The generic reversible journal was subsequently extracted into cb2vec.
That boundary is an architecture and reuse change, not a separate speed or
strength claim. Details are in the changelog and the
packed-window and frontier,
directional-delta,
and journal extraction
reports.
Quick start
Use as a Piskvork engine
Build the engine binary:
RUSTFLAGS="-C target-cpu=native"
Add target/release/pbrain-figrid (or .exe on Windows) to Piskvork as an AI player. With embed-weights,codebook-eval, the NNUE ordering weights and the packed codebook artifact are both available without external model files.
If you build without embed-weights, set FIGRID_WEIGHTS=path/to/weights.bin
or place the file at ./models/ so the binary can locate the ordering weights
at startup. In a codebook-eval build, FIGRID_CODEBOOK_WEIGHTS is the
single codebook knob: unset loads the embedded codebook, a path loads that
model (JSON or the NGCB1 binary format, detected by content, with either the
legacy 4,266-id or the full 199,827-id pattern vocabulary), and
off/0/false/no/empty disables the codebook leaf evaluator and returns
to the v52-lineage NNUE leaf evaluator. This fallback is different from the
flat i16 representation used by the normal codebook runtime. The codebook
always runs through the quantized kernel; NORU_CODEBOOK_EVAL_SCALE=<float>
overrides the eval scale.
The embedded artifact uses compact factored storage, but direct factored
evaluation is not the default. Leave NORU_CODEBOOK_FACTORED unset or set it
to off for the established flat i16 runtime. Setting
NORU_CODEBOOK_FACTORED=on opts into the exact, memory-smaller direct path;
the 0.8.3 audit measured wall ratios 1.038437 with VCT off and 1.012149
with product VCT on, so it was not promoted.
FIGRID_WHITE_ROOT_ORDER accepts auto (default), on, or off. Explicit
on fails closed unless the embedded quantized codebook is active.
The state-update optimizations have independent rollback switches:
NORU_PACKED_LINE_WINDOWS=offrestores the 0.8.1 Pattern4 updater.NORU_CANDIDATE_FRONTIER=offkeeps packed windows but restores legacy candidate generation.NORU_CODEBOOK_DIRECTIONAL_DELTA=offrestores full accumulator refreshes for the quantized codebook evaluator instead of the 0.8.3 directional delta journal.
Selective-search switches (on by default since 1.1):
NORU_POLICY_MOVE— unset or empty uses the embedded per-move policy,offdisables it, any other value is aPGM1policy file. The policy applies only with a full-vocabulary codebook of matching dimension.NORU_DEMOTE_PREEMPT_THREE=offtreats pre-empting an opponent three as forcing again.
With both off the search is identical to 1.0.
Opt-in search switches (all off by default):
NORU_POLICY_ORDER=path/to/table.binranks quiet moves with a learned pattern-conditioned policy table (PCB1v1format, legacy or full vocabulary). Threat tiers and killers keep precedence. Tables whose scores could saturate the ordering band are rescaled once at load.NORU_POLICY_REDUCE=on(requiresNORU_POLICY_ORDER) adds policy-rank late move reductions and an earlier late-move-pruning cutoff.NORU_FORCED_REPLY_RESTRICTION=onsearches only the rules-determined reply when the opponent threatens an immediate five (Freestyle and Standard).
An empty value means "default" for every boolean switch. pbrain-figrid
fails closed on stale or misspelled switches: any non-empty NORU_* /
FIGRID_* variable outside its known list (the variables above plus
NORU_PBRAIN_FIXED_DEPTH, NORU_PBRAIN_MAX_DEPTH, NORU_SEARCH_PROFILE,
NORU_TEST_WEIGHTS, FIGRID_BENCH_WEIGHTS, and FIGRID_VCT_*) makes it print
ERROR unknown engine variable <NAME> and exit. The effective configuration
is announced once as a MESSAGE config: ... line before the first START
reply.
Model tools (with codebook-eval): ngcb-convert IN.json OUT.ngcb converts a
JSON codebook to NGCB1 with a bit-exact round-trip check, and
FIGRID_CODEBOOK_WEIGHTS=<model> t1-eval-dump --input games.jsonl --output dump.csv [--stride N] writes a bit-exact static-eval dump
(game_id,ply,value_bits) for comparing builds.
Use as a library
[]
= "1"
use ;
let mut board = new;
board.make_move; // black H8 (row 7, col 7, 0-indexed)
board.make_move; // white I8 (row 7, col 8)
println!; // Black (the side about to move)
NNUE weights and the search struct are exposed for users who want to drive the engine programmatically rather than through the Piskvork protocol.
The packed-window and candidate-frontier accelerators are off in a newly
constructed Searcher; library callers opt in through
set_use_packed_line_windows and set_use_candidate_frontier. The
directional-delta path has been on by
default since 0.8.6 (set_use_codebook_directional_delta(false) restores full
refreshes). The 1.1 selective search is opt-in for library callers through
set_policy_move(Some(Arc::new(PolicyMove::embedded()))) and set_demote_preempt_three(true). Enabling
codebook-eval also activates
the optional cb2vec dependency. Consumers that only need the generic
codebook and reversible-journal primitives can use the standalone package
directly.
Build
Local / development — target the host CPU for maximum performance:
RUSTFLAGS="-C target-cpu=native"
On PowerShell, set RUSTFLAGS first:
$env:RUSTFLAGS='-C target-cpu=native'
cargo build --release
Reproduce the GitHub Windows x86_64-v3 release asset with the native MSVC target, static C runtime, and deterministic linker mode:
$env:RUSTFLAGS='-C target-cpu=x86-64-v3 -C target-feature=+crt-static -C link-arg=/Brepro'
cargo build --release --locked --target x86_64-pc-windows-msvc `
--bin pbrain-figrid `
--features embed-weights,codebook-eval
Release preparation builds this command in two clean target directories and requires byte-identical executables before packaging.
20×20 engine (Gomocup Freestyle / Fastgame): the same recipe with the
board20 feature, shipped as pbrain-figrid_20.exe beside the 15×15 binary:
cargo build --release --locked --target x86_64-pc-windows-msvc `
--bin pbrain-figrid `
--features embed-weights,codebook-eval,board20
Portable builds with the GNU toolchain — -C target-cpu=native is wrong
for a portable binary because it targets the build host. The Gomocup 2026
tournament machines guaranteed SSE4.1, SSE4.2, POPCNT, AVX, and AVX2, which
matches x86_64-v3. With the GNU target instead of MSVC:
RUSTFLAGS="-C target-feature=+crt-static -C target-cpu=x86-64-v3" \
For an AVX-512-targeted variant, additionally enable the avx512 cargo
feature. It requires Rust ≥ 1.89 on the build host:
RUSTFLAGS="-C target-feature=+crt-static -C target-cpu=x86-64-v4" \
The x86_64-v4 binary itself requires a compatible machine, so retain the
x86_64-v3 build as the portable fallback. NORU's avx512 feature performs
runtime AVX2 fallback only when the surrounding binary is compiled for a
compatible baseline.
Current direction
1.0 covers every Gomocup board: Freestyle and Fastgame on 20×20, plus Freestyle, Standard, Renju and Caro on 15×15.
Development favours exact, independently checked changes. Speedups must leave fixed-depth search identical, and
strength claims come from matched-seed matches against external engines. Reusable codebook mechanics are developed in
CB2Vec; Gomoku-specific evaluation, search, and protocol policy remain in
figrid-board.
Maintainership
As of 2026-04-20, primary maintainership has been transferred from the original author wuwbobo to nicotina04. Future development targets a stronger NNUE-based engine; some of the board / rule / tree library features in the 0.3.x series versions might be refactored and introduced again in the future (if needed).
Legacy users
Users who need the pre-Rust figrid-board as a Linux alternative to Renlib can download tag v0.20.
Acknowledgments
- Rapfi for advancing public NNUE work in Gomoku and for serving as a reference point during evaluation development.
- noru for the underlying Rust NNUE training and inference stack.
- wuwbobo for the original engine and for entrusting
figrid-boardto its current maintainer. - CB2Vec for the reusable categorical training, quantization, artifact, scoring, and reversible token-update primitives used by the codebook evaluator.
License
Dual-licensed under either of MIT or Apache-2.0 at your option, matching the SPDX identifier MIT OR Apache-2.0 declared in Cargo.toml.