diff --git a/crates/sage-engine/AGENTS.md b/crates/sage-engine/AGENTS.md index 33a96f12..06941c21 100644 --- a/crates/sage-engine/AGENTS.md +++ b/crates/sage-engine/AGENTS.md @@ -19,6 +19,13 @@ a functional definition per card under a stable `functional_id`, no presentation assets (the schema rejects them structurally), and code-defined behavior only via the declared `scripted` escape hatch. +- **A definition's printed characteristics are checked against the printed card, not + against the tests written over it.** `build.rs` validates *shape*; the accuracy gate is + `tests/printed_characteristics.rs`, reading a fixture transcribed from the set by + `scripts/printed-characteristics.py`. That script never reads `data/catalog/`, and it + must not: a fixture regenerated from the thing it checks is a gate that passes forever + and proves nothing. Rules text stays out of it — the project ships none, and a test + fixture is not an exception. - **Never write a `CardId` down.** `build.rs` assembles `data/catalog/` + `data/sets/` at compile time and interns `CardId(0..n)` from the sorted `FunctionalId`s (ADR 0008 §3), so authoring one card renumbers its neighbours. Name a card by its diff --git a/crates/sage-engine/tests/fixtures/printed_characteristics.json b/crates/sage-engine/tests/fixtures/printed_characteristics.json new file mode 100644 index 00000000..5ba4a774 --- /dev/null +++ b/crates/sage-engine/tests/fixtures/printed_characteristics.json @@ -0,0 +1,4336 @@ +[ + { + "functional_id": "abnormal_endurance", + "name": "Abnormal Endurance", + "mana_cost": "{1}{B}", + "types": [ + "instant" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "act_of_treason", + "name": "Act of Treason", + "mana_cost": "{2}{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "aegis_of_the_heavens", + "name": "Aegis of the Heavens", + "mana_cost": "{1}{W}", + "types": [ + "instant" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "aerial_engineer", + "name": "Aerial Engineer", + "mana_cost": "{2}{W}{U}", + "types": [ + "creature" + ], + "colors": [ + "white", + "blue" + ], + "subtypes": [ + "Human", + "Artificer" + ], + "power": "2", + "toughness": "4" + }, + { + "functional_id": "aether_tunnel", + "name": "Aether Tunnel", + "mana_cost": "{1}{U}", + "types": [ + "enchantment" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "aethershield_artificer", + "name": "Aethershield Artificer", + "mana_cost": "{3}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Dwarf", + "Artificer" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "aggressive_mammoth", + "name": "Aggressive Mammoth", + "mana_cost": "{3}{G}{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elephant" + ], + "power": "8", + "toughness": "8" + }, + { + "functional_id": "air_elemental", + "name": "Air Elemental", + "mana_cost": "{3}{U}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Elemental" + ], + "power": "4", + "toughness": "4" + }, + { + "functional_id": "ajani_adversary_of_tyrants", + "name": "Ajani, Adversary of Tyrants", + "mana_cost": "{2}{W}{W}", + "types": [ + "planeswalker" + ], + "colors": [ + "white" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Ajani" + ], + "loyalty": "4" + }, + { + "functional_id": "ajani_s_influence", + "name": "Ajani's Influence", + "mana_cost": "{2}{W}{W}", + "types": [ + "sorcery" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "ajani_s_last_stand", + "name": "Ajani's Last Stand", + "mana_cost": "{2}{W}{W}", + "types": [ + "enchantment" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "ajani_s_pridemate", + "name": "Ajani's Pridemate", + "mana_cost": "{1}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Cat", + "Soldier" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "ajani_s_welcome", + "name": "Ajani's Welcome", + "mana_cost": "{W}", + "types": [ + "enchantment" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "ajani_wise_counselor", + "name": "Ajani, Wise Counselor", + "mana_cost": "{3}{W}{W}", + "types": [ + "planeswalker" + ], + "colors": [ + "white" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Ajani" + ], + "loyalty": "5" + }, + { + "functional_id": "alpine_moon", + "name": "Alpine Moon", + "mana_cost": "{R}", + "types": [ + "enchantment" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "amulet_of_safekeeping", + "name": "Amulet of Safekeeping", + "mana_cost": "{2}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "angel_of_the_dawn", + "name": "Angel of the Dawn", + "mana_cost": "{4}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Angel" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "anticipate", + "name": "Anticipate", + "mana_cost": "{1}{U}", + "types": [ + "instant" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "apex_of_power", + "name": "Apex of Power", + "mana_cost": "{7}{R}{R}{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "arcades_the_strategist", + "name": "Arcades, the Strategist", + "mana_cost": "{1}{G}{W}{U}", + "types": [ + "creature" + ], + "colors": [ + "white", + "blue", + "green" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Elder", + "Dragon" + ], + "power": "3", + "toughness": "5" + }, + { + "functional_id": "arcane_encyclopedia", + "name": "Arcane Encyclopedia", + "mana_cost": "{3}", + "types": [ + "artifact" + ], + "colors": [], + "subtypes": [ + "Book" + ] + }, + { + "functional_id": "arisen_gorgon", + "name": "Arisen Gorgon", + "mana_cost": "{1}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie", + "Gorgon" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "aven_wind_mage", + "name": "Aven Wind Mage", + "mana_cost": "{2}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Bird", + "Wizard" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "aviation_pioneer", + "name": "Aviation Pioneer", + "mana_cost": "{2}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Human", + "Artificer" + ], + "power": "1", + "toughness": "2" + }, + { + "functional_id": "banefire", + "name": "Banefire", + "mana_cost": "{X}{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "befuddle", + "name": "Befuddle", + "mana_cost": "{2}{U}", + "types": [ + "instant" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "blanchwood_armor", + "name": "Blanchwood Armor", + "mana_cost": "{2}{G}", + "types": [ + "enchantment" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "blood_divination", + "name": "Blood Divination", + "mana_cost": "{3}{B}", + "types": [ + "sorcery" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "boggart_brute", + "name": "Boggart Brute", + "mana_cost": "{2}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Goblin", + "Warrior" + ], + "power": "3", + "toughness": "2" + }, + { + "functional_id": "bogstomper", + "name": "Bogstomper", + "mana_cost": "{4}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Beast" + ], + "power": "6", + "toughness": "5" + }, + { + "functional_id": "bone_dragon", + "name": "Bone Dragon", + "mana_cost": "{3}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Dragon", + "Skeleton" + ], + "power": "5", + "toughness": "4" + }, + { + "functional_id": "bone_to_ash", + "name": "Bone to Ash", + "mana_cost": "{2}{U}{U}", + "types": [ + "instant" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "brawl_bash_ogre", + "name": "Brawl-Bash Ogre", + "mana_cost": "{2}{B}{R}", + "types": [ + "creature" + ], + "colors": [ + "black", + "red" + ], + "subtypes": [ + "Ogre", + "Warrior" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "bristling_boar", + "name": "Bristling Boar", + "mana_cost": "{3}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Boar" + ], + "power": "4", + "toughness": "3" + }, + { + "functional_id": "cancel", + "name": "Cancel", + "mana_cost": "{1}{U}{U}", + "types": [ + "instant" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "catalyst_elemental", + "name": "Catalyst Elemental", + "mana_cost": "{2}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Elemental" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "cavalry_drillmaster", + "name": "Cavalry Drillmaster", + "mana_cost": "{1}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Knight" + ], + "power": "2", + "toughness": "1" + }, + { + "functional_id": "centaur_courser", + "name": "Centaur Courser", + "mana_cost": "{2}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Centaur", + "Warrior" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "chaos_wand", + "name": "Chaos Wand", + "mana_cost": "{3}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "child_of_night", + "name": "Child of Night", + "mana_cost": "{1}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Vampire" + ], + "power": "2", + "toughness": "1" + }, + { + "functional_id": "chromium_the_mutable", + "name": "Chromium, the Mutable", + "mana_cost": "{4}{W}{U}{B}", + "types": [ + "creature" + ], + "colors": [ + "white", + "blue", + "black" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Elder", + "Dragon" + ], + "power": "7", + "toughness": "7" + }, + { + "functional_id": "cinder_barrens", + "name": "Cinder Barrens", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "cleansing_nova", + "name": "Cleansing Nova", + "mana_cost": "{3}{W}{W}", + "types": [ + "sorcery" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "colossal_dreadmaw", + "name": "Colossal Dreadmaw", + "mana_cost": "{4}{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Dinosaur" + ], + "power": "6", + "toughness": "6" + }, + { + "functional_id": "colossal_majesty", + "name": "Colossal Majesty", + "mana_cost": "{2}{G}", + "types": [ + "enchantment" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "court_cleric", + "name": "Court Cleric", + "mana_cost": "{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Cleric" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "crash_through", + "name": "Crash Through", + "mana_cost": "{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "crucible_of_worlds", + "name": "Crucible of Worlds", + "mana_cost": "{3}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "daggerback_basilisk", + "name": "Daggerback Basilisk", + "mana_cost": "{2}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Basilisk" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "dark_dweller_oracle", + "name": "Dark-Dweller Oracle", + "mana_cost": "{1}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Goblin", + "Shaman" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "daybreak_chaplain", + "name": "Daybreak Chaplain", + "mana_cost": "{1}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Cleric" + ], + "power": "1", + "toughness": "3" + }, + { + "functional_id": "death_baron", + "name": "Death Baron", + "mana_cost": "{1}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie", + "Wizard" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "declare_dominance", + "name": "Declare Dominance", + "mana_cost": "{3}{G}{G}", + "types": [ + "sorcery" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "demanding_dragon", + "name": "Demanding Dragon", + "mana_cost": "{3}{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Dragon" + ], + "power": "5", + "toughness": "5" + }, + { + "functional_id": "demon_of_catastrophes", + "name": "Demon of Catastrophes", + "mana_cost": "{2}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Demon" + ], + "power": "6", + "toughness": "6" + }, + { + "functional_id": "departed_deckhand", + "name": "Departed Deckhand", + "mana_cost": "{1}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Spirit", + "Pirate" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "desecrated_tomb", + "name": "Desecrated Tomb", + "mana_cost": "{3}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "detection_tower", + "name": "Detection Tower", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "diamond_mare", + "name": "Diamond Mare", + "mana_cost": "{2}", + "types": [ + "artifact", + "creature" + ], + "colors": [], + "subtypes": [ + "Horse" + ], + "power": "1", + "toughness": "3" + }, + { + "functional_id": "diregraf_ghoul", + "name": "Diregraf Ghoul", + "mana_cost": "{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "dismissive_pyromancer", + "name": "Dismissive Pyromancer", + "mana_cost": "{1}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Human", + "Wizard" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "disperse", + "name": "Disperse", + "mana_cost": "{1}{U}", + "types": [ + "instant" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "divination", + "name": "Divination", + "mana_cost": "{2}{U}", + "types": [ + "sorcery" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "djinn_of_wishes", + "name": "Djinn of Wishes", + "mana_cost": "{3}{U}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Djinn" + ], + "power": "4", + "toughness": "4" + }, + { + "functional_id": "doomed_dissenter", + "name": "Doomed Dissenter", + "mana_cost": "{1}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Human" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "doublecast", + "name": "Doublecast", + "mana_cost": "{R}{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "draconic_disciple", + "name": "Draconic Disciple", + "mana_cost": "{1}{R}{G}", + "types": [ + "creature" + ], + "colors": [ + "red", + "green" + ], + "subtypes": [ + "Human", + "Shaman" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "dragon_egg", + "name": "Dragon Egg", + "mana_cost": "{2}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Dragon", + "Egg" + ], + "power": "0", + "toughness": "2" + }, + { + "functional_id": "dragon_s_hoard", + "name": "Dragon's Hoard", + "mana_cost": "{3}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "druid_of_horns", + "name": "Druid of Horns", + "mana_cost": "{3}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Human", + "Druid" + ], + "power": "2", + "toughness": "3" + }, + { + "functional_id": "druid_of_the_cowl", + "name": "Druid of the Cowl", + "mana_cost": "{1}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elf", + "Druid" + ], + "power": "1", + "toughness": "3" + }, + { + "functional_id": "dryad_greenseeker", + "name": "Dryad Greenseeker", + "mana_cost": "{1}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Dryad" + ], + "power": "1", + "toughness": "3" + }, + { + "functional_id": "duress", + "name": "Duress", + "mana_cost": "{B}", + "types": [ + "sorcery" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "dwarven_priest", + "name": "Dwarven Priest", + "mana_cost": "{3}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Dwarf", + "Cleric" + ], + "power": "2", + "toughness": "4" + }, + { + "functional_id": "dwindle", + "name": "Dwindle", + "mana_cost": "{2}{U}", + "types": [ + "enchantment" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "electrify", + "name": "Electrify", + "mana_cost": "{3}{R}", + "types": [ + "instant" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "elvish_clancaller", + "name": "Elvish Clancaller", + "mana_cost": "{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elf", + "Druid" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "elvish_rejuvenator", + "name": "Elvish Rejuvenator", + "mana_cost": "{2}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elf", + "Druid" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "enigma_drake", + "name": "Enigma Drake", + "mana_cost": "{1}{U}{R}", + "types": [ + "creature" + ], + "colors": [ + "blue", + "red" + ], + "subtypes": [ + "Drake" + ], + "power": "*", + "toughness": "4" + }, + { + "functional_id": "epicure_of_blood", + "name": "Epicure of Blood", + "mana_cost": "{4}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Vampire" + ], + "power": "4", + "toughness": "4" + }, + { + "functional_id": "essence_scatter", + "name": "Essence Scatter", + "mana_cost": "{1}{U}", + "types": [ + "instant" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "exclusion_mage", + "name": "Exclusion Mage", + "mana_cost": "{2}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Human", + "Wizard" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "explosive_apparatus", + "name": "Explosive Apparatus", + "mana_cost": "{1}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "fell_specter", + "name": "Fell Specter", + "mana_cost": "{3}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Specter" + ], + "power": "1", + "toughness": "3" + }, + { + "functional_id": "field_creeper", + "name": "Field Creeper", + "mana_cost": "{2}", + "types": [ + "artifact", + "creature" + ], + "colors": [], + "subtypes": [ + "Scarecrow" + ], + "power": "2", + "toughness": "1" + }, + { + "functional_id": "fiery_finish", + "name": "Fiery Finish", + "mana_cost": "{4}{R}{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "fire_elemental", + "name": "Fire Elemental", + "mana_cost": "{3}{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Elemental" + ], + "power": "5", + "toughness": "4" + }, + { + "functional_id": "forest", + "name": "Forest", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [], + "supertypes": [ + "basic" + ], + "subtypes": [ + "Forest" + ] + }, + { + "functional_id": "forsaken_sanctuary", + "name": "Forsaken Sanctuary", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "foul_orchard", + "name": "Foul Orchard", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "fountain_of_renewal", + "name": "Fountain of Renewal", + "mana_cost": "{1}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "fraying_omnipotence", + "name": "Fraying Omnipotence", + "mana_cost": "{3}{B}{B}", + "types": [ + "sorcery" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "frilled_sea_serpent", + "name": "Frilled Sea Serpent", + "mana_cost": "{4}{U}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Serpent" + ], + "power": "4", + "toughness": "6" + }, + { + "functional_id": "gallant_cavalry", + "name": "Gallant Cavalry", + "mana_cost": "{3}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Knight" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "gargoyle_sentinel", + "name": "Gargoyle Sentinel", + "mana_cost": "{3}", + "types": [ + "artifact", + "creature" + ], + "colors": [], + "subtypes": [ + "Gargoyle" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "gearsmith_guardian", + "name": "Gearsmith Guardian", + "mana_cost": "{5}", + "types": [ + "artifact", + "creature" + ], + "colors": [], + "subtypes": [ + "Construct" + ], + "power": "3", + "toughness": "5" + }, + { + "functional_id": "gearsmith_prodigy", + "name": "Gearsmith Prodigy", + "mana_cost": "{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Human", + "Artificer" + ], + "power": "1", + "toughness": "2" + }, + { + "functional_id": "ghastbark_twins", + "name": "Ghastbark Twins", + "mana_cost": "{5}{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Treefolk" + ], + "power": "7", + "toughness": "7" + }, + { + "functional_id": "ghirapur_guide", + "name": "Ghirapur Guide", + "mana_cost": "{2}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elf", + "Scout" + ], + "power": "3", + "toughness": "2" + }, + { + "functional_id": "ghostform", + "name": "Ghostform", + "mana_cost": "{1}{U}", + "types": [ + "sorcery" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "giant_spider", + "name": "Giant Spider", + "mana_cost": "{3}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Spider" + ], + "power": "2", + "toughness": "4" + }, + { + "functional_id": "gift_of_paradise", + "name": "Gift of Paradise", + "mana_cost": "{2}{G}", + "types": [ + "enchantment" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "gigantosaurus", + "name": "Gigantosaurus", + "mana_cost": "{G}{G}{G}{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Dinosaur" + ], + "power": "10", + "toughness": "10" + }, + { + "functional_id": "goblin_instigator", + "name": "Goblin Instigator", + "mana_cost": "{1}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Goblin", + "Rogue" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "goblin_motivator", + "name": "Goblin Motivator", + "mana_cost": "{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Goblin", + "Warrior" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "goblin_trashmaster", + "name": "Goblin Trashmaster", + "mana_cost": "{2}{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Goblin", + "Warrior" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "goreclaw_terror_of_qal_sisma", + "name": "Goreclaw, Terror of Qal Sisma", + "mana_cost": "{3}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Bear" + ], + "power": "4", + "toughness": "3" + }, + { + "functional_id": "grasping_scoundrel", + "name": "Grasping Scoundrel", + "mana_cost": "{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Human", + "Pirate" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "gravedigger", + "name": "Gravedigger", + "mana_cost": "{3}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "gravewaker", + "name": "Gravewaker", + "mana_cost": "{4}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Bird", + "Spirit" + ], + "power": "5", + "toughness": "5" + }, + { + "functional_id": "graveyard_marshal", + "name": "Graveyard Marshal", + "mana_cost": "{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie", + "Soldier" + ], + "power": "3", + "toughness": "2" + }, + { + "functional_id": "greenwood_sentinel", + "name": "Greenwood Sentinel", + "mana_cost": "{1}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elf", + "Scout" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "guttersnipe", + "name": "Guttersnipe", + "mana_cost": "{2}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Goblin", + "Shaman" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "havoc_devils", + "name": "Havoc Devils", + "mana_cost": "{2}{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Devil" + ], + "power": "4", + "toughness": "3" + }, + { + "functional_id": "herald_of_faith", + "name": "Herald of Faith", + "mana_cost": "{3}{W}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Angel" + ], + "power": "4", + "toughness": "3" + }, + { + "functional_id": "heroic_reinforcements", + "name": "Heroic Reinforcements", + "mana_cost": "{2}{R}{W}", + "types": [ + "sorcery" + ], + "colors": [ + "white", + "red" + ] + }, + { + "functional_id": "hieromancer_s_cage", + "name": "Hieromancer's Cage", + "mana_cost": "{3}{W}", + "types": [ + "enchantment" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "highland_game", + "name": "Highland Game", + "mana_cost": "{1}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elk" + ], + "power": "2", + "toughness": "1" + }, + { + "functional_id": "highland_lake", + "name": "Highland Lake", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "hired_blade", + "name": "Hired Blade", + "mana_cost": "{2}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Human", + "Assassin" + ], + "power": "3", + "toughness": "2" + }, + { + "functional_id": "horizon_scholar", + "name": "Horizon Scholar", + "mana_cost": "{5}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Sphinx" + ], + "power": "4", + "toughness": "4" + }, + { + "functional_id": "hostile_minotaur", + "name": "Hostile Minotaur", + "mana_cost": "{3}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Minotaur" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "hungering_hydra", + "name": "Hungering Hydra", + "mana_cost": "{X}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Hydra" + ], + "power": "0", + "toughness": "0" + }, + { + "functional_id": "infectious_horror", + "name": "Infectious Horror", + "mana_cost": "{3}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie", + "Horror" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "infernal_reckoning", + "name": "Infernal Reckoning", + "mana_cost": "{B}", + "types": [ + "instant" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "infernal_scarring", + "name": "Infernal Scarring", + "mana_cost": "{1}{B}", + "types": [ + "enchantment" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "inferno_hellion", + "name": "Inferno Hellion", + "mana_cost": "{3}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Hellion" + ], + "power": "7", + "toughness": "3" + }, + { + "functional_id": "inspired_charge", + "name": "Inspired Charge", + "mana_cost": "{2}{W}{W}", + "types": [ + "instant" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "invoke_the_divine", + "name": "Invoke the Divine", + "mana_cost": "{2}{W}", + "types": [ + "instant" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "isareth_the_awakener", + "name": "Isareth the Awakener", + "mana_cost": "{1}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Human", + "Wizard" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "island", + "name": "Island", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [], + "supertypes": [ + "basic" + ], + "subtypes": [ + "Island" + ] + }, + { + "functional_id": "isolate", + "name": "Isolate", + "mana_cost": "{W}", + "types": [ + "instant" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "kargan_dragonrider", + "name": "Kargan Dragonrider", + "mana_cost": "{1}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Human", + "Warrior" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "knight_of_the_tusk", + "name": "Knight of the Tusk", + "mana_cost": "{4}{W}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Knight" + ], + "power": "3", + "toughness": "7" + }, + { + "functional_id": "knight_s_pledge", + "name": "Knight's Pledge", + "mana_cost": "{1}{W}", + "types": [ + "enchantment" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "knightly_valor", + "name": "Knightly Valor", + "mana_cost": "{4}{W}", + "types": [ + "enchantment" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "lathliss_dragon_queen", + "name": "Lathliss, Dragon Queen", + "mana_cost": "{4}{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Dragon" + ], + "power": "6", + "toughness": "6" + }, + { + "functional_id": "lava_axe", + "name": "Lava Axe", + "mana_cost": "{4}{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "lena_selfless_champion", + "name": "Lena, Selfless Champion", + "mana_cost": "{4}{W}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Human", + "Knight" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "leonin_vanguard", + "name": "Leonin Vanguard", + "mana_cost": "{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Cat", + "Soldier" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "leonin_warleader", + "name": "Leonin Warleader", + "mana_cost": "{2}{W}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Cat", + "Soldier" + ], + "power": "4", + "toughness": "4" + }, + { + "functional_id": "lich_s_caress", + "name": "Lich's Caress", + "mana_cost": "{3}{B}{B}", + "types": [ + "sorcery" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "lightning_mare", + "name": "Lightning Mare", + "mana_cost": "{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Elemental", + "Horse" + ], + "power": "3", + "toughness": "1" + }, + { + "functional_id": "lightning_strike", + "name": "Lightning Strike", + "mana_cost": "{1}{R}", + "types": [ + "instant" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "liliana_s_contract", + "name": "Liliana's Contract", + "mana_cost": "{3}{B}{B}", + "types": [ + "enchantment" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "liliana_s_spoils", + "name": "Liliana's Spoils", + "mana_cost": "{3}{B}", + "types": [ + "sorcery" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "liliana_the_necromancer", + "name": "Liliana, the Necromancer", + "mana_cost": "{3}{B}{B}", + "types": [ + "planeswalker" + ], + "colors": [ + "black" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Liliana" + ], + "loyalty": "4" + }, + { + "functional_id": "liliana_untouched_by_death", + "name": "Liliana, Untouched by Death", + "mana_cost": "{2}{B}{B}", + "types": [ + "planeswalker" + ], + "colors": [ + "black" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Liliana" + ], + "loyalty": "4" + }, + { + "functional_id": "llanowar_elves", + "name": "Llanowar Elves", + "mana_cost": "{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elf", + "Druid" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "loxodon_line_breaker", + "name": "Loxodon Line Breaker", + "mana_cost": "{2}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Elephant", + "Soldier" + ], + "power": "3", + "toughness": "2" + }, + { + "functional_id": "luminous_bonds", + "name": "Luminous Bonds", + "mana_cost": "{2}{W}", + "types": [ + "enchantment" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "macabre_waltz", + "name": "Macabre Waltz", + "mana_cost": "{1}{B}", + "types": [ + "sorcery" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "magistrate_s_scepter", + "name": "Magistrate's Scepter", + "mana_cost": "{3}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "make_a_stand", + "name": "Make a Stand", + "mana_cost": "{2}{W}", + "types": [ + "instant" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "manalith", + "name": "Manalith", + "mana_cost": "{3}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "marauder_s_axe", + "name": "Marauder's Axe", + "mana_cost": "{2}", + "types": [ + "artifact" + ], + "colors": [], + "subtypes": [ + "Equipment" + ] + }, + { + "functional_id": "meandering_river", + "name": "Meandering River", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "mentor_of_the_meek", + "name": "Mentor of the Meek", + "mana_cost": "{2}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Soldier" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "metamorphic_alteration", + "name": "Metamorphic Alteration", + "mana_cost": "{1}{U}", + "types": [ + "enchantment" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "meteor_golem", + "name": "Meteor Golem", + "mana_cost": "{7}", + "types": [ + "artifact", + "creature" + ], + "colors": [], + "subtypes": [ + "Golem" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "mighty_leap", + "name": "Mighty Leap", + "mana_cost": "{1}{W}", + "types": [ + "instant" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "militia_bugler", + "name": "Militia Bugler", + "mana_cost": "{2}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Soldier" + ], + "power": "2", + "toughness": "3" + }, + { + "functional_id": "millstone", + "name": "Millstone", + "mana_cost": "{2}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "mind_rot", + "name": "Mind Rot", + "mana_cost": "{2}{B}", + "types": [ + "sorcery" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "mirror_image", + "name": "Mirror Image", + "mana_cost": "{2}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Shapeshifter" + ], + "power": "0", + "toughness": "0" + }, + { + "functional_id": "mist_cloaked_herald", + "name": "Mist-Cloaked Herald", + "mana_cost": "{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Merfolk", + "Warrior" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "mistcaller", + "name": "Mistcaller", + "mana_cost": "{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Merfolk", + "Wizard" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "mountain", + "name": "Mountain", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [], + "supertypes": [ + "basic" + ], + "subtypes": [ + "Mountain" + ] + }, + { + "functional_id": "murder", + "name": "Murder", + "mana_cost": "{1}{B}{B}", + "types": [ + "instant" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "mystic_archaeologist", + "name": "Mystic Archaeologist", + "mana_cost": "{1}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Human", + "Wizard" + ], + "power": "2", + "toughness": "1" + }, + { + "functional_id": "naturalize", + "name": "Naturalize", + "mana_cost": "{1}{G}", + "types": [ + "instant" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "nexus_of_fate", + "name": "Nexus of Fate", + "mana_cost": "{5}{U}{U}", + "types": [ + "instant" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "nicol_bolas_the_ravager", + "name": "Nicol Bolas, the Ravager", + "mana_cost": "{1}{U}{B}{R}", + "types": [ + "creature" + ], + "colors": [ + "blue", + "black", + "red" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Elder", + "Dragon" + ], + "power": "4", + "toughness": "4", + "back_face": { + "name": "Nicol Bolas, the Arisen", + "mana_cost": "", + "types": [ + "planeswalker" + ], + "colors": [ + "blue", + "black", + "red" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Bolas" + ], + "loyalty": "7" + } + }, + { + "functional_id": "nightmare_s_thirst", + "name": "Nightmare's Thirst", + "mana_cost": "{B}", + "types": [ + "instant" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "novice_knight", + "name": "Novice Knight", + "mana_cost": "{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Knight" + ], + "power": "2", + "toughness": "3" + }, + { + "functional_id": "oakenform", + "name": "Oakenform", + "mana_cost": "{2}{G}", + "types": [ + "enchantment" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "omenspeaker", + "name": "Omenspeaker", + "mana_cost": "{1}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Human", + "Wizard" + ], + "power": "1", + "toughness": "3" + }, + { + "functional_id": "omniscience", + "name": "Omniscience", + "mana_cost": "{7}{U}{U}{U}", + "types": [ + "enchantment" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "onakke_ogre", + "name": "Onakke Ogre", + "mana_cost": "{2}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Ogre", + "Warrior" + ], + "power": "4", + "toughness": "2" + }, + { + "functional_id": "one_with_the_machine", + "name": "One with the Machine", + "mana_cost": "{3}{U}", + "types": [ + "sorcery" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "open_the_graves", + "name": "Open the Graves", + "mana_cost": "{3}{B}{B}", + "types": [ + "enchantment" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "oreskos_swiftclaw", + "name": "Oreskos Swiftclaw", + "mana_cost": "{1}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Cat", + "Warrior" + ], + "power": "3", + "toughness": "1" + }, + { + "functional_id": "palladia_mors_the_ruiner", + "name": "Palladia-Mors, the Ruiner", + "mana_cost": "{3}{R}{G}{W}", + "types": [ + "creature" + ], + "colors": [ + "white", + "red", + "green" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Elder", + "Dragon" + ], + "power": "6", + "toughness": "6" + }, + { + "functional_id": "patient_rebuilding", + "name": "Patient Rebuilding", + "mana_cost": "{3}{U}{U}", + "types": [ + "enchantment" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "pegasus_courser", + "name": "Pegasus Courser", + "mana_cost": "{2}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Pegasus" + ], + "power": "1", + "toughness": "3" + }, + { + "functional_id": "pelakka_wurm", + "name": "Pelakka Wurm", + "mana_cost": "{4}{G}{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Wurm" + ], + "power": "7", + "toughness": "7" + }, + { + "functional_id": "pendulum_of_patterns", + "name": "Pendulum of Patterns", + "mana_cost": "{2}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "phylactery_lich", + "name": "Phylactery Lich", + "mana_cost": "{B}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie" + ], + "power": "5", + "toughness": "5" + }, + { + "functional_id": "plague_mare", + "name": "Plague Mare", + "mana_cost": "{1}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Nightmare", + "Horse" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "plains", + "name": "Plains", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [], + "supertypes": [ + "basic" + ], + "subtypes": [ + "Plains" + ] + }, + { + "functional_id": "plummet", + "name": "Plummet", + "mana_cost": "{1}{G}", + "types": [ + "instant" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "poison_tip_archer", + "name": "Poison-Tip Archer", + "mana_cost": "{2}{B}{G}", + "types": [ + "creature" + ], + "colors": [ + "black", + "green" + ], + "subtypes": [ + "Elf", + "Archer" + ], + "power": "2", + "toughness": "3" + }, + { + "functional_id": "prodigious_growth", + "name": "Prodigious Growth", + "mana_cost": "{4}{G}{G}", + "types": [ + "enchantment" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "psychic_corrosion", + "name": "Psychic Corrosion", + "mana_cost": "{2}{U}", + "types": [ + "enchantment" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "psychic_symbiont", + "name": "Psychic Symbiont", + "mana_cost": "{4}{U}{B}", + "types": [ + "creature" + ], + "colors": [ + "blue", + "black" + ], + "subtypes": [ + "Nightmare", + "Horror" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "rabid_bite", + "name": "Rabid Bite", + "mana_cost": "{1}{G}", + "types": [ + "sorcery" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "radiating_lightning", + "name": "Radiating Lightning", + "mana_cost": "{3}{R}", + "types": [ + "instant" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "ravenous_harpy", + "name": "Ravenous Harpy", + "mana_cost": "{2}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Harpy" + ], + "power": "1", + "toughness": "2" + }, + { + "functional_id": "reassembling_skeleton", + "name": "Reassembling Skeleton", + "mana_cost": "{1}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Skeleton", + "Warrior" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "reclamation_sage", + "name": "Reclamation Sage", + "mana_cost": "{2}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elf", + "Shaman" + ], + "power": "2", + "toughness": "1" + }, + { + "functional_id": "recollect", + "name": "Recollect", + "mana_cost": "{2}{G}", + "types": [ + "sorcery" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "regal_bloodlord", + "name": "Regal Bloodlord", + "mana_cost": "{3}{W}{B}", + "types": [ + "creature" + ], + "colors": [ + "white", + "black" + ], + "subtypes": [ + "Vampire", + "Soldier" + ], + "power": "2", + "toughness": "4" + }, + { + "functional_id": "reliquary_tower", + "name": "Reliquary Tower", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "remorseful_cleric", + "name": "Remorseful Cleric", + "mana_cost": "{1}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Spirit", + "Cleric" + ], + "power": "2", + "toughness": "1" + }, + { + "functional_id": "resplendent_angel", + "name": "Resplendent Angel", + "mana_cost": "{1}{W}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Angel" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "revitalize", + "name": "Revitalize", + "mana_cost": "{1}{W}", + "types": [ + "instant" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "rhox_oracle", + "name": "Rhox Oracle", + "mana_cost": "{4}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Rhino", + "Monk" + ], + "power": "4", + "toughness": "2" + }, + { + "functional_id": "riddlemaster_sphinx", + "name": "Riddlemaster Sphinx", + "mana_cost": "{4}{U}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Sphinx" + ], + "power": "5", + "toughness": "5" + }, + { + "functional_id": "rise_from_the_grave", + "name": "Rise from the Grave", + "mana_cost": "{4}{B}", + "types": [ + "sorcery" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "rogue_s_gloves", + "name": "Rogue's Gloves", + "mana_cost": "{2}", + "types": [ + "artifact" + ], + "colors": [], + "subtypes": [ + "Equipment" + ] + }, + { + "functional_id": "root_snare", + "name": "Root Snare", + "mana_cost": "{1}{G}", + "types": [ + "instant" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "runic_armasaur", + "name": "Runic Armasaur", + "mana_cost": "{1}{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Dinosaur" + ], + "power": "2", + "toughness": "5" + }, + { + "functional_id": "rupture_spire", + "name": "Rupture Spire", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "rustwing_falcon", + "name": "Rustwing Falcon", + "mana_cost": "{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Bird" + ], + "power": "1", + "toughness": "2" + }, + { + "functional_id": "sai_master_thopterist", + "name": "Sai, Master Thopterist", + "mana_cost": "{2}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Human", + "Artificer" + ], + "power": "1", + "toughness": "4" + }, + { + "functional_id": "salvager_of_secrets", + "name": "Salvager of Secrets", + "mana_cost": "{3}{U}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Merfolk", + "Wizard" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "sarkhan_dragonsoul", + "name": "Sarkhan, Dragonsoul", + "mana_cost": "{4}{R}{R}", + "types": [ + "planeswalker" + ], + "colors": [ + "red" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Sarkhan" + ], + "loyalty": "5" + }, + { + "functional_id": "sarkhan_fireblood", + "name": "Sarkhan, Fireblood", + "mana_cost": "{1}{R}{R}", + "types": [ + "planeswalker" + ], + "colors": [ + "red" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Sarkhan" + ], + "loyalty": "3" + }, + { + "functional_id": "sarkhan_s_dragonfire", + "name": "Sarkhan's Dragonfire", + "mana_cost": "{3}{R}{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "sarkhan_s_unsealing", + "name": "Sarkhan's Unsealing", + "mana_cost": "{3}{R}", + "types": [ + "enchantment" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "sarkhan_s_whelp", + "name": "Sarkhan's Whelp", + "mana_cost": "{2}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Dragon" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "satyr_enchanter", + "name": "Satyr Enchanter", + "mana_cost": "{1}{G}{W}", + "types": [ + "creature" + ], + "colors": [ + "white", + "green" + ], + "subtypes": [ + "Satyr", + "Druid" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "scapeshift", + "name": "Scapeshift", + "mana_cost": "{2}{G}{G}", + "types": [ + "sorcery" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "scholar_of_stars", + "name": "Scholar of Stars", + "mana_cost": "{3}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Human", + "Artificer" + ], + "power": "3", + "toughness": "2" + }, + { + "functional_id": "serra_s_guardian", + "name": "Serra's Guardian", + "mana_cost": "{4}{W}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Angel" + ], + "power": "5", + "toughness": "5" + }, + { + "functional_id": "shield_mare", + "name": "Shield Mare", + "mana_cost": "{1}{W}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Horse" + ], + "power": "2", + "toughness": "3" + }, + { + "functional_id": "shivan_dragon", + "name": "Shivan Dragon", + "mana_cost": "{4}{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Dragon" + ], + "power": "5", + "toughness": "5" + }, + { + "functional_id": "shock", + "name": "Shock", + "mana_cost": "{R}", + "types": [ + "instant" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "siegebreaker_giant", + "name": "Siegebreaker Giant", + "mana_cost": "{3}{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Giant", + "Warrior" + ], + "power": "6", + "toughness": "3" + }, + { + "functional_id": "sift", + "name": "Sift", + "mana_cost": "{3}{U}", + "types": [ + "sorcery" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "sigiled_sword_of_valeron", + "name": "Sigiled Sword of Valeron", + "mana_cost": "{3}", + "types": [ + "artifact" + ], + "colors": [], + "subtypes": [ + "Equipment" + ] + }, + { + "functional_id": "silverbeak_griffin", + "name": "Silverbeak Griffin", + "mana_cost": "{W}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Griffin" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "skalla_wolf", + "name": "Skalla Wolf", + "mana_cost": "{3}{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Wolf", + "Spirit" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "skeleton_archer", + "name": "Skeleton Archer", + "mana_cost": "{3}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Skeleton", + "Archer" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "skilled_animator", + "name": "Skilled Animator", + "mana_cost": "{2}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Human", + "Artificer" + ], + "power": "1", + "toughness": "3" + }, + { + "functional_id": "skymarch_bloodletter", + "name": "Skymarch Bloodletter", + "mana_cost": "{2}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Vampire", + "Soldier" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "skyrider_patrol", + "name": "Skyrider Patrol", + "mana_cost": "{2}{G}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue", + "green" + ], + "subtypes": [ + "Elf", + "Scout" + ], + "power": "2", + "toughness": "3" + }, + { + "functional_id": "skyscanner", + "name": "Skyscanner", + "mana_cost": "{3}", + "types": [ + "artifact", + "creature" + ], + "colors": [], + "subtypes": [ + "Thopter" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "sleep", + "name": "Sleep", + "mana_cost": "{2}{U}{U}", + "types": [ + "sorcery" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "smelt", + "name": "Smelt", + "mana_cost": "{R}", + "types": [ + "instant" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "snapping_drake", + "name": "Snapping Drake", + "mana_cost": "{3}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Drake" + ], + "power": "3", + "toughness": "2" + }, + { + "functional_id": "sovereign_s_bite", + "name": "Sovereign's Bite", + "mana_cost": "{1}{B}", + "types": [ + "sorcery" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "sparktongue_dragon", + "name": "Sparktongue Dragon", + "mana_cost": "{3}{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Dragon" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "spit_flame", + "name": "Spit Flame", + "mana_cost": "{2}{R}", + "types": [ + "instant" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "star_crowned_stag", + "name": "Star-Crowned Stag", + "mana_cost": "{3}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Elk" + ], + "power": "3", + "toughness": "3" + }, + { + "functional_id": "stitcher_s_supplier", + "name": "Stitcher's Supplier", + "mana_cost": "{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie" + ], + "power": "1", + "toughness": "1" + }, + { + "functional_id": "stone_quarry", + "name": "Stone Quarry", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "strangling_spores", + "name": "Strangling Spores", + "mana_cost": "{3}{B}", + "types": [ + "instant" + ], + "colors": [ + "black" + ] + }, + { + "functional_id": "submerged_boneyard", + "name": "Submerged Boneyard", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "sun_sentinel", + "name": "Sun Sentinel", + "mana_cost": "{1}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Soldier" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "suncleanser", + "name": "Suncleanser", + "mana_cost": "{1}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Cleric" + ], + "power": "1", + "toughness": "4" + }, + { + "functional_id": "supreme_phantom", + "name": "Supreme Phantom", + "mana_cost": "{1}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Spirit" + ], + "power": "1", + "toughness": "3" + }, + { + "functional_id": "sure_strike", + "name": "Sure Strike", + "mana_cost": "{1}{R}", + "types": [ + "instant" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "surge_mare", + "name": "Surge Mare", + "mana_cost": "{U}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Horse", + "Fish" + ], + "power": "0", + "toughness": "5" + }, + { + "functional_id": "suspicious_bookcase", + "name": "Suspicious Bookcase", + "mana_cost": "{2}", + "types": [ + "artifact", + "creature" + ], + "colors": [], + "subtypes": [ + "Wall" + ], + "power": "0", + "toughness": "4" + }, + { + "functional_id": "swamp", + "name": "Swamp", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [], + "supertypes": [ + "basic" + ], + "subtypes": [ + "Swamp" + ] + }, + { + "functional_id": "switcheroo", + "name": "Switcheroo", + "mana_cost": "{4}{U}", + "types": [ + "sorcery" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "take_vengeance", + "name": "Take Vengeance", + "mana_cost": "{1}{W}", + "types": [ + "sorcery" + ], + "colors": [ + "white" + ] + }, + { + "functional_id": "talons_of_wildwood", + "name": "Talons of Wildwood", + "mana_cost": "{1}{G}", + "types": [ + "enchantment" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "tattered_mummy", + "name": "Tattered Mummy", + "mana_cost": "{1}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie", + "Jackal" + ], + "power": "1", + "toughness": "2" + }, + { + "functional_id": "tectonic_rift", + "name": "Tectonic Rift", + "mana_cost": "{3}{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "tezzeret_artifice_master", + "name": "Tezzeret, Artifice Master", + "mana_cost": "{3}{U}{U}", + "types": [ + "planeswalker" + ], + "colors": [ + "blue" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Tezzeret" + ], + "loyalty": "5" + }, + { + "functional_id": "tezzeret_cruel_machinist", + "name": "Tezzeret, Cruel Machinist", + "mana_cost": "{4}{U}{U}", + "types": [ + "planeswalker" + ], + "colors": [ + "blue" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Tezzeret" + ], + "loyalty": "4" + }, + { + "functional_id": "tezzeret_s_gatebreaker", + "name": "Tezzeret's Gatebreaker", + "mana_cost": "{4}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "tezzeret_s_strider", + "name": "Tezzeret's Strider", + "mana_cost": "{3}", + "types": [ + "artifact", + "creature" + ], + "colors": [], + "subtypes": [ + "Golem" + ], + "power": "3", + "toughness": "1" + }, + { + "functional_id": "thorn_lieutenant", + "name": "Thorn Lieutenant", + "mana_cost": "{1}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elf", + "Warrior" + ], + "power": "2", + "toughness": "3" + }, + { + "functional_id": "thornhide_wolves", + "name": "Thornhide Wolves", + "mana_cost": "{4}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Wolf" + ], + "power": "4", + "toughness": "5" + }, + { + "functional_id": "thud", + "name": "Thud", + "mana_cost": "{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "timber_gorge", + "name": "Timber Gorge", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "titanic_growth", + "name": "Titanic Growth", + "mana_cost": "{1}{G}", + "types": [ + "instant" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "tolarian_scholar", + "name": "Tolarian Scholar", + "mana_cost": "{2}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Human", + "Wizard" + ], + "power": "2", + "toughness": "3" + }, + { + "functional_id": "tormenting_voice", + "name": "Tormenting Voice", + "mana_cost": "{1}{R}", + "types": [ + "sorcery" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "totally_lost", + "name": "Totally Lost", + "mana_cost": "{4}{U}", + "types": [ + "instant" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "tranquil_expanse", + "name": "Tranquil Expanse", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + }, + { + "functional_id": "transmogrifying_wand", + "name": "Transmogrifying Wand", + "mana_cost": "{3}", + "types": [ + "artifact" + ], + "colors": [] + }, + { + "functional_id": "trumpet_blast", + "name": "Trumpet Blast", + "mana_cost": "{2}{R}", + "types": [ + "instant" + ], + "colors": [ + "red" + ] + }, + { + "functional_id": "trusty_packbeast", + "name": "Trusty Packbeast", + "mana_cost": "{2}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Beast" + ], + "power": "2", + "toughness": "3" + }, + { + "functional_id": "two_headed_zombie", + "name": "Two-Headed Zombie", + "mana_cost": "{3}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie" + ], + "power": "4", + "toughness": "2" + }, + { + "functional_id": "uncomfortable_chill", + "name": "Uncomfortable Chill", + "mana_cost": "{2}{U}", + "types": [ + "instant" + ], + "colors": [ + "blue" + ] + }, + { + "functional_id": "ursine_champion", + "name": "Ursine Champion", + "mana_cost": "{1}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Human", + "Berserker" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "vaevictis_asmadi_the_dire", + "name": "Vaevictis Asmadi, the Dire", + "mana_cost": "{3}{B}{R}{G}", + "types": [ + "creature" + ], + "colors": [ + "black", + "red", + "green" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Elder", + "Dragon" + ], + "power": "6", + "toughness": "6" + }, + { + "functional_id": "valiant_knight", + "name": "Valiant Knight", + "mana_cost": "{3}{W}", + "types": [ + "creature" + ], + "colors": [ + "white" + ], + "subtypes": [ + "Human", + "Knight" + ], + "power": "3", + "toughness": "4" + }, + { + "functional_id": "vampire_neonate", + "name": "Vampire Neonate", + "mana_cost": "{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Vampire" + ], + "power": "0", + "toughness": "3" + }, + { + "functional_id": "vampire_sovereign", + "name": "Vampire Sovereign", + "mana_cost": "{3}{B}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Vampire", + "Noble" + ], + "power": "3", + "toughness": "4" + }, + { + "functional_id": "viashino_pyromancer", + "name": "Viashino Pyromancer", + "mana_cost": "{1}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Lizard", + "Wizard" + ], + "power": "2", + "toughness": "1" + }, + { + "functional_id": "vigilant_baloth", + "name": "Vigilant Baloth", + "mana_cost": "{3}{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Beast" + ], + "power": "5", + "toughness": "5" + }, + { + "functional_id": "vine_mare", + "name": "Vine Mare", + "mana_cost": "{2}{G}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Elemental", + "Horse" + ], + "power": "5", + "toughness": "3" + }, + { + "functional_id": "vivien_of_the_arkbow", + "name": "Vivien of the Arkbow", + "mana_cost": "{4}{G}{G}", + "types": [ + "planeswalker" + ], + "colors": [ + "green" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Vivien" + ], + "loyalty": "5" + }, + { + "functional_id": "vivien_reid", + "name": "Vivien Reid", + "mana_cost": "{3}{G}{G}", + "types": [ + "planeswalker" + ], + "colors": [ + "green" + ], + "supertypes": [ + "legendary" + ], + "subtypes": [ + "Vivien" + ], + "loyalty": "5" + }, + { + "functional_id": "vivien_s_invocation", + "name": "Vivien's Invocation", + "mana_cost": "{5}{G}{G}", + "types": [ + "sorcery" + ], + "colors": [ + "green" + ] + }, + { + "functional_id": "vivien_s_jaguar", + "name": "Vivien's Jaguar", + "mana_cost": "{2}{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Cat", + "Spirit" + ], + "power": "3", + "toughness": "2" + }, + { + "functional_id": "volcanic_dragon", + "name": "Volcanic Dragon", + "mana_cost": "{4}{R}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Dragon" + ], + "power": "4", + "toughness": "4" + }, + { + "functional_id": "volley_veteran", + "name": "Volley Veteran", + "mana_cost": "{3}{R}", + "types": [ + "creature" + ], + "colors": [ + "red" + ], + "subtypes": [ + "Goblin", + "Warrior" + ], + "power": "4", + "toughness": "2" + }, + { + "functional_id": "walking_corpse", + "name": "Walking Corpse", + "mana_cost": "{1}{B}", + "types": [ + "creature" + ], + "colors": [ + "black" + ], + "subtypes": [ + "Zombie" + ], + "power": "2", + "toughness": "2" + }, + { + "functional_id": "wall_of_mist", + "name": "Wall of Mist", + "mana_cost": "{1}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Wall" + ], + "power": "0", + "toughness": "5" + }, + { + "functional_id": "wall_of_vines", + "name": "Wall of Vines", + "mana_cost": "{G}", + "types": [ + "creature" + ], + "colors": [ + "green" + ], + "subtypes": [ + "Plant", + "Wall" + ], + "power": "0", + "toughness": "3" + }, + { + "functional_id": "waterknot", + "name": "Waterknot", + "mana_cost": "{1}{U}{U}", + "types": [ + "enchantment" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Aura" + ] + }, + { + "functional_id": "windreader_sphinx", + "name": "Windreader Sphinx", + "mana_cost": "{5}{U}{U}", + "types": [ + "creature" + ], + "colors": [ + "blue" + ], + "subtypes": [ + "Sphinx" + ], + "power": "3", + "toughness": "7" + }, + { + "functional_id": "woodland_stream", + "name": "Woodland Stream", + "mana_cost": "", + "types": [ + "land" + ], + "colors": [] + } +] diff --git a/crates/sage-engine/tests/printed_characteristics.rs b/crates/sage-engine/tests/printed_characteristics.rs new file mode 100644 index 00000000..e59f1dec --- /dev/null +++ b/crates/sage-engine/tests/printed_characteristics.rs @@ -0,0 +1,321 @@ +//! The bundled catalog against the card that was actually printed (issue #822). +//! +//! `build.rs` validates a definition's *shape* — the file stem matches the identity, a +//! creature carries both power and toughness, a planeswalker carries loyalty and nothing +//! else does. None of that can know a number is **wrong**, and the behavioural suites +//! assert against whatever the definition says, so a mis-transcribed mana cost is +//! invisible at every gate and then cemented by the test written over it. Fifteen cards +//! shipped that way (#819, #820); twelve of them differed in a field a machine can check. +//! +//! This is that machine. It reads a fixture transcribed **from the printed set** and +//! compares it to `CardDatabase::bundled()` — the same embedded, validated catalog the +//! game is played with, not the JSON on disk. +//! +//! ## What the fixture may hold, and why +//! +//! Name, mana cost, supertypes, types, subtypes, power, toughness, loyalty, colours: the +//! functional characteristics ADR 0009 already sources for the catalog itself, and +//! nothing else. No rules text, no flavour text, no artist, no image, no set symbol — the +//! project ships none of those (`AGENTS.md`, Legal Considerations), and a fixture is not a +//! loophole in that rule. +//! +//! Rules text is therefore **out of scope for this gate**, and the limit is real: of the +//! fifteen wrong cards, this catches twelve. A missing `{R}` in a cost list, a `gain_life` +//! the card does not print, a token created tapped — those stay a human-review problem, +//! and the tests in `m19_activation_costs.rs` and `m19_attacking_tokens.rs` are where they +//! are held. +//! +//! ## The one rule that keeps this honest +//! +//! `crates/sage-engine/tests/fixtures/printed_characteristics.json` is transcribed from +//! the printed set by `scripts/printed-characteristics.py`, which reads a third-party set +//! file and **never** reads `data/catalog/`. Regenerating it the other way round would +//! leave this file green forever while proving nothing, which is the failure mode to +//! design against rather than to discover. +#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] + +use std::collections::BTreeMap; + +use sage_engine::{Ability, CardData, CardDatabase, CardType, Color, Supertype}; +use serde::Deserialize; + +/// One printed **face**. Every field is optional in the source the way it is optional on +/// a card — a land has no mana cost, an instant has no power — and absent means the +/// catalog must not carry one either. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct Face { + name: String, + mana_cost: String, + types: Vec, + colors: Vec, + #[serde(default)] + supertypes: Vec, + #[serde(default)] + subtypes: Vec, + /// Kept as a **string**, because a printed power is not always a number: Enigma + /// Drake's is `*`, which the catalog says as a base of 0 plus a `defined_power` + /// ability, and a numeric field could not tell that from a card that really is a 0. + #[serde(default)] + power: Option, + #[serde(default)] + toughness: Option, + #[serde(default)] + loyalty: Option, +} + +/// One card as it was printed: its front face, the identity the catalog files it under, +/// and the second face when it has one (CR 712). +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct Printed { + functional_id: String, + #[serde(flatten)] + front: Face, + #[serde(default)] + back_face: Option, +} + +const FIXTURE: &str = include_str!("fixtures/printed_characteristics.json"); + +fn printed() -> Vec { + serde_json::from_str(FIXTURE).expect("the fixture parses") +} + +fn catalog() -> BTreeMap { + // Leaked once, so the map can borrow for the whole test: a `CardDatabase` owns its + // definitions and every test here wants them by identity rather than by handle. + let db: &'static CardDatabase = Box::leak(Box::new( + CardDatabase::bundled().expect("the bundled catalog loads"), + )); + db.all() + .into_iter() + .map(|(_, data)| (data.functional_id.as_str().to_string(), data)) + .collect() +} + +/// Whether `card` sets its own power from something it counts — the shape a printed `*` +/// takes in this catalog. +fn power_is_defined(card: &CardData) -> bool { + card.abilities + .iter() + .any(|ability| matches!(ability, Ability::DefinedPower { .. })) +} + +// ----- the two directions of the correspondence ----------------------------- + +#[test] +fn issue_822_every_catalog_card_has_a_printed_card_behind_it() { + let fixture: Vec = printed() + .into_iter() + .map(|card| card.functional_id) + .collect(); + let catalog = catalog(); + let unbacked: Vec<&String> = catalog.keys().filter(|id| !fixture.contains(id)).collect(); + assert!( + unbacked.is_empty(), + "a definition with no printed card behind it is a card nobody can check: {unbacked:?}" + ); +} + +#[test] +fn issue_822_every_printed_card_in_the_fixture_is_in_the_catalog() { + let catalog = catalog(); + let orphans: Vec = printed() + .into_iter() + .map(|card| card.functional_id) + .filter(|id| !catalog.contains_key(id)) + .collect(); + assert!( + orphans.is_empty(), + "a fixture row naming no definition is a row nothing checks: {orphans:?}" + ); +} + +// ----- the characteristics themselves ---------------------------------------- + +#[test] +fn issue_822_the_catalog_matches_the_printed_characteristics() { + let catalog = catalog(); + let mut wrong: Vec = Vec::new(); + + for card in printed() { + let Some(data) = catalog.get(&card.functional_id) else { + continue; // reported by the correspondence test above + }; + let mut note = |field: &str, found: String, want: String| { + wrong.push(format!( + "{}: {field} is {found}, printed {want}", + card.functional_id + )); + }; + + if data.name != card.front.name { + note( + "name", + format!("{:?}", data.name), + format!("{:?}", card.front.name), + ); + } + if data.mana_cost != card.front.mana_cost { + note( + "mana_cost", + format!("{:?}", data.mana_cost), + format!("{:?}", card.front.mana_cost), + ); + } + if data.types != card.front.types { + note( + "types", + format!("{:?}", data.types), + format!("{:?}", card.front.types), + ); + } + if data.supertypes != card.front.supertypes { + note( + "supertypes", + format!("{:?}", data.supertypes), + format!("{:?}", card.front.supertypes), + ); + } + if data.subtypes != card.front.subtypes { + note( + "subtypes", + format!("{:?}", data.subtypes), + format!("{:?}", card.front.subtypes), + ); + } + if data.colors != card.front.colors { + note( + "colors", + format!("{:?}", data.colors), + format!("{:?}", card.front.colors), + ); + } + + // A `*` is not a number the catalog stores: the card says "equal to the number + // of…", and the definition says that with an ability. What is checked is that + // the ability is there — a base of 0 with nothing to define it is a 0/4 wearing + // a `*`'s clothes. + match card.front.power.as_deref() { + Some("*") => { + if !power_is_defined(data) { + note( + "power", + format!("{:?} with no defined_power ability", data.power), + "* (a defined power)".to_string(), + ); + } + } + other => { + let found = data.power.map(|value| value.to_string()); + if found.as_deref() != other { + note("power", format!("{found:?}"), format!("{other:?}")); + } + } + } + let toughness = data.toughness.map(|value| value.to_string()); + if toughness.as_deref() != card.front.toughness.as_deref() { + note( + "toughness", + format!("{toughness:?}"), + format!("{:?}", card.front.toughness), + ); + } + let loyalty = data.loyalty.map(|value| value.to_string()); + if loyalty.as_deref() != card.front.loyalty.as_deref() { + note( + "loyalty", + format!("{loyalty:?}"), + format!("{:?}", card.front.loyalty), + ); + } + } + + assert!( + wrong.is_empty(), + "the catalog disagrees with the card that was printed:\n {}", + wrong.join("\n ") + ); +} + +#[test] +fn issue_822_a_second_face_matches_the_face_that_was_printed() { + let catalog = catalog(); + let mut wrong: Vec = Vec::new(); + + for card in printed() { + let Some(data) = catalog.get(&card.functional_id) else { + continue; + }; + match (&card.back_face, &data.back_face) { + (None, None) => {} + (Some(printed), Some(face)) => { + if face.name != printed.name + || face.types != printed.types + || face.supertypes != printed.supertypes + || face.subtypes != printed.subtypes + || face.colors != printed.colors + || face.power.map(|value| value.to_string()).as_deref() + != printed.power.as_deref() + || face.toughness.map(|value| value.to_string()).as_deref() + != printed.toughness.as_deref() + || face.loyalty.map(|value| value.to_string()).as_deref() + != printed.loyalty.as_deref() + { + wrong.push(format!("{}: back face is {face:?}", card.functional_id)); + } + } + (Some(_), None) => wrong.push(format!( + "{}: the printed card transforms and the definition has no back face", + card.functional_id + )), + (None, Some(_)) => wrong.push(format!( + "{}: the definition has a back face and the printed card has none", + card.functional_id + )), + } + } + + assert!( + wrong.is_empty(), + "a second face disagrees with the face that was printed:\n {}", + wrong.join("\n ") + ); +} + +// ----- the licensing constraint, enforced rather than remembered ------------- + +#[test] +fn issue_822_the_fixture_carries_nothing_but_printed_characteristics() { + // `deny_unknown_fields` on `Printed` already refuses a field this test does not know + // about, so a future regeneration cannot smuggle rules text past it silently. This + // states the allowed set outright as well, because the reason for the limit lives in + // `AGENTS.md` rather than in serde, and a reviewer should not have to infer it. + const ALLOWED: [&str; 11] = [ + "functional_id", + "name", + "mana_cost", + "types", + "colors", + "supertypes", + "subtypes", + "power", + "toughness", + "loyalty", + "back_face", + ]; + let rows: Vec> = + serde_json::from_str(FIXTURE).expect("the fixture parses"); + assert!(!rows.is_empty(), "the fixture is not empty"); + for row in &rows { + for key in row.keys() { + assert!( + ALLOWED.contains(&key.as_str()), + "{key:?} is not a printed characteristic — this project ships no rules \ + text, flavour text, art, or branding, and a test fixture is not an \ + exception (AGENTS.md)" + ); + } + } +} diff --git a/docs/card-schema.md b/docs/card-schema.md index cea9fb5c..07d4a38d 100644 --- a/docs/card-schema.md +++ b/docs/card-schema.md @@ -17,6 +17,37 @@ The Rust authorities are `CardData` and `Printing` in `build.rs` discovers, validates, sorts, and embeds both directories at compile time. The running engine performs no filesystem I/O. +### The accuracy fixture + +`build.rs` and `src/catalog/` check a definition's **shape** — the file stem matches the +identity, a creature carries both power and toughness, a planeswalker carries loyalty and +nothing else does. None of that can know a number is *wrong*, and the behavioural tests +assert against whatever the definition says, so a mis-transcribed mana cost passes every +gate and is then cemented by the test written over it. Fifteen cards shipped that way +(issue #819). + +`crates/sage-engine/tests/fixtures/printed_characteristics.json` is the independent copy +that closes that gap: one record per catalog card, carrying **only** name, mana cost, +supertypes, types, subtypes, power, toughness, loyalty, and colours — the same functional +data [ADR 0009](decisions/0009-real-functional-card-data.md) already sources, and nothing +the licensing rule forbids. `tests/printed_characteristics.rs` compares it to +`CardDatabase::bundled()` and names the card and the field when they disagree. + +Regenerate it when a set is added, from a third-party set file that is **not** committed: + +```sh +scripts/printed-characteristics.py path/to/M19.json [path/to/OTHER.json ...] +``` + +The script reads that file and the catalog's *identities*; it never reads a definition's +characteristics. Regenerating the fixture from `data/catalog/` instead would leave the +gate green forever while proving nothing, which is the one way this file can fail. + +Rules text is deliberately outside the fixture, and the limit is real: of the fifteen wrong +cards this catches the twelve that differed in a printed characteristic. A missing `{R}` in +a cost list or a token created tapped is still a human-review problem, held by the +behavioural tests instead. + ## Functional definition The bundled catalog's functional definitions are sourced from a real set (Core Set 2019); diff --git a/scripts/printed-characteristics.py b/scripts/printed-characteristics.py new file mode 100755 index 00000000..9f2dec5e --- /dev/null +++ b/scripts/printed-characteristics.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Transcribe a set's printed characteristics into the fixture the catalog is checked against. + +Usage: scripts/printed-characteristics.py [ ...] + +Writes crates/sage-engine/tests/fixtures/printed_characteristics.json: one record per +card the bundled catalog holds, carrying only what is printed in the type line and the +mana cost. No rules text, no flavour text, no artist, no image, no set symbol -- the +functional characteristics ADR 0009 already sources, and nothing the licensing rule in +AGENTS.md forbids. + +The point of this file is that it is transcribed from the *printed set* and never from +the catalog. Regenerating it from `crates/sage-engine/data/catalog/` would turn the gate +that reads it into a no-op. +""" +import json, glob, os, sys + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +CATALOG = os.path.join(ROOT, "crates", "sage-engine", "data", "catalog") +OUT = os.path.join(ROOT, "crates", "sage-engine", "tests", "fixtures", + "printed_characteristics.json") + +COLOR = {"W": "white", "U": "blue", "B": "black", "R": "red", "G": "green"} +WUBRG = ["white", "blue", "black", "red", "green"] + + +def faces(cards): + """Every printing in the source, indexed by the name a catalog entry would use.""" + by_name = {} + for card in cards: + by_name.setdefault(card["name"], []).append(card) + if card.get("faceName"): + by_name.setdefault(card["faceName"], []).append(card) + return by_name + + +def characteristics(card): + out = { + "name": card.get("faceName") or card["name"], + "mana_cost": card.get("manaCost", ""), + "types": [t.lower() for t in card.get("types", [])], + "colors": [c for c in WUBRG if c in {COLOR[x] for x in card.get("colors", [])}], + } + if card.get("supertypes"): + out["supertypes"] = [t.lower() for t in card["supertypes"]] + if card.get("subtypes"): + out["subtypes"] = list(card["subtypes"]) + if card.get("power") is not None: + out["power"] = card["power"] + if card.get("toughness") is not None: + out["toughness"] = card["toughness"] + if card.get("loyalty") is not None: + out["loyalty"] = card["loyalty"] + return out + + +def main(paths): + cards = [] + for path in paths: + cards.extend(json.load(open(path))["data"]["cards"]) + by_name = faces(cards) + + records = [] + missing = [] + for path in sorted(glob.glob(os.path.join(CATALOG, "*.json"))): + entry = json.load(open(path)) + printings = by_name.get(entry["name"]) + if not printings: + missing.append(entry["functional_id"]) + continue + front = next((c for c in printings if c.get("side") in (None, "a")), printings[0]) + record = {"functional_id": entry["functional_id"]} + record.update(characteristics(front)) + if front.get("layout") == "transform": + back = next(c for c in cards + if c["name"] == front["name"] and c.get("side") == "b") + record["back_face"] = characteristics(back) + records.append(record) + + if missing: + sys.exit(f"no printing found for: {', '.join(missing)}") + os.makedirs(os.path.dirname(OUT), exist_ok=True) + with open(OUT, "w") as handle: + json.dump(records, handle, indent=2) + handle.write("\n") + print(f"wrote {OUT} ({len(records)} cards)") + + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.exit(__doc__) + main(sys.argv[1:])