onepass-c4 β a one-pass specialist that plays Connect Four
A one-pass specialist scores a supplied list of options in one forward pass: context bytes in, one
score per option out, no generation, no tokenizer. This one plays Connect Four: the board goes in as
44 bytes, the legal columns go in as short strings (column 4), and the highest-scoring column is
played. No search, no hand-written rules β the scores are the whole decision.
Play it in your browser: https://precisit.github.io/onepass-web/demo/c4/ (the page's arena pits v2 against v1 or a random player). The 1.6 MB ternary version (v3, below) plays in https://precisit.github.io/onepass-web/demo/c4-size/.
The story behind the model: Meet your one-pass AI opponent follows the first attempt, the better teacher and the browser release.
v2 (current)
| architecture | the one-pass option scorer from one-pass-specialists (byte embedding β 8 transformer layers, width 256 β option attention head) β the same architecture as v1, larger config |
| parameters | 7.38 M |
| input | context 44 bytes: 1: or 2: (did the player to move open the game?) + 42 cells, bottom row first, m = mover, t = opponent, . = empty; options column 1 β¦ column 7, 8 bytes each |
| files | onepass-c4-v2-int8.onnx (7.8 MB, ONNX Runtime dynamic int8 β what the demo loads) Β· onepass-c4-v2.onnx (fp32, 29.7 MB) Β· v2/model.safetensors + v2/config.json (toolkit checkpoint) |
| ONNX inputs | context_ids int32 [1, 44], option_ids int32 [1, 7, 8], option_mask int32 [1, 7]; bytes are UTF-8 + 1, 0 = padding; output logits [1, 7] |
How well it plays
Frozen protocol, pre-registered before training: 200 games per match, colours alternate, games start from the empty board, and both players make a uniformly random move 5 % of the time (otherwise two deterministic players replay one game). Score = (wins + draws/2) / games, 95 % Wilson intervals.
| opponent | v2 score (checkpoint) | v2 score (int8 file) | v1 score |
|---|---|---|---|
| depth-2 alpha-beta bot | 0.905 [0.856β0.938] | 0.910 | 0.03 |
| depth-4 alpha-beta bot | 0.915 [0.868β0.946] | 0.905 | 0.03 |
| depth-6 alpha-beta bot | 0.893 [0.842β0.928] | 0.878 | 0.02 |
| perfect player (exact solver) | 0.475 [0.407β0.544] | β | β |
| random player | 1.000 | β | 0.885 |
| v1 | 0.985 | β | β |
For scale, a perfect player scores 0.890 against the depth-4 bot and 0.925 against the depth-6 bot under the same protocol β its own 5 % random moves cost it the rest. Against the perfect player itself v2 is close to even.
Move level, on a held-out set of 17 325 positions covering every ply (half from strong self-play, half from games between weak and strong players): 98.7 % of v2's moves keep the game-theoretic value of the position (win stays win, draw stays draw); 97.0 % on positions where the choice matters; 93.5 % of its moves are a fastest win / slowest loss. It reads the board: reassigning every stone's owner (column heights kept) drops the non-trivial rate by 49 points.
Browser: the int8 file runs in ONNX Runtime Web; ~20 ms per decision on wasm and ~9 ms on WebNN in headless Chromium on an M1 Max (v1 on the same machine: 10.8 ms on wasm).
How it was trained
Full recipe, protocol and every measurement: examples/c4
in one-pass-specialists.
- Labels: every legal column of every position scored exactly (win/draw/loss and distance), by connect-four-ai (MIT), verified before use against Pascal Pons' public test sets (6 000 positions), the UCI Connect-4 8-ply database (67 557 positions) and an independent solver β zero disagreements.
- Positions: 41.6 M unique positions from TonyCWang/ConnectFour (MIT; solver self-play at varied temperature, every column scored β re-solved on a 20 k sample with zero disagreements) plus 513 k positions from our own games between weak and strong players.
- Target: listwise cross-entropy over the legal columns, towards a softmax of the exact scores in which the win/draw/loss class dominates and distance breaks ties.
- Schedule: 18 000 steps Γ 1 024 positions (β 18 M samples), ply-balanced sampling, random horizontal mirroring; about two hours on one Apple M5 Pro.
Limitations
- Not perfect: 1.3 % of its moves give away the game-theoretic value, and it loses to a perfect player about as often as it wins under the noisy protocol.
- Its choice between near-equal columns is not mirror-symmetric (~77 % consistency on mirrored boards).
- The int8 file picks a different column than the fp32 one on 1.9 % of positions (mostly near-ties; its value-preserving rate is the same, 98.75 %); its measured strength is shown separately above.
v3, ternary (1.6 MB)
The same architecture and training data as v2, trained with ternary weights (-1, 0 or +1, times one fp16 scale per 128 weights) in the forward pass from the first step. Code, all records and the full results: precisit/onepass-webgpu-ternary.
The story behind it: A game-playing AI in 1.6 MB (also in Swedish).
onepass-c4-v3-t34.onnx |
1.59 MB. T34, the 3:4 structured ternary format of Sherry (Huang et al., ACL 2026, arXiv:2601.07892): in every four weights one is zero and three are -1 or +1, 5 bits per four weights; 1.375 bits per ternary weight with scales. Trained from scratch with a final stage of 24 000 steps. sha256 6ba65b964ad35a7029535461280b3726c332634b28ae16712d660e2dd56b3627 |
onepass-c4-v3-b243.onnx |
1.93 MB. Base243: any ternary values, five per byte in base 3 (as llama.cpp's TQ1_0); 1.75 bits per ternary weight with scales. Trained from scratch. sha256 656bffc5a0e61b6d6d378a35e3976768987b2b9350adaec1266a5b99760fc5ee |
*.plan.json |
the layer plans for the onepass-webgpu runtime |
| inputs and outputs | the same as v2 |
The byte embedding and the scoring head's q/k/v matrices are int8; positions, biases and norms fp16.
| same protocol as v2 | v2 (checkpoint) | v3 T34 | v3 Base243 |
|---|---|---|---|
| depth-4 alpha-beta bot | 0.915 | 0.930 [0.886, 0.958] | 0.885 |
| depth-6 alpha-beta bot | 0.893 | 0.910 | 0.877 |
| perfect player | 0.475 | 0.427 | 0.427 |
| moves that keep the game's value (17 325 positions) | 98.7 % | 98.9 % | 98.6 % |
Against the search bots the T34 file scores 0.925 to 0.935 (depth 4) and 0.885 to 0.923 (depth 6) over three game seeds. It plays at v2's level; against the perfect player it stays below v2. A second training run of the grid's T34 recipe (with the shorter final stage) scored 0.882 against the depth-4 bot, so single runs vary.
Running it. The ternary matrices use custom ops (com.precisit.T34MatMul, com.precisit.Base243MatMul) with a
model-local FunctionProto reference decode, so standard ONNX Runtime runs the files as they are (slowly: it decodes
every call). The fast path is onepass-webgpu with the format plugin kernels/ternary-formats.js from the ternary
repository: about 1.1 ms per move for T34 on an M5 Pro in Chrome. ONNX Runtime running each file chooses the same
move as the evaluated model on all 17 325 positions, and the WebGPU runtime matches each file's reference decode on
all of them.
v1 (previous, kept for comparison)
onepass-c4-8x24.onnx + model.safetensors (706 k parameters, 224 context bytes, 8 Γ 24 option
bytes): trained on ~80 k endgame positions only. It read the position from a move-history line
rather than the board, and scores 0.02β0.03 against the search bots above. It stays here because the
demo's arena plays it against v2.
License and credits
MIT. Labels by connect-four-ai (MIT, Benjamin Rall); ternary formats: T34 from Sherry (Tencent), Base243 packing as in llama.cpp's TQ1_0 (compilade); positions from TonyCWang/ConnectFour (MIT); evaluation positions from the UCI Connect-4 database (John Tromp, CC BY 4.0) and Pascal Pons' test sets were used only to verify the labeller. Architecture adapted in one-pass-specialists from Cua-S1 / jevlike (MIT).
- Downloads last month
- 67