mtg/decks/reaper-king-no-more/sim_substitutions.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

42 lines
2.4 KiB
YAML

# Simulation-only card substitutions.
#
# Some cards are `AI:RemoveDeck:All` in Forge because the AI genuinely cannot
# evaluate them -- not because nobody wrote logic. Forcing those on makes the AI
# play them badly, which is worse than not playing them. Instead we swap in a
# functionally similar card the AI CAN pilot, so the deck's SHAPE is simulated
# correctly (the removal slot still does removal) even though the specific card
# differs.
#
# decklist.txt remains the source of truth. This map applies ONLY when building
# the .dck for simulation, and every substitution is recorded in the run's JSON
# metadata and printed in reports.
#
# IMPORTANT: per-card statistics for a substituted card describe the STAND-IN,
# not the real card. Never read "Terminate had a 40% win rate" as a fact about
# Beast Within. This trades per-card precision for whole-deck realism.
substitutions:
# Blocked because it gives the opponent a 3/3 -- the AI can't weigh that downside,
# even though it already has working targeting logic (AITgts$ Card.cmcGE4).
# Feed the Swarm is the same slot: cheap unconditional removal, and it also hits
# enchantments, so it is closer to Beast Within's flexibility than Terminate.
# DEVIATION: Beast Within hits ANY permanent at instant speed; Feed the Swarm is
# a sorcery limited to creatures/enchantments and costs life.
Beast Within: Feed the Swarm
# Blocked because of `Cost$ 2 B PayLife<X>` -- an X-cost paid with LIFE, which
# the AI has no logic to size. Blasphemous Edict is a fixed-cost mass sacrifice.
# DEVIATION: Deluge is scalable, symmetric and dodges indestructible; the Edict
# is an edict effect that opponents choose for, so it kills different creatures.
Toxic Deluge: Blasphemous Edict
# Blocked because "draw X, then discard 3" is a symmetrical drawback it can't
# evaluate. Umbral Collar Zealot is a repeatable sac-for-value outlet the AI can
# pilot, keeping the "sacrifice converts to cards" slot filled.
# DEVIATION: far smaller effect, and a creature rather than an enchantment.
Greater Good: Umbral Collar Zealot
# Black Sun's Zenith and Cauldron of Souls are also AI-blocked but are NOT
# substituted: no close AI-playable analogue exists, and swapping them would
# distort the -1/-1 theme more than leaving the slot inert. They stay in the
# deck, remain uncast, and are reported as "never cast (AI-blocked)".