Archon 3 (g115): a Magic: The Gathering Pauper policy network

Unofficial fan content. Not affiliated with, endorsed by, or sponsored by Wizards of the Coast. Magic: The Gathering and its card names are trademarks of Wizards of the Coast.

What it is

A ~1.23M-parameter policy and value network (kernel-policy-value-net-8, hidden size 64, 16-dimension card embeddings) that plays Pauper on the open-source mtg-kernel rules engine. It is Phase 1 lineage g, block 115, from seeded initialization engineering-fresh-initialization-002-b (the parent line of Basilisk 1), after 32,400 optimizer steps. Trained by reinforcement learning from random initialization: terminal win or loss is the only reward, with no human game data and no heuristic-bot imitation. Training decks: six of the eight board decks plus Terror; it never saw Spy or CawGates. The file is the full training checkpoint (weights plus Adam state), the exact file the rated entrant loads.

The Archon family is the first generation of mtg-kernel networks. Its successor, Basilisk 1, is the current top mtg-kernel model and is not public yet (the project publishes every model except its current top one).

How strong

Spellbench pauper-kernel-v2 board, snapshot 2026-10-28, anchored so the uniform-random bot is 1000 Elo. This model played 896 rated games.

Entrant Elo (95% CI)
Basilisk 1 (later mtg-kernel network) 1431.0 (1350.1 to 1521.2)
Archon 3 (g115) 1381.1 (1315.4 to 1462.7)
gorge-ar8 (non-learning bot) 1259.6 (1181.2 to 1348.6)
Archon 2 (c12) 1211.2 (1150.9 to 1287.9)
Archon 1 (a48) 1178.7 (1114.4 to 1254.0)

The rated entry is policy-only: it samples from the network's softmax (sampled-wide-v1) with no search. Board data: https://github.com/jackmaiorino/spellbench/tree/main/benchmarks/pauper-kernel-v2.

How to run it

These steps were verified end to end for Basilisk 1 (same engine and file format) on Windows 11 x86_64 (Git Bash), 2026-11-03, from fresh clones. For this package, the step 3 load and model-state check was rerun on 2026-11-07. It needs the exact engine it was rated on. Other platforms are untested.

Prerequisites

  • Git for Windows. Run every command below in Git Bash from this folder (it provides sha256sum; in PowerShell the > redirect writes UTF-16 and breaks the catalog).
  • Rust through rustup plus the Visual Studio Build Tools "Desktop development with C++" workload. The repo pins Rust 1.94.1 and rustup fetches it on the first build.
  • Python 3.11 or newer on PATH (python --version). The bot and the arena use only the standard library, so uv is optional.
  • About 1 GB of disk. The build took 8 minutes on 4 cores.

1. Check the files, then build the engine (clones go next to this folder)

sha256sum -c SHA256SUMS                      # five lines, all OK
git clone https://github.com/jackmaiorino/mtg-kernel ../mtg-kernel
git -C ../mtg-kernel checkout 92ec2393db34bea805a85991163458b9ff890f57
git clone https://github.com/jackmaiorino/spellbench ../spellbench
cd ../mtg-kernel
cargo build --release --locked -p mtg-kernel --bin agent_bridge_v1 --bin spellbench_scorer_v1 --bin spellbench_kernel_catalog
target/release/spellbench_kernel_catalog.exe > pauper-v2-catalog.json
sha256sum pauper-v2-catalog.json             # af8e1b914d76ed6d30318403c6d434fc4d3a651a68d7e37c79c9d09c68cc457e
cd -

The compiler prints dozens of warnings; ignore them. Your binaries will not be byte-identical to the rated ones; the model-state check below is what ties the weights to the rating.

2. Make local copies of the two path files. weights/g115.scorer.json and weights/play-import-b.json hold absolute paths from another machine, and the scorer config pins the descriptor's sha256. This writes corrected copies to local/ and leaves weights/ untouched, so sha256sum -c keeps passing:

python - <<'EOF'
import hashlib, json, pathlib
w = pathlib.Path("weights").resolve()
out = pathlib.Path("local").resolve(); out.mkdir(exist_ok=True)
imp = json.loads((w / "play-import-b.json").read_text())
imp["initialization"]["path"] = (w / "init-generated-b/initialization.json").as_posix()
imp["parameters"]["path"] = (w / "init-generated-b/parameters.f32le").as_posix()
(out / "play-import-b.json").write_text(json.dumps(imp, separators=(",", ":")))
cfg = json.loads((w / "g115.scorer.json").read_text())
cfg["source"]["play_import"] = {"path": (out / "play-import-b.json").as_posix(),
    "sha256": hashlib.sha256((out / "play-import-b.json").read_bytes()).hexdigest()}
cfg["source"]["checkpoint"]["path"] = (w / "88c0b997708c2b5156b44f3940ad9d5d682f78ac24d346978bb3c9f34c59e8d1.json").as_posix()
(out / "g115.scorer.json").write_text(json.dumps(cfg, indent=2))
EOF

3. Check that the weights load

python ../spellbench/integrations/mtg_kernel/kernel_flat_bot.py --scorer ../mtg-kernel/target/release/spellbench_scorer_v1.exe \
    --config local/g115.scorer.json --name g115 --version pauper-v2.0.0 \
    --expect-model-state 8139016ca561961714f25e22a9d6f7fc888548fc332e45b6bce402dfc43159f2

Success is silence: the bot waits for the arena on stdin (press Ctrl+C). On a wrong or moved checkpoint it exits with status 1 before playing and prints scorer loaded model state ..., expected ... or a checkpoint_load error.

4. Play it. Create local/local.json from this template, replacing <MODEL>, <KERNEL> and <SPELLBENCH> with absolute paths using forward slashes (this folder, ../mtg-kernel and ../spellbench) and <PYTHON> with the full path of your python.exe:

{
  "PYTHON": "<PYTHON>",
  "MTG_KERNEL_V2_BRIDGE": "<KERNEL>/target/release/agent_bridge_v1.exe",
  "MTG_KERNEL_SCORER": "<KERNEL>/target/release/spellbench_scorer_v1.exe",
  "MTG_KERNEL_PUBLIC_CATALOG": "<KERNEL>/pauper-v2-catalog.json",
  "MTG_KERNEL_V2_SOURCE": "92ec2393db34bea805a85991163458b9ff890f57",
  "MTG_KERNEL_V2_ENGINE": "<SPELLBENCH>/integrations/mtg_kernel/kernel_engine_v2.py",
  "MTG_KERNEL_V2_BOT": "<SPELLBENCH>/integrations/mtg_kernel/kernel_flat_bot.py",
  "G115_SCORER_CONFIG": "<MODEL>/local/g115.scorer.json",
  "G115_CHECKPOINT": "<MODEL>/weights/88c0b997708c2b5156b44f3940ad9d5d682f78ac24d346978bb3c9f34c59e8d1.json"
}

Then start the human client (still in this folder):

PYTHONPATH=../spellbench/python python -m spellbench.human --games 16 --opponent g115 \
    --local local/local.json --out ../g115-sessions --no-record

It prints a Play here: http://127.0.0.1:8765/?t=... link and opens your browser (add --no-browser to skip that). Games come in whole rotations of the eight board decks, so --games rounds up to a multiple of 16 and you play both seats of each deck. The default clock is a 30-minute bank per game (--bank-minutes). The session runs on the pauper-kernel-v2 benchmark definition (the board these ratings come from) and is unrated. Without --no-record the client also keeps engine transcripts and wants 60 GiB of free disk. Ctrl+C ends the session and keeps the games played.

Format

weights/88c0b997708c2b5156b44f3940ad9d5d682f78ac24d346978bb3c9f34c59e8d1.json uses schema mtg-kernel-expanded-deck-fresh-checkpoint/v1 (float32 stored as raw 32-bit words). It loads only through the mtg-kernel scorer, not PyTorch or Hugging Face libraries. Observation: x_kernel_flat_v4 (feature contract digest c4af415a..., encoding digest 271c0e5a...). Output: one logit per legal candidate action plus a value estimate.

Training

GAE advantages (gamma 1, lambda 0.9), entropy 0, learning rate 1e-4, value coefficient 0.5.

Limitations

  • Pauper only, and only the Spellbench Pauper pool deck lists.
  • Policy-only and early-generation: expect obvious misplays.
  • Ratings are on one board with a fixed deck pool and seat-swapped pairs.

License

MIT for the code and these weights, matching the mtg-kernel repository. Card data and names belong to their owners.

Downloads last month

-

Downloads are not tracked for this model. How to track
Video Preview
loading