From 568b81b222619597aac5c1277c9109c3d41e5e9f Mon Sep 17 00:00:00 2001 From: David Fischer Date: Tue, 29 Sep 2026 20:12:57 -0700 Subject: [PATCH] Update Vintage archetypes in line with IamActuallyLvL1 See https://github.com/IamActuallyLvL1/MTGOFormatData --- README.md | 13 +- archetypes/vintage/README.md | 13 ++ archetypes/vintage/aggrovine.yaml | 25 +++ archetypes/vintage/beseech-storm.yaml | 19 ++ archetypes/vintage/blue-control.yaml | 44 +++++ .../{underworld-breach.yaml => breach.yaml} | 12 +- archetypes/vintage/countervine.yaml | 6 +- archetypes/vintage/dimir-lurrus.yaml | 7 +- archetypes/vintage/doomsday.yaml | 3 + archetypes/vintage/dredge.yaml | 13 +- archetypes/vintage/eldrazi.yaml | 10 +- archetypes/vintage/esper-lurrus.yaml | 11 +- archetypes/vintage/fallback-other-aggro.yaml | 52 ++++++ archetypes/vintage/fallback-other-combo.yaml | 47 +++++ archetypes/vintage/fallback-other-lurrus.yaml | 26 +++ archetypes/vintage/fallback-other-shops.yaml | 23 +++ ...-white-initiative.yaml => initiative.yaml} | 8 +- archetypes/vintage/jewel-shops.yaml | 3 +- archetypes/vintage/lurrus-breach.yaml | 20 +++ archetypes/vintage/lurrus-doomsday.yaml | 15 ++ .../{bug-lurrus.yaml => lurrus-drs.yaml} | 4 +- archetypes/vintage/lurrus-po.yaml | 10 +- archetypes/vintage/lurrus-vault-key.yaml | 21 +++ archetypes/vintage/merfolk.yaml | 6 +- archetypes/vintage/oath.yaml | 4 - .../{paradoxical-outcome-po.yaml => po.yaml} | 12 +- archetypes/vintage/raker-shops.yaml | 13 +- archetypes/vintage/red-prison.yaml | 5 +- archetypes/vintage/scam.yaml | 21 +++ archetypes/vintage/sphere-shops.yaml | 10 +- archetypes/vintage/storm.yaml | 14 -- archetypes/vintage/sultai.yaml | 23 +++ archetypes/vintage/tinker.yaml | 7 +- tests/test_classifier.py | 163 ++++++++++++++++++ 34 files changed, 607 insertions(+), 76 deletions(-) create mode 100644 archetypes/vintage/README.md create mode 100644 archetypes/vintage/aggrovine.yaml create mode 100644 archetypes/vintage/beseech-storm.yaml create mode 100644 archetypes/vintage/blue-control.yaml rename archetypes/vintage/{underworld-breach.yaml => breach.yaml} (65%) create mode 100644 archetypes/vintage/fallback-other-aggro.yaml create mode 100644 archetypes/vintage/fallback-other-combo.yaml create mode 100644 archetypes/vintage/fallback-other-lurrus.yaml create mode 100644 archetypes/vintage/fallback-other-shops.yaml rename archetypes/vintage/{mono-white-initiative.yaml => initiative.yaml} (79%) create mode 100644 archetypes/vintage/lurrus-breach.yaml create mode 100644 archetypes/vintage/lurrus-doomsday.yaml rename archetypes/vintage/{bug-lurrus.yaml => lurrus-drs.yaml} (84%) create mode 100644 archetypes/vintage/lurrus-vault-key.yaml rename archetypes/vintage/{paradoxical-outcome-po.yaml => po.yaml} (64%) create mode 100644 archetypes/vintage/scam.yaml delete mode 100644 archetypes/vintage/storm.yaml create mode 100644 archetypes/vintage/sultai.yaml diff --git a/README.md b/README.md index b6ba21f..ce291f6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,12 @@ cat ur-delver.txt | mtg-archetypes legacy pip install mtg-archetypes ``` +Or if you have [`uv`](https://docs.astral.sh/uv/) installed, you can use mtg-archetypes directly: + +```bash +cat ur-delver.txt | uvx mtg-archetypes legacy +``` + --- ## Quickstart @@ -71,7 +77,7 @@ Category: Tempo ``` ```bash -cat unknown_pile.txt | mtg-archetypes legacy --verbose +echo "60 Plains" | mtg-archetypes legacy --verbose ``` ```text Archetype: Unclassified @@ -118,7 +124,7 @@ classifier = ArchetypeClassifier() # Or use: # decklist = "4 Delver of Secrets\n..." -# mtg_archetypes.cards.parse_decklist_text(decklist) +# mtg_archetypes.parse_decklist_text(decklist) mainboard = [ "4 Delver of Secrets", "4 Daze", @@ -239,9 +245,6 @@ uv run python scripts/validate_archetypes.py # Also validate every card name against Scryfall catalog uv run python scripts/validate_archetypes.py --check-cards - -# Force update local Scryfall cache -uv run python scripts/validate_archetypes.py --update-scryfall ``` --- diff --git a/archetypes/vintage/README.md b/archetypes/vintage/README.md new file mode 100644 index 0000000..33c4e8e --- /dev/null +++ b/archetypes/vintage/README.md @@ -0,0 +1,13 @@ +# Vintage Archetypes + +The archetype definitions in this directory are heavily based on the Vintage format data from [Badaro/MTGOFormatData](https://github.com/Badaro/MTGOFormatData), created and maintained by [IamActuallyLvL1](https://github.com/IamActuallyLvL1) (see [fork](https://github.com/IamActuallyLvL1/MTGOFormatData)). + +The main difference should be just a preference for "new" color names (Sultai vs. BUG, Dimir vs. UB) + +We also categorize archetypes into the following: + +- Bazaar +- Shops +- Combo +- Control +- Aggro diff --git a/archetypes/vintage/aggrovine.yaml b/archetypes/vintage/aggrovine.yaml new file mode 100644 index 0000000..210d55c --- /dev/null +++ b/archetypes/vintage/aggrovine.yaml @@ -0,0 +1,25 @@ +name: "Aggrovine" +category: "Bazaar" +priority: 95 + +mandatory: + - card: "Bazaar of Baghdad" + min: 2 + in: "main" + - "Vengevine" + +# Unlike Countervine, Aggrovine needs to make green mana +signatures: + - "Basking Rootwalla" + - "Blazing Rootwalla" + - "Gaea's Cradle" + - "Boseiju, Who Endures" + - "Yavimaya, Cradle of Growth" + - "Bayou" + - "Dryad Arbor" + - "Elvish Spirit Guide" + - "Once Upon a Time" +min_signatures: 3 + +anti_signatures: + - "Force of Will" diff --git a/archetypes/vintage/beseech-storm.yaml b/archetypes/vintage/beseech-storm.yaml new file mode 100644 index 0000000..2c6b112 --- /dev/null +++ b/archetypes/vintage/beseech-storm.yaml @@ -0,0 +1,19 @@ +name: "Beseech Storm" +category: "Combo" + +# Higher priority than Tinker (100) and Storm (90) +priority: 105 + +mandatory: + - "Beseech the Mirror" + - "Tendrils of Agony" + +signatures: + - "Tinker" + - "Dark Ritual" + - "Cabal Ritual" + - "Bolas's Citadel" + - "Necropotence" + - "Necrodominance" + - "Wishclaw Talisman" +min_signatures: 1 diff --git a/archetypes/vintage/blue-control.yaml b/archetypes/vintage/blue-control.yaml new file mode 100644 index 0000000..0ee9aff --- /dev/null +++ b/archetypes/vintage/blue-control.yaml @@ -0,0 +1,44 @@ +# Despite the name, this doesn't have to be mono-U +name: "Blue Control" +category: "Control" + +# Lower than more explicit archetypes but not a fallback +priority: 75 + +mandatory: + - "Force of Will" + +signatures: + - "Swords to Plowshares" + - "Pyroblast" + - "Red Elemental Blast" + - "Fatal Push" + - "Preordain" + - "Ponder" + - "Brainstorm" + - "Ancestral Recall" + - "Mana Drain" + - "The One Ring" + - "Narset, Parter of Veils" + - "Teferi, Time Raveler" + - "Prismatic Ending" + - "Lightning Bolt" + - "Hullbreacher" + - "Force of Negation" + - "Lórien Revealed" + - "Volcanic Island" + - "Tundra" +min_signatures: 4 + +anti_signatures: + - "Tinker" + - "Bazaar of Baghdad" + - "Mishra's Workshop" + - "Doomsday" + - "Underworld Breach" + - "Paradoxical Outcome" + - "Oath of Druids" + - "Reanimate" + - "Lurrus of the Dream-Den" + - "Deathrite Shaman" + - "Chrome Mox" diff --git a/archetypes/vintage/underworld-breach.yaml b/archetypes/vintage/breach.yaml similarity index 65% rename from archetypes/vintage/underworld-breach.yaml rename to archetypes/vintage/breach.yaml index 8ad9fb9..925eb6f 100644 --- a/archetypes/vintage/underworld-breach.yaml +++ b/archetypes/vintage/breach.yaml @@ -1,15 +1,19 @@ -name: "Underworld Breach" -category: "Aggro" +name: "Breach" +category: "Combo" priority: 90 mandatory: - "Underworld Breach" signatures: - - "Lurrus of the Dream-Den" - "Brain Freeze" - "Gifts Ungiven" - "Demonic Tutor" - "Merchant Scroll" - "wheel of fortune" -min_signatures: 3 +min_signatures: 1 + +anti_signatures: + - "Lurrus of the Dream-Den" + - "Doomsday" + - "Oath of Druids" diff --git a/archetypes/vintage/countervine.yaml b/archetypes/vintage/countervine.yaml index 4a3b6df..6e24629 100644 --- a/archetypes/vintage/countervine.yaml +++ b/archetypes/vintage/countervine.yaml @@ -1,10 +1,10 @@ -name: "CounterVine" -category: "Aggro" +name: "Countervine" +category: "Bazaar" priority: 95 mandatory: - card: "bazaar of baghdad" - min: 3 + min: 2 - "Vengevine" signatures: diff --git a/archetypes/vintage/dimir-lurrus.yaml b/archetypes/vintage/dimir-lurrus.yaml index 96051a0..6a0c246 100644 --- a/archetypes/vintage/dimir-lurrus.yaml +++ b/archetypes/vintage/dimir-lurrus.yaml @@ -1,5 +1,5 @@ name: "Dimir Lurrus" -category: "Midrange" +category: "Lurrus" priority: 90 mandatory: @@ -9,6 +9,7 @@ signatures: - "Psychic Frog" - "Orcish Bowmasters" - "Underground Sea" + - "Bilbo, Thief in the Night" min_signatures: 2 anti_signatures: @@ -16,3 +17,7 @@ anti_signatures: - "Tundra" - "Tropical Island" - "paradoxical outcome" + - "Time Vault" + - "Underworld Breach" + - "Doomsday" + - "Deathrite Shaman" diff --git a/archetypes/vintage/doomsday.yaml b/archetypes/vintage/doomsday.yaml index 4eda7e6..45b0cda 100644 --- a/archetypes/vintage/doomsday.yaml +++ b/archetypes/vintage/doomsday.yaml @@ -12,3 +12,6 @@ signatures: - "demonic consultation" - "time walk" min_signatures: 2 + +anti_signatures: + - "Lurrus of the Dream-Den" diff --git a/archetypes/vintage/dredge.yaml b/archetypes/vintage/dredge.yaml index b725e33..41174d4 100644 --- a/archetypes/vintage/dredge.yaml +++ b/archetypes/vintage/dredge.yaml @@ -1,10 +1,12 @@ name: "Dredge" -category: "Aggro" -priority: 95 +category: "Bazaar" + +# Lower than Aggrovine and Countervine +priority: 90 mandatory: - card: "bazaar of baghdad" - min: 3 + min: 2 signatures: - "golgari grave-troll" @@ -12,7 +14,6 @@ signatures: - "hollow one" - "ichorid" - "Creeping Chill" + - "Golgari Thug" + - "Shambling Shell" min_signatures: 2 - -anti_signatures: - - "Vengevine" diff --git a/archetypes/vintage/eldrazi.yaml b/archetypes/vintage/eldrazi.yaml index 03ea921..7dac8c1 100644 --- a/archetypes/vintage/eldrazi.yaml +++ b/archetypes/vintage/eldrazi.yaml @@ -3,14 +3,14 @@ category: "Aggro" priority: 90 mandatory: - - "sowing mycospawn" - - "glaring fleshraker" + - "eye of ugin" + - "eldrazi temple" signatures: - "wastescape battlemage" - "kozileks command" - "thought-knot seer" - - "eye of ugin" - "gaeas cradle" - - "eldrazi temple" -min_signatures: 3 + - "sowing mycospawn" + - "glaring fleshraker" +min_signatures: 2 diff --git a/archetypes/vintage/esper-lurrus.yaml b/archetypes/vintage/esper-lurrus.yaml index c9ff494..87ca1ef 100644 --- a/archetypes/vintage/esper-lurrus.yaml +++ b/archetypes/vintage/esper-lurrus.yaml @@ -1,5 +1,5 @@ name: "Esper Lurrus" -category: "Control" +category: "Lurrus" priority: 90 mandatory: @@ -11,3 +11,12 @@ signatures: - "Tundra" - "Underground Sea" min_signatures: 3 + +anti_signatures: + - "Oswald Fiddlebender" + - "Time Vault" + - "Volcanic Island" + - "Tropical Island" + - "Underworld Breach" + - "Paradoxical Outcome" + - "Deathrite Shaman" diff --git a/archetypes/vintage/fallback-other-aggro.yaml b/archetypes/vintage/fallback-other-aggro.yaml new file mode 100644 index 0000000..1f920a3 --- /dev/null +++ b/archetypes/vintage/fallback-other-aggro.yaml @@ -0,0 +1,52 @@ +name: "Other Aggro" +category: "Aggro" +priority: 20 + +signature_groups: + - min: 2 + cards: + - "Thalia, Guardian of Thraben" + - "Archon of Emeria" + - "Stoneforge Mystic" + - "Collector Ouphe" + - "Outland Liberator" + - "Luminarch Aspirant" + - "Noble Hierarch" + - "Anointed Peacekeeper" + - "Clarion Conqueror" + - "Containment Priest" + - "Spirit of the Labyrinth" + - "Phyrexian Revoker" + - "White Plume Adventurer" + - "Seasoned Dungeoneer" + - "Solitude" + - "Endurance" + +signatures: + - "Thalia, Guardian of Thraben" + - "Archon of Emeria" + - "Stoneforge Mystic" + - "Collector Ouphe" + - "Outland Liberator" + - "Luminarch Aspirant" + - "Noble Hierarch" + - "Anointed Peacekeeper" + - "Clarion Conqueror" + - "Containment Priest" + - "Spirit of the Labyrinth" + - "Phyrexian Revoker" + - "White Plume Adventurer" + - "Seasoned Dungeoneer" + - "Solitude" + - "Endurance" + - "Wasteland" + - "Strip Mine" + - "Karakas" + - "Cavern of Souls" + - "Force of Vigor" + - "Once Upon a Time" + - "Simian Spirit Guide" + - "Elvish Spirit Guide" + - "Kaldra Compleat" + - "Batterskull" +min_signatures: 3 diff --git a/archetypes/vintage/fallback-other-combo.yaml b/archetypes/vintage/fallback-other-combo.yaml new file mode 100644 index 0000000..1f7089f --- /dev/null +++ b/archetypes/vintage/fallback-other-combo.yaml @@ -0,0 +1,47 @@ +name: "Other Combo" +category: "Combo" + +# Other Lurrus (25) should match first +priority: 20 + +# Fair decks don't have these cards generally +signature_groups: + - min: 1 + cards: + - "Painter's Servant" + - "Grindstone" + - "Show and Tell" + - "Time Vault" + - "Helm of Obedience" + - "Necropotence" + - "Necrodominance" + - "Aetherflux Reservoir" + - "Mind's Desire" + # Consider removing as some decks splash a fastbond + - "Fastbond" + - "Yawgmoth's Will" + - "Goblin Charbelcher" + +signatures: + - "Painter's Servant" + - "Grindstone" + - "Goblin Engineer" + - "Goblin Welder" + - "Show and Tell" + - "Sneak Attack" + - "Omniscience" + - "Fastbond" + - "Yawgmoth's Will" + - "Necropotence" + - "Necrodominance" + - "Time Vault" + - "Manifold Key" + - "Voltaic Key" + - "Helm of Obedience" + - "Aetherflux Reservoir" + - "Mind's Desire" + - "Tendrils of Agony" + - "Bolas's Citadel" + - "Atraxa, Grand Unifier" + - "Archon of Cruelty" +min_signatures: 2 diff --git a/archetypes/vintage/fallback-other-lurrus.yaml b/archetypes/vintage/fallback-other-lurrus.yaml new file mode 100644 index 0000000..d3b79af --- /dev/null +++ b/archetypes/vintage/fallback-other-lurrus.yaml @@ -0,0 +1,26 @@ +name: "Other Lurrus" +category: "Lurrus" +priority: 25 + +mandatory: + - "Lurrus of the Dream-Den" + +signatures: + - "Ancestral Recall" + - "Brainstorm" + - "Ponder" + - "Preordain" + - "Dig Through Time" + - "Treasure Cruise" + - "Force of Will" + - "Force of Negation" + - "Spell Pierce" + - "Gitaxian Probe" + - "Swords to Plowshares" + - "Fatal Push" + - "Pyroblast" + - "Lórien Revealed" + - "Lavinia, Azorius Renegade" + - "Orcish Bowmasters" + - "Mishra's Bauble" +min_signatures: 3 diff --git a/archetypes/vintage/fallback-other-shops.yaml b/archetypes/vintage/fallback-other-shops.yaml new file mode 100644 index 0000000..071d59c --- /dev/null +++ b/archetypes/vintage/fallback-other-shops.yaml @@ -0,0 +1,23 @@ +name: "Other Shops" +category: "Shops" +priority: 25 + +mandatory: + - card: "Mishra's Workshop" + min: 3 + in: "main" + +signatures: + - "Patchwork Automaton" + - "Phyrexian Metamorph" + - "Lodestone Golem" + - "Mox Opal" + - "Nettlecyst" + - "The One Ring" + - "Chimil, the Inner Sun" + - "Grim Monolith" + - "Karn, the Great Creator" + - "Trinisphere" + - "Chalice of the Void" + - "Sphere of Resistance" +min_signatures: 2 diff --git a/archetypes/vintage/mono-white-initiative.yaml b/archetypes/vintage/initiative.yaml similarity index 79% rename from archetypes/vintage/mono-white-initiative.yaml rename to archetypes/vintage/initiative.yaml index 55df22e..4e7d812 100644 --- a/archetypes/vintage/mono-white-initiative.yaml +++ b/archetypes/vintage/initiative.yaml @@ -1,12 +1,12 @@ -name: "Mono-White Initiative" +name: "Initiative" category: "Aggro" priority: 95 mandatory: - card: "White Plume Adventurer" - min: 3 + min: 2 - card: "Seasoned Dungeoneer" - min: 3 + min: 2 signatures: - "Archon of Emeria" @@ -15,4 +15,4 @@ signatures: - "Chancellor of the Annex" - "Chrome Mox" - "Cavern of Souls" -min_signatures: 3 +min_signatures: 1 diff --git a/archetypes/vintage/jewel-shops.yaml b/archetypes/vintage/jewel-shops.yaml index 49752ec..3a1c5e7 100644 --- a/archetypes/vintage/jewel-shops.yaml +++ b/archetypes/vintage/jewel-shops.yaml @@ -1,5 +1,5 @@ name: "Jewel Shops" -category: "Prison" +category: "Shops" priority: 95 mandatory: @@ -13,4 +13,5 @@ signatures: - "The One Ring" - "Phyrexian Metamorph" - "Transmute Artifact" + - "Force of Will" min_signatures: 2 diff --git a/archetypes/vintage/lurrus-breach.yaml b/archetypes/vintage/lurrus-breach.yaml new file mode 100644 index 0000000..bbea7fb --- /dev/null +++ b/archetypes/vintage/lurrus-breach.yaml @@ -0,0 +1,20 @@ +name: "Lurrus Breach" +category: "Lurrus" +priority: 90 + +mandatory: + - "Underworld Breach" + - "Lurrus of the Dream-Den" + +signatures: + - "Brain Freeze" + - "Gifts Ungiven" + - "Demonic Tutor" + - "Merchant Scroll" + - "wheel of fortune" +min_signatures: 1 + +anti_signatures: + - "Doomsday" + - "Oath of Druids" + - "Paradoxical Outcome" diff --git a/archetypes/vintage/lurrus-doomsday.yaml b/archetypes/vintage/lurrus-doomsday.yaml new file mode 100644 index 0000000..55f12c0 --- /dev/null +++ b/archetypes/vintage/lurrus-doomsday.yaml @@ -0,0 +1,15 @@ +name: "Lurrus Doomsday" +category: "Lurrus" +priority: 95 + +mandatory: + - "doomsday" + - "Lurrus of the Dream-Den" + +signatures: + - "thassa's oracle" + - "dark ritual" + - "gush" + - "demonic consultation" + - "time walk" +min_signatures: 1 diff --git a/archetypes/vintage/bug-lurrus.yaml b/archetypes/vintage/lurrus-drs.yaml similarity index 84% rename from archetypes/vintage/bug-lurrus.yaml rename to archetypes/vintage/lurrus-drs.yaml index 9ea411d..66457c2 100644 --- a/archetypes/vintage/bug-lurrus.yaml +++ b/archetypes/vintage/lurrus-drs.yaml @@ -1,5 +1,5 @@ -name: "BUG Lurrus" -category: "Midrange" +name: "Lurrus DRS" +category: "Lurrus" priority: 90 mandatory: diff --git a/archetypes/vintage/lurrus-po.yaml b/archetypes/vintage/lurrus-po.yaml index fdd5165..4f8d282 100644 --- a/archetypes/vintage/lurrus-po.yaml +++ b/archetypes/vintage/lurrus-po.yaml @@ -1,5 +1,5 @@ name: "Lurrus PO" -category: "Combo" +category: "Lurrus" priority: 100 mandatory: @@ -11,4 +11,10 @@ signatures: - "Thundertrap Trainer" - "Flame of Anor" - "Tolarian Academy" -min_signatures: 2 + - "Sol Ring" +min_signatures: 1 +anti_signatures: + - "Oswald Fiddlebender" + - "Doomsday" + - "Oath of Druids" + - "Goblin Engineer" diff --git a/archetypes/vintage/lurrus-vault-key.yaml b/archetypes/vintage/lurrus-vault-key.yaml new file mode 100644 index 0000000..531d7ce --- /dev/null +++ b/archetypes/vintage/lurrus-vault-key.yaml @@ -0,0 +1,21 @@ +name: "Lurrus Vault Key" +category: "Lurrus" +priority: 95 + +mandatory: + - "Lurrus of the Dream-Den" + - "Time Vault" + +signatures: + - "Manifold Key" + - "Voltaic Key" + - "Demonic Tutor" + - "Urza's Saga" + - "Force of Will" +min_signatures: 1 + +anti_signatures: + - "Underworld Breach" + - "Brain Freeze" + - "Deathrite Shaman" + - "Paradoxical Outcome" diff --git a/archetypes/vintage/merfolk.yaml b/archetypes/vintage/merfolk.yaml index d549497..2f0223f 100644 --- a/archetypes/vintage/merfolk.yaml +++ b/archetypes/vintage/merfolk.yaml @@ -2,10 +2,6 @@ name: "Merfolk" category: "Aggro" priority: 90 -mandatory: - - card: "Cavern of Souls" - min: 3 - signatures: - "true-name nemesis" - "vodalian hexcatcher" @@ -13,4 +9,6 @@ signatures: - "Tishana's Tidebinder" - "Hullbreacher" - "Floodpits Drowner" + - "Cavern of Souls" + - "Lord of Atlantis" min_signatures: 3 diff --git a/archetypes/vintage/oath.yaml b/archetypes/vintage/oath.yaml index cd9ec40..0475d5f 100644 --- a/archetypes/vintage/oath.yaml +++ b/archetypes/vintage/oath.yaml @@ -13,7 +13,3 @@ signatures: - "flash" - "gaea's blessing" min_signatures: 2 - -anti_signatures: - - "doomsday" - - "paradoxical outcome" diff --git a/archetypes/vintage/paradoxical-outcome-po.yaml b/archetypes/vintage/po.yaml similarity index 64% rename from archetypes/vintage/paradoxical-outcome-po.yaml rename to archetypes/vintage/po.yaml index 4e9965a..54ee4e0 100644 --- a/archetypes/vintage/paradoxical-outcome-po.yaml +++ b/archetypes/vintage/po.yaml @@ -1,19 +1,21 @@ -name: "Paradoxical Outcome (PO)" +name: "PO" category: "Combo" priority: 95 mandatory: - - card: "paradoxical outcome" - min: 3 + - "paradoxical outcome" + - "tinker" signatures: - "mox opal" - - "tinker" - "bolas's citadel" - "urza's saga" - "tolarian academy" -min_signatures: 2 +min_signatures: 1 anti_signatures: - "mishra's workshop" - "Lurrus of the Dream-Den" + - "Goblin Engineer" + - "Underworld Breach" + - "Oath of Druids" diff --git a/archetypes/vintage/raker-shops.yaml b/archetypes/vintage/raker-shops.yaml index 2f5a2b9..1aa889c 100644 --- a/archetypes/vintage/raker-shops.yaml +++ b/archetypes/vintage/raker-shops.yaml @@ -1,18 +1,15 @@ name: "Raker Shops" -category: "Combo" +category: "Shops" priority: 95 mandatory: - card: "mishra's workshop" - min: 3 + min: 2 - "Glaring Fleshraker" - -signatures: - "Sensei's Divining Top" - - "patchwork automaton" - - "Tezzeret, Cruel Captain" - - "The One Ring" -min_signatures: 2 anti_signatures: - "coveted jewel" + - "Sowing Mycospawn" + - card: "Sphere of Resistance" + in: "main" diff --git a/archetypes/vintage/red-prison.yaml b/archetypes/vintage/red-prison.yaml index 640734b..d158abb 100644 --- a/archetypes/vintage/red-prison.yaml +++ b/archetypes/vintage/red-prison.yaml @@ -1,9 +1,8 @@ name: "Red Prison" -category: "Stompy" +category: "Prison" priority: 90 mandatory: - - "simian spirit guide" - "magus of the moon" signatures: @@ -12,4 +11,6 @@ signatures: - "fury" - "broadside bombardiers" - "null rod" + - "chrome mox" + - "simian spirit guide" min_signatures: 2 diff --git a/archetypes/vintage/scam.yaml b/archetypes/vintage/scam.yaml new file mode 100644 index 0000000..3d53913 --- /dev/null +++ b/archetypes/vintage/scam.yaml @@ -0,0 +1,21 @@ +name: "Scam" +category: "Aggro" +priority: 90 + +mandatory: + - "Grief" + - "Reanimate" + +signatures: + - "Entomb" + - "Troll of Khazad-dûm" + - "Animate Dead" + - "Dauthi Voidwalker" + - "Orcish Bowmasters" + - "Wasteland" +min_signatures: 1 + +anti_signatures: + - "Lurrus of the Dream-Den" + - "Doomsday" + - "Bazaar of Baghdad" diff --git a/archetypes/vintage/sphere-shops.yaml b/archetypes/vintage/sphere-shops.yaml index c2e66e0..bfd5ce2 100644 --- a/archetypes/vintage/sphere-shops.yaml +++ b/archetypes/vintage/sphere-shops.yaml @@ -1,12 +1,12 @@ name: "Sphere Shops" -category: "Combo" +category: "Shops" priority: 95 mandatory: - card: "mishra's workshop" - min: 3 + min: 2 - card: "Sphere of Resistance" - min: 3 + min: 2 signatures: - "Thorn of Amethyst" @@ -18,7 +18,9 @@ signatures: - "Golos, Tireless Pilgrim" - "Stonecoil Serpent" - "Nettlecyst" + - "Arcbound Ravager" + - "Phyrexian Revoker" min_signatures: 2 anti_signatures: - - "coveted jewel" + - "force of will" diff --git a/archetypes/vintage/storm.yaml b/archetypes/vintage/storm.yaml deleted file mode 100644 index 2eb5bd4..0000000 --- a/archetypes/vintage/storm.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: "Storm" -category: "Stompy" -priority: 90 - -signatures: - - "beseech the mirror" - - "necropotence" - - "dark petition" - - "dark ritual" - - "tendrils of agony" -min_signatures: 3 - -anti_signatures: - - "Underworld Breach" diff --git a/archetypes/vintage/sultai.yaml b/archetypes/vintage/sultai.yaml new file mode 100644 index 0000000..605276e --- /dev/null +++ b/archetypes/vintage/sultai.yaml @@ -0,0 +1,23 @@ +name: "Sultai" +category: "Midrange" +priority: 90 + +mandatory: + - "Deathrite Shaman" + - "Tropical Island" + - "Underground Sea" + +signatures: + - "Oko, Thief of Crowns" + - "Collector Ouphe" + - "Wasteland" + - "Leovold, Emissary of Trest" + - "Abrupt Decay" + - "Grist, the Hunger Tide" + - "Psychic Frog" +min_signatures: 2 + +anti_signatures: + # This is BUG non-lurrus + - "Lurrus of the Dream-Den" + - "Volcanic Island" diff --git a/archetypes/vintage/tinker.yaml b/archetypes/vintage/tinker.yaml index 7fa6180..3fa9eb5 100644 --- a/archetypes/vintage/tinker.yaml +++ b/archetypes/vintage/tinker.yaml @@ -11,8 +11,13 @@ signatures: - "Paradox Engine" - "Time Vault" - "Tezzeret the Seeker" -min_signatures: 2 +min_signatures: 1 anti_signatures: - "coveted jewel" - "underworld breach" + - "Lurrus of the Dream-Den" + - "Mishra's Workshop" + - "Oath of Druids" + - "Paradoxical Outcome" + - "Beseech the Mirror" diff --git a/tests/test_classifier.py b/tests/test_classifier.py index eb6fdc8..0562fe2 100644 --- a/tests/test_classifier.py +++ b/tests/test_classifier.py @@ -281,3 +281,166 @@ def test_higher_priority_matches_first(tmp_path): assert res.matched is True assert res.name == "Delver" assert res.priority == 100 + + +def test_vintage_fallback_archetypes(classifier): + """Vintage fallback archetypes match when specific rules do not, and have lower priority.""" + # 1. Other Shops fallback (Karn / Monolith Shops that isn't Jewel or Sphere) + res_shops = classifier.classify( + [ + {"card": "Mishra's Workshop", "count": 4}, + "Karn, the Great Creator", + "Grim Monolith", + "The One Ring", + ], + format="vintage", + ) + assert res_shops.matched is True + assert res_shops.name == "Other Shops" + assert res_shops.slug == "other-shops" + assert res_shops.priority == 25 + + # 2. Other Lurrus fallback (Jeskai Lurrus without Dimir/Esper specific signatures) + res_lurrus = classifier.classify( + mainboard_cards=[ + "Volcanic Island", + "Tundra", + "Swords to Plowshares", + "Ancestral Recall", + "Brainstorm", + "Force of Will", + ], + sideboard_cards=["Lurrus of the Dream-Den"], + format="vintage", + ) + assert res_lurrus.matched is True + assert res_lurrus.name == "Other Lurrus" + assert res_lurrus.slug == "other-lurrus" + assert res_lurrus.priority == 25 + + # 3. Other Combo fallback (Painter combo) + res_combo = classifier.classify( + ["Painter's Servant", "Grindstone", "Pyroblast", "Force of Will"], + format="vintage", + ) + assert res_combo.matched is True + assert res_combo.name == "Other Combo" + assert res_combo.slug == "other-combo" + assert res_combo.priority == 20 + + # 4. Other Aggro fallback (Hatebears / Death & Taxes) + res_aggro = classifier.classify( + [ + "Thalia, Guardian of Thraben", + "Archon of Emeria", + "Wasteland", + "Karakas", + "Plains", + ], + format="vintage", + ) + assert res_aggro.matched is True + assert res_aggro.name == "Other Aggro" + assert res_aggro.slug == "other-aggro" + assert res_aggro.priority == 20 + + +def test_vintage_new_archetypes(classifier): + """Test newly added Vintage archetypes from MTGOFormatData.""" + # 1. Aggrovine + res_aggrovine = classifier.classify( + [ + {"card": "Bazaar of Baghdad", "count": 4}, + {"card": "Vengevine", "count": 4}, + {"card": "Basking Rootwalla", "count": 4}, + {"card": "Blazing Rootwalla", "count": 4}, + {"card": "Gaea's Cradle", "count": 2}, + {"card": "Bayou", "count": 4}, + ], + format="vintage", + ) + assert res_aggrovine.matched is True + assert res_aggrovine.name == "Aggrovine" + assert res_aggrovine.slug == "aggrovine" + + # 2. Lurrus Vault Key + res_lvk = classifier.classify( + mainboard_cards=[ + "Time Vault", + "Manifold Key", + "Demonic Tutor", + "Force of Will", + "Underground Sea", + ], + sideboard_cards=["Lurrus of the Dream-Den"], + format="vintage", + ) + assert res_lvk.matched is True + assert res_lvk.name == "Lurrus Vault Key" + assert res_lvk.slug == "lurrus-vault-key" + + # 3. Sultai (non-Lurrus BUG) + res_sultai = classifier.classify( + [ + "Deathrite Shaman", + "Oko, Thief of Crowns", + "Wasteland", + "Tropical Island", + "Underground Sea", + "Collector Ouphe", + ], + format="vintage", + ) + assert res_sultai.matched is True + assert res_sultai.name == "Sultai" + assert res_sultai.slug == "sultai" + + # 4. Scam + res_scam = classifier.classify( + [ + "Grief", + "Reanimate", + "Entomb", + "Troll of Khazad-dûm", + "Badlands", + "Wasteland", + ], + format="vintage", + ) + assert res_scam.matched is True + assert res_scam.name == "Scam" + assert res_scam.slug == "scam" + + # 5. Blue Control + res_blue = classifier.classify( + [ + "Force of Will", + "Swords to Plowshares", + "Pyroblast", + "Brainstorm", + "Ancestral Recall", + "Tundra", + "Volcanic Island", + "The One Ring", + ], + format="vintage", + ) + assert res_blue.matched is True + assert res_blue.name == "Blue Control" + assert res_blue.slug == "blue-control" + + # 6. Beseech Storm + res_beseech = classifier.classify( + [ + "Beseech the Mirror", + "Tendrils of Agony", + "Tinker", + "Dark Ritual", + "Bolas's Citadel", + ], + format="vintage", + ) + assert res_beseech.matched is True + assert res_beseech.name == "Beseech Storm" + assert res_beseech.slug == "beseech-storm" + assert res_beseech.priority == 105