--- license: cc-by-nc-4.0 library_name: quantik-models pipeline_tag: reinforcement-learning tags: - quantik - board-games - policy-value-network - resnet - onnx model-index: - name: resnet-c128-b6 results: - task: type: reinforcement-learning name: Quantik optimal-move prediction dataset: type: quantik-exact-solutions name: exact-sampled metrics: - type: accuracy name: Held-out optimal-move accuracy, plies 4-6 value: 0.9126 - type: accuracy name: Held-out optimal-move accuracy, plies 7-12 value: 0.972 - type: win_rate name: Arena win rate vs the field (1800 games) value: 0.4783 --- # resnet-c128-b6 A policy/value network for **Quantik**, 1,786,823 parameters. Quantik is a two-player game on a 4x4 board with four piece shapes. A player may not place a shape in a row, column or 2x2 zone where that shape already appears, whoever played it — so a move can be blocked by your own piece. The first player to complete a line or zone holding all four distinct shapes wins. There are no draws. This model predicts, for a given position, which move an exact solver would play (policy) and who is winning (value). ## About this project Quantik began as a holiday rivalry and became an engineering project. Before building an AI to play — or teach — the game, the game itself had to be represented precisely: an exact notation, a canonical form under the board's 192 symmetries, and a bitboard the rules can be computed on cheaply. That foundation is what these models are trained on. Every label is exact, produced by a solver rather than by self-play, so the network fits ground truth instead of its own earlier opinions. The engineering is written up as a series on **The Full-Stack Mind**: first-principles representation, then Monte-Carlo search, beam search, exact endgame proof, and a tournament where the engines finally played each other. - Series: ## Architecture A convolutional residual trunk — the incumbent design, and the one every hyperparameter in this project was originally chosen for. ```mermaid flowchart LR IN["board
(B,9,4,4)"] --> STEM["stem
Conv3x3 9→C · BN · ReLU"] STEM --> TRUNK["trunk
B × residual block
Conv3x3 · BN · ReLU · Conv3x3 · BN · +skip"] TRUNK --> PH["policy head
Conv1x1 C→2 · flatten · Linear 32→64"] TRUNK --> VH["value head
Conv1x1 C→1 · flatten · Linear · tanh"] PH --> POL["policy logits (B,64)"] VH --> VAL["value (B,)"] ``` | | | |---|---| | blocks | 6 | | channels | 128 | | value_hidden | 64 | | parameters | 1,786,823 | Every architecture in this family is matched to within 1.2% on parameter count, so a comparison between them is about the design and not about capacity. ## Results | metric | value | |---|---| | Held-out optimal-move accuracy, plies 4-6 | **0.9126** | | Held-out optimal-move accuracy, plies 7-12 | **0.9720** | | Arena win rate vs the field (1800 games) | **47.8%** | Held-out accuracy is measured on exactly solved positions sharing no canonical key with the training corpus, up to the 192 board symmetries — so it measures generalisation, not recall. It is reported split rather than pooled because the corpus contains nothing at the shallowest plies, and a pooled figure is dominated by deep positions where every model is near perfect. ## Input and output contract ``` input (B, 9, 4, 4) float32 tensor-board.v1, mover-relative output (B, 64) policy logits action_index = shape * 16 + position (B,) value in [-1, 1] +1 = good for the side to move ``` Planes 0-3 are the side to move, 4-7 the opponent, 8 a ply indicator. `position = row * 4 + col`. ### Legality masking happens outside this model It emits logits over all 64 actions, including illegal ones. Applying the legal-move mask before the softmax is the caller's job. **An unmasked `argmax` from this model will play illegal moves** — silently, because an illegal move looks like a bad move rather than like a bug. This is by design. Quantik's rules are exact and cheap to compute in `quantik-core`, so the network is never asked to approximate them and never spends capacity on legality. ## Usage There is no `AutoModel` for this architecture — the Hub cannot reconstruct it from weights alone. Two supported paths. ### With `quantik-models` Reads `manifest.json` and rebuilds the network from `architecture_spec`, and gives you the legality masking for free. ```bash # quantik-models is not on PyPI yet; install it from source. pip install 'quantik-models[torch] @ git+https://github.com/mberlanda/quantik-models-py' pip install huggingface_hub ``` ```python from huggingface_hub import snapshot_download from quantik_models.arena.registry import load_evaluator from quantik_models.env import fastboard as fb evaluator = load_evaluator(snapshot_download("brpoplpush/quantik-resnet-c128-b6"), "cpu") boards = fb.empty_boards(1) # (1, 8) uint16 policy, value = evaluator.evaluate(boards) # masking applied ``` ### With ONNX Runtime, and neither torch nor this package ```bash pip install onnxruntime numpy huggingface_hub ``` ```python import numpy as np, onnxruntime as ort from huggingface_hub import hf_hub_download path = hf_hub_download("brpoplpush/quantik-resnet-c128-b6", "model.onnx") session = ort.InferenceSession(path) # (B, 9, 4, 4) float32, mover-relative — see the contract above. tensors = np.zeros((1, 9, 4, 4), dtype=np.float32) policy, value = session.run(None, {"board": tensors}) # The mask is yours to apply. `legal` is a (B, 64) bool array; # quantik_models.env.fastboard.legal_masks computes it, and so # does quantik-core in Rust. # policy = np.where(legal, policy, -np.inf) ``` ### The rules engine Legality, symmetry and the exact solver live in `quantik-core`, which is published for both languages and is what generated the training labels. ```bash pip install quantik-core # Python, >=3.12 cargo add quantik-core # Rust, 2021 edition ``` ## How it was trained | | | |---|---| | corpus | `exact-sampled.npz` | | architecture preset | `medium` | | epochs | 16 | | batch size | 1024 | | learning rate | 0.002 (cosine to 1e-05) | | weight decay | 0.0001 | | seed | 20260828 | | symmetry augmentation | yes | | ply-balanced sampling | yes | Labels are **exact**, not bootstrapped: every training target comes from a solved position, so the network is fitting ground truth rather than its own earlier opinions. The learning rate is a property of the architecture rather than a project-wide default. A single shared rate is not equal treatment between architectures — it privileges whichever one it was chosen for — and correcting that in this project reversed several conclusions rather than merely shifting decimals. Ply-balanced sampling gives every game stage equal attention instead of attention proportional to how many positions it happens to contribute. The corpus is dominated by late positions; the match is decided early. ## Limitations **Accuracy is not uniform across the game.** Deep positions are nearly forced and every model in this family is close to perfect there; the shallow openings are where they differ and where they are weakest. | ply | accuracy on provably won positions | |---|---| | 4 | 0.8791 | | 5 | 0.9173 | | 6 | 0.9391 | | 7 | 0.9545 | | 8 | 0.9596 | | 9 | 0.9674 | | 10 | 0.9916 | | 11 | 0.9932 | | 12 | 0.9954 | Weakest at ply 4 (87.9%), strongest at ply 12 (99.5%). **The evaluation is against solved positions and other engines, not against people.** Nothing here says how it plays against a human. **One training seed.** Every number on this card comes from a single run of this architecture. ## Files - `model.safetensors` — `sha256:7c5a259e7f7a1e3b9e70b2b743145867cf6bc1499ac31ec79e000ae4b6579ad2` - `model.onnx` — opset 18, `sha256:bac96ad537dd3eebc999e7d61dc4e178badba15e2cc2e04cb22e6649a4f3f673`, dynamic batch dimension - `config.json` — the architecture spec, readable without loading anything - `manifest.json` — the `model-checkpoint.v1` record this repo was staged from - `training-report.json` — the epoch that produced these weights, and its metrics Contract version `1.2.0`. Exported 2026-08-28. ## Other models in this family Same contract, same corpus, same training protocol — interchangeable at the interface, so they can be compared directly. - [`brpoplpush/quantik-cpool-c191-b6`](https://huggingface.co/brpoplpush/quantik-cpool-c191-b6) - [`brpoplpush/quantik-attn-d192-b6`](https://huggingface.co/brpoplpush/quantik-attn-d192-b6) - [`brpoplpush/quantik-mlp-h455-b4`](https://huggingface.co/brpoplpush/quantik-mlp-h455-b4) ## Source - Model code and training: https://github.com/mberlanda/quantik-models-py - Rules engine (Python): https://github.com/mberlanda/quantik-core-py - Rules engine (Rust): https://github.com/mberlanda/quantik-core-rust - Shared schemas: https://github.com/mberlanda/quantik-core-contracts ## Licence **The weights in this repository are CC BY-NC 4.0.** Free to use, share and adapt for research, teaching and any other non-commercial purpose, with attribution. **Commercial use requires a separate agreement** — open an issue on the source repository or contact the author. This is deliberately not an OSI-approved open-source licence. Every OSI licence permits royalty-free commercial use, which is the one thing this reserves. **The code is separate and more permissive.** `quantik-models` and `quantik-core` are MIT, so the training pipeline, the rules engine and the evaluation harness carry no such restriction — only these weights do.