Migrates the reaper-king-no-more proof of concept into decks/reaper-king-no-more/
as the first deck under a shared tools/ engine, so the repo can hold many
Commander decks instead of one implicit one.
- tools/deckctx.py: new shared DeckContext, resolves --deck / $MTG_DECK / cwd,
loads a deck's config.yaml, and derives color identity from the commander's
own card data (never a hardcoded color combination).
- cards.py: drop the JUND=frozenset("BRG") constant; jund_legal() renamed to
identity_legal(card, identity); DB() takes an explicit blacklist_path since
the blacklist is per-deck, not shared.
- combos.py, roles.py, similar.py, similar_text.py, bracket_check.py: take
--deck, use the deck's real identity instead of Jund/BRG.
- power.py, card_stats.py, forge_report.py: OURS deck-name constant now comes
from the deck's config (name: / commander), matched via DeckContext instead
of hardcoded "Reaper King No More".
- to_dck.py, batch_run.py, ab_test.py: .dck filename and [metadata] Name= are
derived from the deck (slug / config name), not a hardcoded "reaper.dck".
- generate.py: RECIPES and make_variants.py's VARIANTS (deck-specific
hypotheses) moved out of Python into decks/reaper-king-no-more/variants.yaml;
protected cards now come from config.yaml's protected: list instead of a
hardcoded set.
- New tools/mtg dispatcher: `tools/mtg decks/<name> <tool> [args...]` runs any
tool against any deck without cd-ing into its directory.
- data/ (Scryfall/MTGJSON/Spellbook bulk, ~1.2GB, gitignored) and embeddings.npz
move to the repo root as shared, commander-format-wide data.
- docs/howto.md: moved from the deck dir (it documents the tool, not the deck)
and updated its example commands to the real generalized CLI.
- README.md: rewritten to describe the multi-deck repo and point at
decks/reaper-king-no-more as the reference example.
Verified after the change: bracket_check, combos, and roles against
reaper-king-no-more reproduce the exact PASS/11-combos/role-profile numbers
from before the move; generate.py --list now loads recipes from YAML;
make_variants.py reproduces all 7 hand-curated variants including the
mikaeus FAIL control case; to_dck.py derives the right Name= and filename;
variant.py and compare.py reproduce decks/README.md's original numbers
exactly. The original POC at ~/mtg/reaper-king-no-more is untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPKgLYpMR8szWNFXyK1XNu
83 lines
4.4 KiB
Markdown
83 lines
4.4 KiB
Markdown
# mtg
|
|
|
|
A generic toolkit for building, testing, and simulating Magic: The Gathering
|
|
Commander decks — evidenced by real games played in a real rules engine
|
|
([Forge](https://github.com/Card-Forge/forge)), not vibes.
|
|
|
|
Give it four things about a deck — a commander, a card pool, a blacklist of
|
|
cards you refuse to play, and a few constraints (bracket, land count, role
|
|
budget) — and it derives everything else from offline card databases: color
|
|
identity, legality, Game Changers, Scryfall Tagger tags (so it knows what a
|
|
"sac outlet" or "drain payoff" *is*), and the Commander Spellbook combo
|
|
database (108k+ combos). It can then generate deck variants as falsifiable
|
|
hypotheses, check them against Commander Bracket rules and house rules, and
|
|
simulate them in Forge to get card-level and deck-level statistics — cast
|
|
rate, win-rate contribution, synergy pairs — instead of guesses.
|
|
|
|
See `docs/howto.md` for the full methodology: what you supply, how pool size
|
|
and game count determine whether results mean anything, how the Forge AI's
|
|
weaknesses get worked around, and how to read the output honestly.
|
|
|
|
## Layout
|
|
|
|
```
|
|
mtg/
|
|
tools/ generic engine — works on ANY deck given its config.yaml
|
|
mtg single dispatcher: tools/mtg decks/<name> <tool> [args...]
|
|
deckctx.py resolves --deck / $MTG_DECK, loads config.yaml
|
|
cards.py shared index: oracle + tags + edhrecRank + combos + blacklist
|
|
bracket_check.py Bracket 3 + house-rule check (game changers, MLD, vacuum combos)
|
|
combos.py offline combo finder (included / one-card-away)
|
|
roles.py role classification + budget profile
|
|
generate.py role-budget variant generator (recipes in <deck>/variants.yaml)
|
|
make_variants.py hand-curated cut/add variant generator (same file)
|
|
to_dck.py decklist -> Forge .dck
|
|
batch_run.py / run_forge.sh / ab_test.py Forge simulation, in bulk or A/B
|
|
forge_report.py / card_stats.py / power.py turn Forge logs into reports
|
|
compare.py rebuilds a deck's decks/README.md summary table
|
|
similar.py / similar_text.py / embed.py semantic "cards like this" search
|
|
fetch_data.py downloads the shared bulk data below
|
|
|
|
decks/
|
|
reaper-king-no-more/ one deck: data + config, the reference example
|
|
config.yaml commander, pool, constraints, role budget, protected cards
|
|
decklist.txt the 100 cards
|
|
blacklist.txt cards you refuse to play, with reasons
|
|
variants.yaml variant hypotheses (role-budget recipes + hand-curated cut/add)
|
|
plan.md / README.md this deck's own design notes
|
|
forge-overrides/ AI tuning for cards Forge's default AI can't pilot
|
|
sim_substitutions.yaml simulation-only stand-ins for AI-unpilotable cards
|
|
decks/<variant>/ RESULTS: one dir per archetype variant (generated)
|
|
|
|
data/ gitignored, ~1.2GB — Scryfall/MTGJSON/Spellbook bulk downloads.
|
|
Shared across every deck: this is Commander-format-wide data,
|
|
not deck-specific. One copy at the repo root.
|
|
out/ gitignored scratch output
|
|
docs/howto.md the full methodology (pool size, game count, AI tuning, caveats)
|
|
```
|
|
|
|
## Quickstart
|
|
|
|
```bash
|
|
# one-time, shared across all decks
|
|
python3 tools/fetch_data.py # ~1.2GB: Scryfall, MTGJSON, Spellbook
|
|
python3 tools/embed.py # -> data/embeddings.npz
|
|
# install Forge 2.0.14 to ~/forge (needs Java 17+)
|
|
|
|
# check the reference deck
|
|
python3 tools/bracket_check.py --deck decks/reaper-king-no-more
|
|
python3 tools/combos.py --deck decks/reaper-king-no-more
|
|
python3 tools/roles.py --deck decks/reaper-king-no-more
|
|
|
|
# or via the single dispatcher
|
|
tools/mtg decks/reaper-king-no-more bracket_check
|
|
tools/mtg decks/reaper-king-no-more variant no-mikaeus --sim 24
|
|
```
|
|
|
|
To add a new deck: copy the shape of `decks/reaper-king-no-more/` (at minimum
|
|
`config.yaml`, `decklist.txt`, `blacklist.txt`), point `--deck` at it, and
|
|
every tool in `tools/` works on it immediately — nothing in `tools/` is
|
|
hardcoded to a specific commander, color identity, or deck name. See
|
|
`docs/howto.md` for what each config field does and why, and
|
|
`decks/reaper-king-no-more/plan.md` / `README.md` for a worked example of
|
|
building one deck end to end.
|