mtg/decks/reaper-king-no-more/variants.yaml
mark rosenberg d4a8a587f0 Restructure into a multi-deck engine; generalize tools/ off Jund/Reaper hardcodes
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
2026-09-12 00:37:42 -04:00

141 lines
4.9 KiB
YAML

# Variant recipes for tools/generate.py -- role-budget reallocation hypotheses.
#
# Each recipe is a reallocation: shift N slots from one role to another, then
# let tools/generate.py pick which specific cards to cut and add. Cuts come
# from the weakest cards in the over-served role; adds come from the best
# unused cards in the under-served one.
#
# Deltas for each recipe must sum to zero so the deck stays at 100 cards.
#
# python3 tools/generate.py --deck decks/reaper-king-no-more --list
# python3 tools/generate.py --deck decks/reaper-king-no-more
# python3 tools/generate.py --deck decks/reaper-king-no-more --only aggro-hate
recipes:
aggro-hate:
why: >
15 of 17 losses were life hitting 0 against aggro. Trade combo
redundancy for sweepers and spot removal.
deltas:
removal_mass: 3
removal_spot: 2
minus_engine: -3
sac_outlet: -2
card-engine:
why: >
Only 1 real draw engine in 99 cards. An aristocrats deck that draws
off its own deaths should never run out of gas.
deltas:
draw: 5
death_trigger: 2
minus_engine: -4
ramp: -3
opponent-facing:
why: >
The commander only steals from OPPONENTS' creatures. Maximise cards
that trigger on their deaths and put counters on their board.
deltas:
opp_death: 5
removal_spot: 2
sac_outlet: -3
ramp: -2
minus_engine: -2
go-wide:
why: >
Token swarms feed sac outlets and the -1/-1 engine simultaneously.
Tests whether bodies beat individually stronger cards.
deltas:
tokens: 5
sac_payoff: 2
minus_engine: -3
removal_spot: -2
ramp: -2
proliferate:
why: >
Proliferate multiplies -1/-1 counters, turning one trigger into many.
A different engine for the same theme.
deltas:
proliferate: 5
minus_engine: 1
ramp: -3
sac_outlet: -3
grindy:
why: >
Recursion + drain: never combo, just refuse to lose and bleed the
table out. Tests whether this deck wants to be a value pile.
deltas:
reanimate: 3
drain: 3
draw: 2
minus_engine: -4
sac_outlet: -2
ramp: -2
# Hand-curated variants for tools/make_variants.py -- explicit cut/add lists
# against the baseline, rather than a role-budget reallocation. Each is a
# specific hypothesis about specific cards (see plan.md section 6b/6c for
# how these were chosen).
#
# python3 tools/make_variants.py --deck decks/reaper-king-no-more --list
# python3 tools/make_variants.py --deck decks/reaper-king-no-more
handwritten:
no-mikaeus:
why: "Baseline. Aristocrats + -1/-1, every combo needs board state."
cut: []
add: []
mikaeus:
why: >
Does the highest-leverage combo card (28 combos) stay legal?
Expected FAIL: goes infinite with every sac outlet.
cut: ["Humble Defector"]
add: ["Mikaeus, the Unhallowed"]
lean-outlets:
why: >
The deck runs 8 sac outlets and probably needs ~5. Cut 3 for
interaction and card advantage -- does consistency actually drop?
cut: ["Woe Strider", "Yahenni, Undying Partisan", "Greater Good"]
add: ["Bloodchief Ascension", "Toshiro Umezawa", "Fiery Cannonade"]
commander-focus:
why: >
The commander only steals from OPPONENTS' creatures, but the deck's
-1/-1 engine mostly counters our own. Push counters onto their board.
cut: ["Putrid Goblin", "Cauldron of Souls", "Murderous Redcap", "Woe Strider"]
add: ["Mari, the Killing Quill", "Sangromancer", "Patron of the Vein",
"Slaughter Specialist"]
heavy-removal:
why: >
Bracket 3 pods interact. Trade combo redundancy for removal and see
whether surviving longer beats assembling faster.
cut: ["Flourishing Defenses", "Nest of Scarabs", "Obelisk Spider",
"Persistent Constrictor"]
add: ["Fiery Cannonade", "Vraska, the Silencer", "Meathook Massacre II",
"Go for the Throat"]
fast-ramp:
why: >
6 of 10 ramp pieces cost 3+, and the deck already has 22 three-drops.
T3 ramp means NOT deploying an engine piece. Swap the 3-mana fetch
spells for 1-mana dorks -- which are also creatures, so they feed the
sac outlets and the -1/-1 engine instead of being do-nothing sorceries.
cut: ["Cultivate", "Kodama's Reach", "Springbloom Druid", "Wood Elves",
"Binding the Old Gods"]
add: ["Llanowar Elves", "Elvish Mystic", "Fyndhorn Elves",
"Ignoble Hierarch", "Delighted Halfling"]
low-curve:
why: >
Avg MV is 3.02 and the engine lands ~T6. Cut the top of the curve
entirely -- does the deck come online sooner and win more?
cut: ["Massacre Wurm", "Overseer of the Damned", "Archfiend of Ifnir",
"Grim Poppet", "Cauldron of Souls"]
add: ["Malakir Cullblade", "Slaughter Specialist", "Bloodchief Ascension",
"Nurgle's Rot", "Chevill, Bane of Monsters"]