From 21aecb4b6ed461ed044c6bcea0254314381ebdf4 Mon Sep 17 00:00:00 2001 From: matt Date: Mon, 13 Oct 2025 12:59:52 -0700 Subject: [PATCH 01/58] bugfix: Fixed custom theme selection and fuzzy matching issue --- CHANGELOG.md | 5 +++-- .../build/_new_deck_additional_themes.html | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e526fde..f5abdf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning ## [Unreleased] ### Summary -- _No unreleased changes yet._ +- Fixed issues with custom themes in the web UI. ### Added - _No unreleased changes yet._ @@ -18,7 +18,8 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning - _No unreleased changes yet._ ### Fixed -- _No unreleased changes yet._ +- Custom theme fuzzy matching now accepts selection. +- Custom themes may now be removed from the list. ## [2.6.0] - 2025-10-13 ### Summary diff --git a/code/web/templates/build/_new_deck_additional_themes.html b/code/web/templates/build/_new_deck_additional_themes.html index 190a02e..7c3dda4 100644 --- a/code/web/templates/build/_new_deck_additional_themes.html +++ b/code/web/templates/build/_new_deck_additional_themes.html @@ -75,12 +75,12 @@ {% set reason_code = match.reason if match.reason is defined else match['reason'] %}
{{ matched }} - {% if original and original.casefold() != matched.casefold() %} - (from “{{ original }}”) - {% endif %} - -
- {% endfor %} + {% if original and original.casefold() != matched.casefold() %} + (from "{{ original }}") + {% endif %} + + + {% endfor %} {% if not matches and resolved_labels %} {% for label in resolved_labels %}
@@ -102,7 +102,7 @@
{{ item.input }} - +
{% if item.reason %}
Reason: {{ item.reason|replace('_',' ')|title }}
@@ -113,7 +113,7 @@ {% set suggestion_theme = suggestion.theme if suggestion.theme is defined else suggestion.get('theme') %} {% set suggestion_score = suggestion.score if suggestion.score is defined else suggestion.get('score') %} {% if suggestion_theme %} - {% endif %} From 4a8d71b16b1192761bf080738e1e09ee3d65b8bd Mon Sep 17 00:00:00 2001 From: matt Date: Mon, 13 Oct 2025 15:07:31 -0700 Subject: [PATCH 02/58] feature: add non-basic land types to tagging mechanics --- CHANGELOG.md | 3 +- RELEASE_NOTES_TEMPLATE.md | 9 +- code/deck_builder/phases/phase2_lands_misc.py | 8 +- code/scripts/build_theme_catalog.py | 12 + code/tagging/tagger.py | 51 ++ config/themes/theme_list.json | 496 ++++++++++++------ 6 files changed, 402 insertions(+), 177 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5abdf9..76ed8f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,10 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning ## [Unreleased] ### Summary - Fixed issues with custom themes in the web UI. +- Added non-basic land type tagging (i.e. Caves, Deserts, Gates, etc...) in the tagging module. ### Added -- _No unreleased changes yet._ +- Non-basic land type tagging (i.e. Caves, Deserts, Gates, etc...) in the tagging module. ### Changed - _No unreleased changes yet._ diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index 2bdb2d6..6396484 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -1,14 +1,15 @@ # MTG Python Deckbuilder ${VERSION} -## [Unreleased] ### Summary -- _No unreleased changes yet._ +- Fixed issues with custom themes in the web UI. +- Added non-basic land type tagging (i.e. Caves, Deserts, Gates, etc...) in the tagging module. ### Added -- _No unreleased changes yet._ +- Non-basic land type tagging (i.e. Caves, Deserts, Gates, etc...) in the tagging module. ### Changed - _No unreleased changes yet._ ### Fixed -- _No unreleased changes yet._ +- Custom theme fuzzy matching now accepts selection. +- Custom themes may now be removed from the list. diff --git a/code/deck_builder/phases/phase2_lands_misc.py b/code/deck_builder/phases/phase2_lands_misc.py index f4cf589..a12ce0d 100644 --- a/code/deck_builder/phases/phase2_lands_misc.py +++ b/code/deck_builder/phases/phase2_lands_misc.py @@ -44,8 +44,14 @@ class LandMiscUtilityMixin: return basics = self._basic_land_names() already = set(self.card_library.keys()) - top_n = getattr(bc, 'MISC_LAND_TOP_POOL_SIZE', 30) + top_n = getattr(bc, 'MISC_LAND_TOP_POOL_SIZE', 60) use_full = getattr(bc, 'MISC_LAND_USE_FULL_POOL', False) + if not use_full: + rng = getattr(self, 'rng', None) + pool_multiplier = rng.uniform(1.2, 2.0) if rng else 1.5 + top_n = int(top_n * pool_multiplier) + if getattr(self, 'show_diagnostics', False): + self.output_func(f"[Diagnostics] Misc Step pool size multiplier: {pool_multiplier:.2f}x (base={getattr(bc, 'MISC_LAND_TOP_POOL_SIZE', 60)} → effective={top_n})") effective_n = 999999 if use_full else top_n top_candidates = bu.select_top_land_candidates(df, already, basics, effective_n) # Dynamic EDHREC keep percent diff --git a/code/scripts/build_theme_catalog.py b/code/scripts/build_theme_catalog.py index f638094..43c70ca 100644 --- a/code/scripts/build_theme_catalog.py +++ b/code/scripts/build_theme_catalog.py @@ -730,6 +730,18 @@ def build_catalog(limit: int, verbose: bool) -> Dict[str, Any]: merged = [s for s in merged if s not in special_noise] # If theme is one of the special ones, keep the other if present (no action needed beyond above filter logic). + # Land type theme filtering: Gates/Caves/Spheres are land types, not artifact/token mechanics. + # Rationale: These themes tag specific land cards, creating spurious correlations with artifact/token + # themes when those cards happen to also produce artifacts/tokens (e.g., Tireless Tracker in Gates decks). + # Filter out artifact/token synergies that don't make thematic sense for land-type-matters strategies. + land_type_themes = {"Gates Matter"} + incompatible_with_land_types = { + "Investigate", "Clue Token", "Detective Kindred" + } + if theme in land_type_themes: + merged = [s for s in merged if s not in incompatible_with_land_types] + # For non-land-type themes, don't filter (they can legitimately synergize with these) + if synergy_cap > 0 and len(merged) > synergy_cap: ce_len = len(curated_list) + len([s for s in enforced_list if s not in curated_list]) if ce_len < synergy_cap: diff --git a/code/tagging/tagger.py b/code/tagging/tagger.py index f0d3538..b5543df 100644 --- a/code/tagging/tagger.py +++ b/code/tagging/tagger.py @@ -417,6 +417,8 @@ def _tag_mechanical_themes(df: pd.DataFrame, color: str) -> None: print('\n====================\n') tag_for_bending(df, color) print('\n====================\n') + tag_for_land_types(df, color) + print('\n====================\n') tag_for_web_slinging(df, color) print('\n====================\n') tag_for_tokens(df, color) @@ -4239,6 +4241,55 @@ def tag_for_web_slinging(df: pd.DataFrame, color: str) -> None: logger.error(f'Error tagging Web-Slinging keywords: {str(e)}') raise +### Tag for land types +def tag_for_land_types(df: pd.DataFrame, color: str) -> None: + """Tag card for specific non-basic land types. + + Looks for 'Cave', 'Desert', 'Gate', 'Lair', 'Locus', 'Sphere', 'Urza's' in rules text and applies tags accordingly. + """ + try: + cave_mask = ( + (tag_utils.create_text_mask(df, 'Cave') & ~tag_utils.create_text_mask(df, 'scavenge')) | + tag_utils.create_type_mask(df, 'Cave') + ) + desert_mask = ( + tag_utils.create_text_mask(df, 'Desert') | + tag_utils.create_type_mask(df, 'Desert') + ) + gate_mask = ( + ( + tag_utils.create_text_mask(df, 'Gate') & + ~tag_utils.create_text_mask(df, 'Agate') & + ~tag_utils.create_text_mask(df, 'Legate') & + ~tag_utils.create_text_mask(df, 'Throw widethe Gates') & + ~tag_utils.create_text_mask(df, 'Eternity Gate') & + ~tag_utils.create_text_mask(df, 'Investigates') + ) | + tag_utils.create_text_mask(df, 'Gate card') | + tag_utils.create_type_mask(df, 'Gate') + ) + lair_mask = (tag_utils.create_type_mask(df, 'Lair')) + locus_mask = (tag_utils.create_type_mask(df, 'Locus')) + sphere_mask = ( + (tag_utils.create_text_mask(df, 'Sphere') & ~tag_utils.create_text_mask(df, 'Detention Sphere')) | + tag_utils.create_type_mask(df, 'Sphere')) + urzas_mask = (tag_utils.create_type_mask(df, "Urza's")) + rules = [ + {'mask': cave_mask, 'tags': ['Caves Matter', 'Lands Matter']}, + {'mask': desert_mask, 'tags': ['Deserts Matter', 'Lands Matter']}, + {'mask': gate_mask, 'tags': ['Gates Matter', 'Lands Matter']}, + {'mask': lair_mask, 'tags': ['Lairs Matter', 'Lands Matter']}, + {'mask': locus_mask, 'tags': ['Locus Matter', 'Lands Matter']}, + {'mask': sphere_mask, 'tags': ['Spheres Matter', 'Lands Matter']}, + {'mask': urzas_mask, 'tags': ["Urza's Lands Matter", 'Lands Matter']}, + ] + + tag_utils.tag_with_rules_and_logging(df, rules, 'non-basic land types', color=color, logger=logger) + + except Exception as e: + logger.error(f'Error tagging non-basic land types: {str(e)}') + raise + ## Big Mana def create_big_mana_cost_mask(df: pd.DataFrame) -> pd.Series: """Create a boolean mask for cards with high mana costs or X costs. diff --git a/config/themes/theme_list.json b/config/themes/theme_list.json index fd11271..6b05b89 100644 --- a/config/themes/theme_list.json +++ b/config/themes/theme_list.json @@ -47,9 +47,9 @@ "primary_color": "Black", "secondary_color": "Blue", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", - "Krenko, Tin Street Kingpin - Synergy (Counters Matter)" + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "example_cards": [ "Wall of Roots", @@ -118,9 +118,6 @@ "Yawgmoth, Thran Physician", "Tezzeret's Gambit" ], - "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Infect)" - ], "popularity_bucket": "Common", "editorial_quality": "draft", "description": "Spreads -1/-1 counters for removal, attrition, and loop engines leveraging death & sacrifice triggers. Synergies like Proliferate and Counters Matter reinforce the plan." @@ -155,7 +152,7 @@ "Silverflame Ritual" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -191,8 +188,8 @@ "Jetfire, Ingenious Scientist // Jetfire, Air Guardian" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Voltron)" ], "popularity_bucket": "Rare", @@ -490,7 +487,7 @@ "Cosima, God of the Voyage // The Omenkeel" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Rishkar, Peema Renegade - Synergy (Counters Matter)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -725,8 +722,8 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", - "Yahenni, Undying Partisan - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)" ], "example_cards": [ "Kilnmouth Dragon", @@ -2459,21 +2456,21 @@ "primary_color": "Red", "secondary_color": "Black", "example_commanders": [ - "Edgar, Charmed Groom // Edgar Markov's Coffin", "Old Rutstein", "Kamber, the Plunderer", "Strefan, Maurer Progenitor", - "Anje, Maid of Dishonor" + "Anje, Maid of Dishonor", + "Shilgengar, Sire of Famine" ], "example_cards": [ "Voldaren Estate", "Blood for the Blood God!", - "Edgar, Charmed Groom // Edgar Markov's Coffin", "Old Rutstein", "Transmutation Font", "Glass-Cast Heart", "Voldaren Epicure", - "Font of Agonies" + "Font of Agonies", + "Exsanguinator Cavalry" ], "synergy_commanders": [ "Indoraptor, the Perfect Hybrid - Synergy (Bloodthirst)", @@ -2494,9 +2491,9 @@ "primary_color": "Red", "secondary_color": "Green", "example_commanders": [ - "Edgar, Charmed Groom // Edgar Markov's Coffin - Synergy (Blood Token)", "Old Rutstein - Synergy (Blood Token)", "Kamber, the Plunderer - Synergy (Blood Token)", + "Strefan, Maurer Progenitor - Synergy (Blood Token)", "Etali, Primal Storm - Synergy (Aggro)", "Ragavan, Nimble Pilferer - Synergy (Aggro)" ], @@ -2531,9 +2528,9 @@ "secondary_color": "Green", "example_commanders": [ "Indoraptor, the Perfect Hybrid", - "Edgar, Charmed Groom // Edgar Markov's Coffin - Synergy (Blood Token)", "Old Rutstein - Synergy (Blood Token)", "Kamber, the Plunderer - Synergy (Blood Token)", + "Strefan, Maurer Progenitor - Synergy (Blood Token)", "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)" ], "example_cards": [ @@ -2705,7 +2702,7 @@ "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", "Samut, Voice of Dissent - Synergy (Combat Tricks)", "Naru Meha, Master Wizard - Synergy (Combat Tricks)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -2917,7 +2914,7 @@ "Investigate", "Unconditional Draw", "Sacrifice to Draw", - "Gift" + "Gates Matter" ], "primary_color": "Blue", "secondary_color": "White", @@ -3043,7 +3040,6 @@ "Seasoned Dungeoneer" ], "synergy_commanders": [ - "Kellan, Daring Traveler // Journey On - Synergy (Map Token)", "Selvala, Heart of the Wilds - Synergy (Scout Kindred)" ], "popularity_bucket": "Niche", @@ -3218,6 +3214,43 @@ "editorial_quality": "draft", "description": "Focuses on getting a high number of Cat creatures into play with shared payoffs (e.g., Forestwalk and Vigilance)." }, + { + "id": "caves-matter", + "theme": "Caves Matter", + "synergies": [ + "Discover", + "Land Types Matter", + "Transform", + "Lands Matter", + "Exile Matters" + ], + "primary_color": "Green", + "secondary_color": "Black", + "example_commanders": [ + "Nikara, Lair Scavenger", + "Pantlaza, Sun-Favored - Synergy (Discover)", + "Caparocti Sunborn - Synergy (Discover)", + "Ellie and Alan, Paleontologists - Synergy (Discover)", + "Vorinclex // The Grand Evolution - Synergy (Land Types Matter)" + ], + "example_cards": [ + "Gemstone Caverns", + "Urza's Cave", + "Spelunking", + "Brass's Tunnel-Grinder // Tecutlan, the Searing Rift", + "Volatile Fault", + "Echoing Deeps", + "Promising Vein", + "Hidden Nursery" + ], + "synergy_commanders": [ + "Karametra, God of Harvests - Synergy (Land Types Matter)", + "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Transform)" + ], + "popularity_bucket": "Rare", + "editorial_quality": "draft", + "description": "Builds around Caves Matter leveraging synergies with Discover and Land Types Matter." + }, { "id": "celebration", "theme": "Celebration", @@ -3426,7 +3459,7 @@ "synergy_commanders": [ "Codsworth, Handy Helper - Synergy (Mana Rock)", "Karn, Legacy Reforged - Synergy (Mana Rock)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Rishkar, Peema Renegade - Synergy (Counters Matter)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -3821,10 +3854,10 @@ "theme": "Clue Token", "synergies": [ "Investigate", + "Gates Matter", "Detective Kindred", "Sacrifice to Draw", - "Artifact Tokens", - "Cantrips" + "Artifact Tokens" ], "primary_color": "Blue", "secondary_color": "White", @@ -3848,8 +3881,7 @@ "synergy_commanders": [ "Tivit, Seller of Secrets - Synergy (Investigate)", "Kellan, Inquisitive Prodigy // Tail the Suspect - Synergy (Investigate)", - "Nelly Borca, Impulsive Accuser - Synergy (Detective Kindred)", - "Braids, Arisen Nightmare - Synergy (Sacrifice to Draw)" + "Nelly Borca, Impulsive Accuser - Synergy (Detective Kindred)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -4402,9 +4434,9 @@ "secondary_color": "Green", "example_commanders": [ "Ixhel, Scion of Atraxa", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Poison Counters)", "Skrelv, Defector Mite - Synergy (Poison Counters)", "Skithiryx, the Blight Dragon - Synergy (Poison Counters)", + "Fynn, the Fangbearer - Synergy (Poison Counters)", "Yawgmoth, Thran Physician - Synergy (Infect)" ], "example_cards": [ @@ -4543,11 +4575,11 @@ "primary_color": "Green", "secondary_color": "White", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness", "Rishkar, Peema Renegade", "Krenko, Tin Street Kingpin", "Yawgmoth, Thran Physician", - "Yahenni, Undying Partisan" + "Yahenni, Undying Partisan", + "Heliod, Sun-Crowned" ], "example_cards": [ "The One Ring", @@ -4849,9 +4881,9 @@ "example_commanders": [ "The Pride of Hull Clade", "Kalakscion, Hunger Tyrant", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", - "Krenko, Tin Street Kingpin - Synergy (Counters Matter)" + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "example_cards": [ "The Pride of Hull Clade", @@ -4864,8 +4896,8 @@ "Algae Gharial" ], "synergy_commanders": [ - "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", "Yahenni, Undying Partisan - Synergy (+1/+1 Counters)", + "Heliod, Sun-Crowned - Synergy (+1/+1 Counters)", "Azusa, Lost but Seeking - Synergy (Toughness Matters)" ], "popularity_bucket": "Rare", @@ -4902,8 +4934,8 @@ "Elephant Grass" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Enchantments Matter)" ], "popularity_bucket": "Niche", @@ -5450,8 +5482,8 @@ "Azusa, Lost but Seeking - Synergy (Lands Matter)", "Tatyova, Benthic Druid - Synergy (Lands Matter)", "Sheoldred, Whispering One - Synergy (Lands Matter)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", - "Rishkar, Peema Renegade - Synergy (Counters Matter)" + "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)" ], "example_cards": [ "Sandstone Needle", @@ -5514,6 +5546,45 @@ "editorial_quality": "draft", "description": "Focuses on getting a high number of Deserter creatures into play with shared payoffs." }, + { + "id": "deserts-matter", + "theme": "Deserts Matter", + "synergies": [ + "Land Types Matter", + "Lands Matter", + "Cycling", + "Loot", + "Ramp" + ], + "primary_color": "Green", + "secondary_color": "Red", + "example_commanders": [ + "Hazezon, Shaper of Sand", + "Yuma, Proud Protector", + "Sophina, Spearsage Deserter", + "Vorinclex // The Grand Evolution - Synergy (Land Types Matter)", + "Karametra, God of Harvests - Synergy (Land Types Matter)" + ], + "example_cards": [ + "Scavenger Grounds", + "Arid Archway", + "Conduit Pylons", + "Lazotep Quarry", + "Survivors' Encampment", + "Desert of the Fervent", + "Hour of Promise", + "Desert of the True" + ], + "synergy_commanders": [ + "Titania, Nature's Force - Synergy (Land Types Matter)", + "Azusa, Lost but Seeking - Synergy (Lands Matter)", + "Tatyova, Benthic Druid - Synergy (Lands Matter)", + "The Balrog of Moria - Synergy (Cycling)" + ], + "popularity_bucket": "Rare", + "editorial_quality": "draft", + "description": "Builds around Deserts Matter leveraging synergies with Land Types Matter and Lands Matter." + }, { "id": "detain", "theme": "Detain", @@ -5557,8 +5628,8 @@ "Collect evidence", "Investigate", "Clue Token", - "Disguise", - "Sacrifice to Draw" + "Gates Matter", + "Disguise" ], "primary_color": "Blue", "secondary_color": "White", @@ -5605,7 +5676,7 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "example_cards": [ "Scourge of the Throne", @@ -5618,7 +5689,7 @@ "Marchesa's Emissary" ], "synergy_commanders": [ - "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Voltron)" ], "popularity_bucket": "Rare", @@ -5727,7 +5798,7 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "example_cards": [ "Mycoloth", @@ -5740,7 +5811,7 @@ "Voracious Dragon" ], "synergy_commanders": [ - "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Voltron)" ], "popularity_bucket": "Rare", @@ -5829,11 +5900,11 @@ "id": "discover", "theme": "Discover", "synergies": [ + "Caves Matter", "Land Types Matter", "Exile Matters", "Topdeck", - "Lands Matter", - "Big Mana" + "Lands Matter" ], "primary_color": "Red", "secondary_color": "Green", @@ -5842,7 +5913,7 @@ "Caparocti Sunborn", "Ellie and Alan, Paleontologists", "Aloy, Savior of Meridian", - "Vorinclex // The Grand Evolution - Synergy (Land Types Matter)" + "Nikara, Lair Scavenger - Synergy (Caves Matter)" ], "example_cards": [ "Chimil, the Inner Sun", @@ -5855,11 +5926,11 @@ "Hidden Volcano" ], "synergy_commanders": [ + "Gemstone Caverns - Synergy (Caves Matter)", + "Brass's Tunnel-Grinder // Tecutlan, the Searing Rift - Synergy (Caves Matter)", + "Vorinclex // The Grand Evolution - Synergy (Land Types Matter)", "Karametra, God of Harvests - Synergy (Land Types Matter)", - "Titania, Nature's Force - Synergy (Land Types Matter)", - "Etali, Primal Storm - Synergy (Exile Matters)", - "Ragavan, Nimble Pilferer - Synergy (Exile Matters)", - "The Reality Chip - Synergy (Topdeck)" + "Etali, Primal Storm - Synergy (Exile Matters)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -6652,7 +6723,7 @@ "Plargg and Nassari", "Yusri, Fortune's Flame", "Najal, the Storm Runner", - "Uvilda, Dean of Perfection // Nassari, Dean of Expression" + "Niv-Mizzet, Parun - Synergy (Flying)" ], "example_cards": [ "Veyran, Voice of Duality", @@ -6661,11 +6732,10 @@ "Najal, the Storm Runner", "Frenetic Efreet", "Efreet Flamepainter", - "Uvilda, Dean of Perfection // Nassari, Dean of Expression", - "Emissary of Grudges" + "Emissary of Grudges", + "Capricious Efreet" ], "synergy_commanders": [ - "Niv-Mizzet, Parun - Synergy (Flying)", "Avacyn, Angel of Hope - Synergy (Flying)", "Old Gnawbone - Synergy (Flying)", "Syr Konrad, the Grim - Synergy (Burn)", @@ -7763,7 +7833,7 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "example_cards": [ "Gyre Sage", @@ -7776,7 +7846,7 @@ "Experiment One" ], "synergy_commanders": [ - "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Voltron)" ], "popularity_bucket": "Rare", @@ -7888,7 +7958,7 @@ "The Indomitable - Synergy (Vehicles)", "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -8027,7 +8097,6 @@ "Nicanzil, Current Conductor" ], "synergy_commanders": [ - "Kellan, Daring Traveler // Journey On - Synergy (Map Token)", "Selvala, Heart of the Wilds - Synergy (Scout Kindred)" ], "popularity_bucket": "Niche", @@ -8154,8 +8223,8 @@ "primary_color": "Green", "secondary_color": "Black", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Enchantments Matter)" ], "example_cards": [ @@ -8183,8 +8252,8 @@ "primary_color": "Green", "secondary_color": "Black", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Enchantments Matter)" ], "example_cards": [ @@ -8461,8 +8530,8 @@ "Syr Konrad, the Grim - Synergy (Mill)", "Emry, Lurker of the Loch - Synergy (Mill)", "Sheoldred, Whispering One - Synergy (Mill)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Six - Synergy (Reanimate)" ], "popularity_bucket": "Rare", @@ -8797,7 +8866,6 @@ ], "synergy_commanders": [ "Otharri, Suns' Glory - Synergy (Phoenix Kindred)", - "Joshua, Phoenix's Dominant // Phoenix, Warden of Fire - Synergy (Phoenix Kindred)", "Syrix, Carrier of the Flame - Synergy (Phoenix Kindred)", "Ezrim, Agency Chief - Synergy (Archon Kindred)", "Krond the Dawn-Clad - Synergy (Archon Kindred)", @@ -9203,8 +9271,8 @@ ], "synergy_commanders": [ "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Voltron)" ], "popularity_bucket": "Rare", @@ -9418,6 +9486,47 @@ "editorial_quality": "draft", "description": "Focuses on getting a high number of Gargoyle creatures into play with shared payoffs (e.g., Flying and Blink)." }, + { + "id": "gates-matter", + "theme": "Gates Matter", + "synergies": [ + "Sacrifice to Draw", + "Artifact Tokens", + "Land Types Matter", + "Lands Matter", + "Cantrips" + ], + "primary_color": "Blue", + "secondary_color": "Green", + "example_commanders": [ + "Tivit, Seller of Secrets", + "Lonis, Cryptozoologist", + "Piper Wright, Publick Reporter", + "Teysa, Opulent Oligarch", + "Nine-Fingers Keene" + ], + "example_cards": [ + "Tireless Tracker", + "Talon Gates of Madara", + "The Black Gate", + "Simic Guildgate", + "Golgari Guildgate", + "Izzet Guildgate", + "Forensic Gadgeteer", + "Dimir Guildgate" + ], + "synergy_commanders": [ + "Braids, Arisen Nightmare - Synergy (Sacrifice to Draw)", + "Peregrin Took - Synergy (Sacrifice to Draw)", + "Sai, Master Thopterist - Synergy (Sacrifice to Draw)", + "Ragavan, Nimble Pilferer - Synergy (Artifact Tokens)", + "Lotho, Corrupt Shirriff - Synergy (Artifact Tokens)", + "Vorinclex // The Grand Evolution - Synergy (Land Types Matter)" + ], + "popularity_bucket": "Uncommon", + "editorial_quality": "draft", + "description": "Builds around Gates Matter leveraging synergies with Sacrifice to Draw and Artifact Tokens." + }, { "id": "germ-kindred", "theme": "Germ Kindred", @@ -9811,7 +9920,7 @@ "Golden Argosy", "King Macar, the Gold-Cursed", "Goldbug, Humanity's Ally // Goldbug, Scrappy Scout", - "Tetzin, Gnome Champion // The Golden-Gear Colossus" + "Raksha Golden Cub" ], "example_cards": [ "Curse of Opulence", @@ -9946,7 +10055,7 @@ "Novijen Sages" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -10344,7 +10453,7 @@ "Ulasht, the Hate Seed - Synergy (Hellion Kindred)", "Thromok the Insatiable - Synergy (Hellion Kindred)", "Otharri, Suns' Glory - Synergy (Phoenix Kindred)", - "Joshua, Phoenix's Dominant // Phoenix, Warden of Fire - Synergy (Phoenix Kindred)" + "Syrix, Carrier of the Flame - Synergy (Phoenix Kindred)" ], "popularity_bucket": "Common", "editorial_quality": "draft", @@ -10518,8 +10627,8 @@ ], "synergy_commanders": [ "Spider-Man, Brooklyn Visionary - Synergy (Web-slinging)", - "Peter Parker // Amazing Spider-Man - Synergy (Web-slinging)", "Spider-Man India - Synergy (Web-slinging)", + "Scarlet Spider, Ben Reilly - Synergy (Web-slinging)", "Arasta of the Endless Web - Synergy (Spider Kindred)" ], "popularity_bucket": "Niche", @@ -11094,9 +11203,9 @@ "primary_color": "Blue", "secondary_color": "Black", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", - "Krenko, Tin Street Kingpin - Synergy (Counters Matter)" + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "example_cards": [ "Dark Depths", @@ -11476,25 +11585,25 @@ "primary_color": "Green", "secondary_color": "Black", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness", "Yawgmoth, Thran Physician", "Skrelv, Defector Mite", "Vorinclex, Monstrous Raider", - "Lae'zel, Vlaakith's Champion" + "Lae'zel, Vlaakith's Champion", + "Tekuthal, Inquiry Dominus" ], "example_cards": [ "Karn's Bastion", "Doubling Season", "Evolution Sage", "Cankerbloom", - "Etali, Primal Conqueror // Etali, Primal Sickness", "Thrummingbird", "Yawgmoth, Thran Physician", - "Tezzeret's Gambit" + "Tezzeret's Gambit", + "Innkeeper's Talent" ], "synergy_commanders": [ "Skithiryx, the Blight Dragon - Synergy (Poison Counters)", - "Tekuthal, Inquiry Dominus - Synergy (Proliferate)", + "Fynn, the Fangbearer - Synergy (Poison Counters)", "Karumonix, the Rat King - Synergy (Toxic)" ], "popularity_bucket": "Uncommon", @@ -11607,7 +11716,7 @@ "Bristly Bill, Spine Sower - Synergy (Landfall)", "Shay Cormac - Synergy (Shroud)", "Eladamri, Lord of Leaves - Synergy (Shroud)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Poison Counters)" + "Skrelv, Defector Mite - Synergy (Poison Counters)" ], "popularity_bucket": "Common", "editorial_quality": "draft", @@ -11733,10 +11842,10 @@ "theme": "Investigate", "synergies": [ "Clue Token", + "Gates Matter", "Detective Kindred", "Sacrifice to Draw", - "Artifact Tokens", - "Cantrips" + "Artifact Tokens" ], "primary_color": "Blue", "secondary_color": "White", @@ -11758,9 +11867,7 @@ "Wojek Investigator" ], "synergy_commanders": [ - "Astrid Peth - Synergy (Clue Token)", - "Nelly Borca, Impulsive Accuser - Synergy (Detective Kindred)", - "Braids, Arisen Nightmare - Synergy (Sacrifice to Draw)" + "Astrid Peth - Synergy (Clue Token)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -12187,8 +12294,8 @@ "Kodama of the West Tree - Synergy (Spirit Kindred)", "Kodama of the East Tree - Synergy (Spirit Kindred)", "Junji, the Midnight Sky - Synergy (Spirit Kindred)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", - "Rishkar, Peema Renegade - Synergy (Counters Matter)" + "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)" ], "example_cards": [ "Petalmane Baku", @@ -12554,7 +12661,7 @@ "Mountaincycling", "Forestcycling", "Swampcycling", - "Discover" + "Spheres Matter" ], "primary_color": "Green", "secondary_color": "White", @@ -12885,8 +12992,8 @@ "primary_color": "Blue", "secondary_color": "White", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Emry, Lurker of the Loch - Synergy (Wizard Kindred)" ], "example_cards": [ @@ -12916,8 +13023,8 @@ "primary_color": "Blue", "secondary_color": "White", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Kutzil, Malamet Exemplar - Synergy (Warrior Kindred)" ], "example_cards": [ @@ -13571,10 +13678,10 @@ "secondary_color": "Black", "example_commanders": [ "Jeska, Thrice Reborn", - "Ral, Monsoon Mage // Ral, Leyline Prodigy", "Commodore Guff", "Mila, Crafty Companion // Lukka, Wayward Bonder", - "Heart of Kiran" + "Heart of Kiran", + "Adeline, Resplendent Cathar - Synergy (Planeswalkers)" ], "example_cards": [ "Spark Double", @@ -13587,12 +13694,11 @@ "Ral, Crackling Wit" ], "synergy_commanders": [ - "Adeline, Resplendent Cathar - Synergy (Planeswalkers)", "Yawgmoth, Thran Physician - Synergy (Planeswalkers)", "Vorinclex, Monstrous Raider - Synergy (Planeswalkers)", "Lae'zel, Vlaakith's Champion - Synergy (Superfriends)", "Tekuthal, Inquiry Dominus - Synergy (Superfriends)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Rishkar, Peema Renegade - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -13875,11 +13981,11 @@ "primary_color": "Blue", "secondary_color": "Green", "example_commanders": [ - "Kellan, Daring Traveler // Journey On", "Hakbal of the Surging Soul - Synergy (Explore)", "Amalia Benavides Aguirre - Synergy (Explore)", "Nicanzil, Current Conductor - Synergy (Explore)", - "Astrid Peth - Synergy (Card Selection)" + "Astrid Peth - Synergy (Card Selection)", + "Francisco, Fowl Marauder - Synergy (Card Selection)" ], "example_cards": [ "Get Lost", @@ -13892,7 +13998,6 @@ "Spyglass Siren" ], "synergy_commanders": [ - "Francisco, Fowl Marauder - Synergy (Card Selection)", "Ragavan, Nimble Pilferer - Synergy (Artifact Tokens)" ], "popularity_bucket": "Rare", @@ -14107,8 +14212,8 @@ "Saryth, the Viper's Fang - Synergy (Warlock Kindred)", "Honest Rutstein - Synergy (Warlock Kindred)", "Breena, the Demagogue - Synergy (Warlock Kindred)", - "Edgar, Charmed Groom // Edgar Markov's Coffin - Synergy (Blood Token)", "Old Rutstein - Synergy (Blood Token)", + "Kamber, the Plunderer - Synergy (Blood Token)", "Ragavan, Nimble Pilferer - Synergy (Pirate Kindred)" ], "popularity_bucket": "Common", @@ -14149,7 +14254,7 @@ "Thalia, Heretic Cathar - Synergy (Soldier Kindred)", "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -14536,8 +14641,8 @@ "Skrelv, Defector Mite", "Vishgraz, the Doomhive", "Ria Ivor, Bane of Bladehold", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Poison Counters)", - "Skithiryx, the Blight Dragon - Synergy (Poison Counters)" + "Skithiryx, the Blight Dragon - Synergy (Poison Counters)", + "Fynn, the Fangbearer - Synergy (Poison Counters)" ], "example_cards": [ "Skrelv, Defector Mite", @@ -14880,7 +14985,7 @@ "Bonny Pall, Clearcutter - Synergy (Giant Kindred)", "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -14942,8 +15047,8 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", - "Yahenni, Undying Partisan - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)" ], "example_cards": [ "Tragic Slip", @@ -15195,7 +15300,7 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "example_cards": [ "Everflowing Chalice", @@ -15208,7 +15313,7 @@ "Bloodhusk Ritualist" ], "synergy_commanders": [ - "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)", "Selvala, Heart of the Wilds - Synergy (Blink)" ], "popularity_bucket": "Rare", @@ -15954,7 +16059,7 @@ "Mondrak, Glory Dominus - Synergy (Phyrexian Kindred)", "Sheoldred, the Apocalypse - Synergy (Phyrexian Kindred)", "Elas il-Kor, Sadistic Pilgrim - Synergy (Phyrexian Kindred)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Rishkar, Peema Renegade - Synergy (Counters Matter)" ], "example_cards": [ "Vat of Rebirth", @@ -15967,7 +16072,7 @@ "Sawblade Scamp" ], "synergy_commanders": [ - "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Ragavan, Nimble Pilferer - Synergy (Artifacts Matter)" ], "popularity_bucket": "Rare", @@ -16024,7 +16129,7 @@ "Sakashima of a Thousand Faces - Synergy (Clones)", "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -16256,8 +16361,8 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", - "Yahenni, Undying Partisan - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)" ], "example_cards": [ "Abzan Falconer", @@ -16824,9 +16929,9 @@ "example_commanders": [ "The War Doctor", "King of the Oathbreakers", - "Cyclonus, the Saboteur // Cyclonus, Cybertronian Fighter", "Taniwha", - "Sram, Senior Edificer - Synergy (Equipment Matters)" + "Sram, Senior Edificer - Synergy (Equipment Matters)", + "Kodama of the West Tree - Synergy (Equipment Matters)" ], "example_cards": [ "Teferi's Protection", @@ -16839,7 +16944,6 @@ "Teferi, Master of Time" ], "synergy_commanders": [ - "Kodama of the West Tree - Synergy (Equipment Matters)", "Danitha Capashen, Paragon - Synergy (Equipment Matters)", "Toski, Bearer of Secrets - Synergy (Protective Effects)", "Mondrak, Glory Dominus - Synergy (Protective Effects)", @@ -16862,23 +16966,22 @@ "primary_color": "Red", "example_commanders": [ "Otharri, Suns' Glory", - "Joshua, Phoenix's Dominant // Phoenix, Warden of Fire", "Syrix, Carrier of the Flame", "Aurelia, the Warleader - Synergy (Haste)", - "Yahenni, Undying Partisan - Synergy (Haste)" + "Yahenni, Undying Partisan - Synergy (Haste)", + "Kiki-Jiki, Mirror Breaker - Synergy (Haste)" ], "example_cards": [ "Otharri, Suns' Glory", "Aurora Phoenix", "Phoenix Chick", "Jaya's Phoenix", - "Joshua, Phoenix's Dominant // Phoenix, Warden of Fire", "Detective's Phoenix", "Flamewake Phoenix", - "Everquill Phoenix" + "Everquill Phoenix", + "Ashcloud Phoenix" ], "synergy_commanders": [ - "Kiki-Jiki, Mirror Breaker - Synergy (Haste)", "Niv-Mizzet, Parun - Synergy (Flying)", "Avacyn, Angel of Hope - Synergy (Flying)", "Rishkar, Peema Renegade - Synergy (Midrange)" @@ -16904,7 +17007,7 @@ "Sheoldred, the Apocalypse", "Elas il-Kor, Sadistic Pilgrim", "Sheoldred, Whispering One", - "Etali, Primal Conqueror // Etali, Primal Sickness" + "Elesh Norn, Grand Cenobite" ], "example_cards": [ "Phyrexian Metamorph", @@ -16939,9 +17042,9 @@ "example_commanders": [ "Baird, Steward of Argive", "Teysa, Envoy of Ghosts", - "Tamiyo, Inquisitive Student // Tamiyo, Seasoned Scholar", "Sivitri, Dragon Master", - "Isperia, Supreme Judge" + "Isperia, Supreme Judge", + "Thantis, the Warweaver" ], "example_cards": [ "Propaganda", @@ -17277,24 +17380,23 @@ "primary_color": "Black", "secondary_color": "Green", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness", "Skrelv, Defector Mite", "Skithiryx, the Blight Dragon", "Fynn, the Fangbearer", - "Karumonix, the Rat King" + "Karumonix, the Rat King", + "Ixhel, Scion of Atraxa" ], "example_cards": [ - "Etali, Primal Conqueror // Etali, Primal Sickness", "Skrelv, Defector Mite", "Triumph of the Hordes", "Vraska, Betrayal's Sting", "White Sun's Twilight", "Skrelv's Hive", "Plague Myr", - "Grafted Exoskeleton" + "Grafted Exoskeleton", + "Vraska's Fall" ], "synergy_commanders": [ - "Ixhel, Scion of Atraxa - Synergy (Toxic)", "Vishgraz, the Doomhive - Synergy (Mite Kindred)" ], "popularity_bucket": "Uncommon", @@ -17565,7 +17667,7 @@ "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Adeline, Resplendent Cathar - Synergy (Planeswalkers)", "Vorinclex, Monstrous Raider - Synergy (Planeswalkers)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -17969,8 +18071,8 @@ "Tatyova, Benthic Druid - Synergy (Landfall)", "Aesi, Tyrant of Gyre Strait - Synergy (Landfall)", "Bristly Bill, Spine Sower - Synergy (Landfall)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Enchantments Matter)" ], "popularity_bucket": "Rare", @@ -18679,8 +18781,8 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", - "Yahenni, Undying Partisan - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)" ], "example_cards": [ "Wren's Run Hydra", @@ -18768,7 +18870,7 @@ "Alchemist's Assistant" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -18804,7 +18906,7 @@ "Valeron Wardens" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -19050,7 +19152,7 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "example_cards": [ "Spider-Punk", @@ -19063,7 +19165,7 @@ "Wrecking Beast" ], "synergy_commanders": [ - "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Voltron)" ], "popularity_bucket": "Rare", @@ -19298,11 +19400,11 @@ "id": "sacrifice-to-draw", "theme": "Sacrifice to Draw", "synergies": [ + "Spheres Matter", "Clue Token", "Investigate", - "Blood Token", - "Detective Kindred", - "Artifact Tokens" + "Gates Matter", + "Blood Token" ], "primary_color": "Black", "secondary_color": "Blue", @@ -19326,10 +19428,7 @@ "synergy_commanders": [ "Lonis, Cryptozoologist - Synergy (Clue Token)", "Astrid Peth - Synergy (Clue Token)", - "Piper Wright, Publick Reporter - Synergy (Clue Token)", - "Tivit, Seller of Secrets - Synergy (Investigate)", - "Kellan, Inquisitive Prodigy // Tail the Suspect - Synergy (Investigate)", - "Edgar, Charmed Groom // Edgar Markov's Coffin - Synergy (Blood Token)" + "Tivit, Seller of Secrets - Synergy (Investigate)" ], "popularity_bucket": "Common", "editorial_quality": "draft", @@ -19441,7 +19540,7 @@ "synergy_commanders": [ "Syr Konrad, the Grim - Synergy (Mill)", "Emry, Lurker of the Loch - Synergy (Mill)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Rishkar, Peema Renegade - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -19550,8 +19649,8 @@ "theme": "Satyr Kindred", "synergies": [ "Ramp", - "Lands Matter", "Sacrifice Matters", + "Lands Matter", "Aristocrats", "Little Fellas" ], @@ -19575,9 +19674,9 @@ "Nessian Wanderer" ], "synergy_commanders": [ - "Tatyova, Benthic Druid - Synergy (Lands Matter)", - "Sheoldred, Whispering One - Synergy (Lands Matter)", - "Syr Konrad, the Grim - Synergy (Sacrifice Matters)" + "Syr Konrad, the Grim - Synergy (Sacrifice Matters)", + "Braids, Arisen Nightmare - Synergy (Sacrifice Matters)", + "Tatyova, Benthic Druid - Synergy (Lands Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -19638,7 +19737,7 @@ "Sewer Shambler" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -20201,8 +20300,8 @@ "synergy_commanders": [ "Anim Pakal, Thousandth Moon - Synergy (Soldier Kindred)", "Thalia, Heretic Cathar - Synergy (Soldier Kindred)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Tatyova, Benthic Druid - Synergy (Lifegain)" ], "popularity_bucket": "Rare", @@ -20445,8 +20544,8 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", - "Yahenni, Undying Partisan - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)" ], "example_cards": [ "Arcbound Slith", @@ -21067,6 +21166,41 @@ "editorial_quality": "draft", "description": "Chains cheap instants & sorceries for velocity—converting triggers into scalable damage or card advantage before a finisher. Synergies like Spells Matter and Prowess reinforce the plan." }, + { + "id": "spheres-matter", + "theme": "Spheres Matter", + "synergies": [ + "Land Types Matter", + "Sacrifice to Draw", + "Lands Matter", + "Card Draw" + ], + "primary_color": "Black", + "secondary_color": "Blue", + "example_commanders": [ + "Vorinclex // The Grand Evolution - Synergy (Land Types Matter)", + "Karametra, God of Harvests - Synergy (Land Types Matter)", + "Titania, Nature's Force - Synergy (Land Types Matter)", + "Braids, Arisen Nightmare - Synergy (Sacrifice to Draw)", + "Peregrin Took - Synergy (Sacrifice to Draw)" + ], + "example_cards": [ + "The Mycosynth Gardens", + "Mirrex", + "Planar Nexus", + "The Seedcore", + "The Fair Basilica", + "The Hunter Maze", + "The Dross Pits", + "The Autonomous Furnace" + ], + "synergy_commanders": [ + "Azusa, Lost but Seeking - Synergy (Lands Matter)" + ], + "popularity_bucket": "Rare", + "editorial_quality": "draft", + "description": "Builds around Spheres Matter leveraging synergies with Land Types Matter and Sacrifice to Draw." + }, { "id": "sphinx-kindred", "theme": "Sphinx Kindred", @@ -21139,8 +21273,8 @@ ], "synergy_commanders": [ "Spider-Man, Brooklyn Visionary - Synergy (Web-slinging)", - "Peter Parker // Amazing Spider-Man - Synergy (Web-slinging)", "Spider-Man India - Synergy (Web-slinging)", + "Scarlet Spider, Ben Reilly - Synergy (Web-slinging)", "Deadpool, Trading Card - Synergy (Hero Kindred)", "G'raha Tia, Scion Reborn - Synergy (Hero Kindred)", "Six - Synergy (Reach)" @@ -21165,8 +21299,8 @@ "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", - "Yahenni, Undying Partisan - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)" ], "example_cards": [ "Spike Feeder", @@ -21652,7 +21786,7 @@ "Bottomless Vault" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Rishkar, Peema Renegade - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -21756,9 +21890,9 @@ "Lulu, Stern Guardian" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", + "Yawgmoth, Thran Physician - Synergy (Counters Matter)", "Kutzil, Malamet Exemplar - Synergy (Stax)", "Lotho, Corrupt Shirriff - Synergy (Stax)", "Baral, Chief of Compliance - Synergy (Loot)" @@ -21834,7 +21968,7 @@ "Shoulder to Shoulder" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Heliod, Sun-Crowned - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -22795,8 +22929,8 @@ "Karumonix, the Rat King" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Poison Counters)", "Skithiryx, the Blight Dragon - Synergy (Poison Counters)", + "Fynn, the Fangbearer - Synergy (Poison Counters)", "Yawgmoth, Thran Physician - Synergy (Infect)", "Vorinclex, Monstrous Raider - Synergy (Infect)", "Mondrak, Glory Dominus - Synergy (Phyrexian Kindred)" @@ -22872,7 +23006,7 @@ "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", "Syr Konrad, the Grim - Synergy (Human Kindred)", "Azusa, Lost but Seeking - Synergy (Human Kindred)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Yahenni, Undying Partisan - Synergy (Counters Matter)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -23568,8 +23702,8 @@ "Hellhole Flailer" ], "synergy_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Yahenni, Undying Partisan - Synergy (Counters Matter)", + "Heliod, Sun-Crowned - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Voltron)" ], "popularity_bucket": "Rare", @@ -23633,9 +23767,9 @@ "Twilight Prophet" ], "synergy_commanders": [ - "Edgar, Charmed Groom // Edgar Markov's Coffin - Synergy (Blood Token)", "Old Rutstein - Synergy (Blood Token)", "Kamber, the Plunderer - Synergy (Blood Token)", + "Strefan, Maurer Progenitor - Synergy (Blood Token)", "Heliod, Sun-Crowned - Synergy (Lifegain Triggers)", "Emrakul, the World Anew - Synergy (Madness)" ], @@ -23658,7 +23792,7 @@ "Ojer Pakpatiq, Deepest Epoch // Temple of Cyclical Time - Synergy (Time Counters)", "The Tenth Doctor - Synergy (Time Counters)", "Jhoira of the Ghitu - Synergy (Time Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" + "Rishkar, Peema Renegade - Synergy (Counters Matter)" ], "example_cards": [ "Dreamtide Whale", @@ -23671,7 +23805,7 @@ "Chronozoa" ], "synergy_commanders": [ - "Rishkar, Peema Renegade - Synergy (Counters Matter)", + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Enchantments Matter)" ], "popularity_bucket": "Rare", @@ -23822,9 +23956,9 @@ "secondary_color": "Green", "example_commanders": [ "Yisan, the Wanderer Bard", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", + "Yawgmoth, Thran Physician - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Enchantments Matter)" ], "example_cards": [ @@ -24231,20 +24365,20 @@ "secondary_color": "Green", "example_commanders": [ "Spider-Man, Brooklyn Visionary", - "Peter Parker // Amazing Spider-Man", "Spider-Man India", "Scarlet Spider, Ben Reilly", - "Silk, Web Weaver" + "Silk, Web Weaver", + "Spider-UK" ], "example_cards": [ "Spider-Sense", "Spider-Man, Brooklyn Visionary", - "Peter Parker // Amazing Spider-Man", "Spider-Man India", "Scarlet Spider, Ben Reilly", "Silk, Web Weaver", "Spider-UK", - "Spiders-Man, Heroic Horde" + "Spiders-Man, Heroic Horde", + "Spider-Man, Web-Slinger" ], "synergy_commanders": [ "Deadpool, Trading Card - Synergy (Hero Kindred)", @@ -24909,9 +25043,9 @@ "primary_color": "White", "secondary_color": "Blue", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", - "Krenko, Tin Street Kingpin - Synergy (Counters Matter)" + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "example_cards": [ "Dwarven Armorer", @@ -24935,9 +25069,9 @@ "primary_color": "Red", "secondary_color": "Black", "example_commanders": [ - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", "Rishkar, Peema Renegade - Synergy (Counters Matter)", - "Krenko, Tin Street Kingpin - Synergy (Counters Matter)" + "Krenko, Tin Street Kingpin - Synergy (Counters Matter)", + "Yawgmoth, Thran Physician - Synergy (Counters Matter)" ], "example_cards": [ "Dwarven Armorer", @@ -25029,7 +25163,7 @@ "Lifelink": 226, "Beast Kindred": 30, "Sloth Kindred": 3, - "Lands Matter": 169, + "Lands Matter": 192, "Gargoyle Kindred": 11, "Protection": 65, "Protection from Color": 95, @@ -25174,6 +25308,7 @@ "Mercenary Kindred": 14, "-1/-1 Counters": 27, "Clue Token": 22, + "Gates Matter": 22, "Investigate": 20, "Sacrifice to Draw": 26, "Infect": 35, @@ -25253,6 +25388,7 @@ "Extort": 4, "Pingers": 19, "Mite Kindred": 7, + "Caves Matter": 2, "Radiance": 4, "Myriad": 5, "Treasure": 11, @@ -25301,6 +25437,7 @@ "Unity Counters": 1, "Licid Kindred": 2, "Camel Kindred": 5, + "Deserts Matter": 7, "Warlock Kindred": 6, "Lhurgoyf Kindred": 1, "Devour": 1, @@ -25532,6 +25669,7 @@ "Tempting offer": 2, "Collect evidence": 1, "Enlightened Counters": 1, + "Spheres Matter": 1, "Time Travel": 2, "Currency Counters": 1, "Trap Counters": 1, @@ -25618,7 +25756,7 @@ "X Spells": 194, "Artifacts Matter": 621, "Equipment Matters": 90, - "Lands Matter": 198, + "Lands Matter": 233, "Conditional Draw": 196, "Defender": 69, "Draw Triggers": 171, @@ -25820,6 +25958,7 @@ "Buyback": 9, "Cases Matter": 3, "Clue Token": 29, + "Gates Matter": 35, "Investigate": 30, "Knight Kindred": 19, "Shred Counters": 1, @@ -25890,6 +26029,7 @@ "Mercenary Kindred": 2, "Skeleton Kindred": 3, "Dreadnought Kindred": 1, + "Deserts Matter": 4, "Ascend": 7, "Miracle": 3, "Sliver Kindred": 16, @@ -25964,6 +26104,7 @@ "Efreet Kindred": 4, "Horsemanship": 7, "Demon Kindred": 2, + "Caves Matter": 3, "Discover": 3, "Tide Counters": 2, "Camarid Kindred": 1, @@ -26085,6 +26226,7 @@ "Juggernaut Kindred": 1, "Thalakos Kindred": 7, "Knowledge Counters": 1, + "Spheres Matter": 1, "Sponge Kindred": 2, "Minion Kindred": 1, "Rejection Counters": 1, @@ -26143,7 +26285,7 @@ "Cycling": 48, "Discard Matters": 225, "Landcycling": 2, - "Lands Matter": 193, + "Lands Matter": 204, "Loot": 75, "Ramp": 60, "Eldrazi Kindred": 31, @@ -26340,6 +26482,7 @@ "Storm": 3, "Horse Kindred": 9, "Cat Kindred": 16, + "Gates Matter": 13, "Land Types Matter": 36, "Protection": 26, "Protection from Color": 27, @@ -26492,6 +26635,7 @@ "Delirium": 15, "Affinity": 3, "Despair Counters": 1, + "Deserts Matter": 4, "Peasant Kindred": 6, "Bear Kindred": 1, "Verse Counters": 2, @@ -26555,6 +26699,7 @@ "Inspired": 5, "Clue Token": 7, "\\+0/\\+2 Counters": 1, + "Caves Matter": 5, "Recover": 3, "Max speed": 6, "Start your engines!": 8, @@ -26676,6 +26821,7 @@ "Ripple": 1, "Tempting offer": 1, "Prey Counters": 1, + "Spheres Matter": 1, "Firebending": 1, "Necrodermis Counters": 1, "Varmint Kindred": 1, @@ -26744,7 +26890,7 @@ "Big Mana": 1216, "Stax": 320, "Theft": 129, - "Lands Matter": 250, + "Lands Matter": 264, "Control": 141, "Historics Matter": 308, "Legends Matter": 308, @@ -26972,6 +27118,7 @@ "Shark Kindred": 4, "Mouse Kindred": 9, "Indestructible": 17, + "Caves Matter": 5, "Discover": 9, "Card Selection": 2, "Explore": 1, @@ -27004,6 +27151,7 @@ "Mountainwalk": 14, "Mana Rock": 18, "Cases Matter": 2, + "Deserts Matter": 7, "Cost Scaling": 4, "Modal": 4, "Spree": 4, @@ -27025,6 +27173,7 @@ "Pillowfort": 3, "Clown Kindred": 5, "Radiance": 4, + "Gates Matter": 9, "Noble Kindred": 13, "Monkey Kindred": 6, "Toy Kindred": 3, @@ -27235,6 +27384,7 @@ "Synth Kindred": 1, "Vigilance": 2, "Tempting offer": 2, + "Spheres Matter": 1, "Read Ahead": 2, "Summon": 1, "Slug Kindred": 1, @@ -27285,7 +27435,7 @@ "Cumulative upkeep": 15, "Elemental Kindred": 158, "Card Draw": 351, - "Lands Matter": 612, + "Lands Matter": 633, "Topdeck": 256, "Unconditional Draw": 152, "Auras": 243, @@ -27540,6 +27690,7 @@ "Junk Tokens": 2, "Domain": 18, "Clue Token": 16, + "Gates Matter": 26, "Investigate": 16, "Sacrifice to Draw": 31, "Evoke": 5, @@ -27557,6 +27708,7 @@ "Hyena Kindred": 2, "Morbid": 12, "Rogue Kindred": 25, + "Deserts Matter": 15, "Blitz": 4, "Citizen Kindred": 26, "Myriad": 5, @@ -27602,6 +27754,7 @@ "Corrupted": 5, "Discover": 5, "Myr Kindred": 1, + "Caves Matter": 6, "Exalted": 2, "Monarch": 5, "Suspend": 12, @@ -27795,6 +27948,7 @@ "Ascend": 2, "Hatching Counters": 1, "Gold Token": 1, + "Spheres Matter": 1, "Read Ahead": 2, "Banding": 1, "Meld": 1, @@ -27816,12 +27970,12 @@ "generated_from": "merge (analytics + curated YAML + whitelist)", "metadata_info": { "mode": "merge", - "generated_at": "2025-10-13T16:39:38", - "curated_yaml_files": 739, + "generated_at": "2025-10-13T21:59:09", + "curated_yaml_files": 743, "synergy_cap": 5, "inference": "pmi", "version": "phase-b-merge-v1", - "catalog_hash": "7f0baf0c30adae86d1141ba1a6c43d3ffaa2eedbe0000f9c6b20891c909616cb" + "catalog_hash": "7a3351aa971784571236efe12893bb224ae7bb7fb61112dffd660842c23fd3c9" }, "description_fallback_summary": null } \ No newline at end of file From bf60da89cda2f2fb2ab3bdc899e5bb4f9458d3da Mon Sep 17 00:00:00 2001 From: matt Date: Mon, 13 Oct 2025 16:02:12 -0700 Subject: [PATCH 03/58] bugfix: removed owned bade on alternatives and added a specific close button to remove any confusion --- CHANGELOG.md | 3 ++- RELEASE_NOTES_TEMPLATE.md | 3 ++- code/web/templates/build/_alternatives.html | 9 +++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76ed8f8..2bd1696 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,10 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning ### Added - Non-basic land type tagging (i.e. Caves, Deserts, Gates, etc...) in the tagging module. +- Close button to alternatives panel header so it can be dismissed. ### Changed -- _No unreleased changes yet._ +- Removed the owned badge from each alternative and moved owned metadata to a data attribute on the button. ### Fixed - Custom theme fuzzy matching now accepts selection. diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index 6396484..2b76d59 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -6,9 +6,10 @@ ### Added - Non-basic land type tagging (i.e. Caves, Deserts, Gates, etc...) in the tagging module. +- Close button to alternatives panel header so it can be dismissed. ### Changed -- _No unreleased changes yet._ +- Removed the owned badge from each alternative and moved owned metadata to a data attribute on the button. ### Fixed - Custom theme fuzzy matching now accepts selection. diff --git a/code/web/templates/build/_alternatives.html b/code/web/templates/build/_alternatives.html index 025c6af..ab4ccdd 100644 --- a/code/web/templates/build/_alternatives.html +++ b/code/web/templates/build/_alternatives.html @@ -5,7 +5,10 @@ #}
- Alternatives +
+ Alternatives + +
{% set toggle_q = '0' if require_owned else '1' %} {% set toggle_label = 'Owned only: On' if require_owned else 'Owned only: Off' %}
@@ -21,16 +24,14 @@ {% else %}
    {% for it in items %} - {% set badge = '✔' if it.owned else '✖' %} - {% set title = 'Owned' if it.owned else 'Not owned' %} {% set tags = (it.tags or []) %}
  • - {{ badge }}
{% endif %} {% endif %} + {% include "build/_new_deck_skip_controls.html" %}
Advanced options (ideals)
@@ -222,15 +223,40 @@ {% endfor %}
-
diff --git a/code/web/templates/build/_quick_build_progress.html b/code/web/templates/build/_quick_build_progress.html new file mode 100644 index 0000000..415b2aa --- /dev/null +++ b/code/web/templates/build/_quick_build_progress.html @@ -0,0 +1,16 @@ +{# Quick Build Progress Indicator - Current Stage + Completed List #} + +
+
+ {% include "build/_quick_build_progress_content.html" %} +
+ + {# Auto-polling with HTMX - updates content while running, stops when Step 5 returned #} + +
diff --git a/code/web/templates/build/_quick_build_progress_content.html b/code/web/templates/build/_quick_build_progress_content.html new file mode 100644 index 0000000..7669f24 --- /dev/null +++ b/code/web/templates/build/_quick_build_progress_content.html @@ -0,0 +1,15 @@ +{# Quick Build Progress Content (inner content only, for HTMX updates) #} +
+

Automatic Build in Progress

+

Building your deck automatically without approval steps...

+
+ +{# Simplified Phase Indicator #} +
+
Current Phase
+
{{ current_stage }}
+
+ +
+

This may take 10-30 seconds depending on deck complexity...

+
diff --git a/docker-compose.yml b/docker-compose.yml index 5a47ef9..9f738e2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -100,6 +100,9 @@ services: WEB_TAG_PARALLEL: "1" # 1=parallelize tagging WEB_TAG_WORKERS: "4" # Worker count when parallel tagging + # Build Stage Ordering + WEB_STAGE_ORDER: "new" # new|legacy. 'new' (default): creatures → spells → lands → fill. 'legacy': lands → creatures → spells → fill + # Tagging Refinement Feature Flags TAG_NORMALIZE_KEYWORDS: "1" # 1=normalize keywords & filter specialty mechanics (recommended) TAG_PROTECTION_GRANTS: "1" # 1=Protection tag only for cards granting shields (recommended) diff --git a/dockerhub-docker-compose.yml b/dockerhub-docker-compose.yml index ba712e4..4b7eb69 100644 --- a/dockerhub-docker-compose.yml +++ b/dockerhub-docker-compose.yml @@ -102,6 +102,9 @@ services: WEB_TAG_PARALLEL: "1" # 1=parallelize tagging WEB_TAG_WORKERS: "4" # Worker count when parallel tagging + # Build Stage Ordering + WEB_STAGE_ORDER: "new" # new|legacy. 'new' (default): creatures → spells → lands → fill. 'legacy': lands → creatures → spells → fill + # Tagging Refinement Feature Flags TAG_NORMALIZE_KEYWORDS: "1" # 1=normalize keywords & filter specialty mechanics (recommended) TAG_PROTECTION_GRANTS: "1" # 1=Protection tag only for cards granting shields (recommended) From 35bff901d26600ae5173f5392fe56c436c1c6505 Mon Sep 17 00:00:00 2001 From: matt Date: Tue, 14 Oct 2025 16:45:49 -0700 Subject: [PATCH 06/58] feat: add ideal counts slider UI with smart validation --- .env.example | 3 + CHANGELOG.md | 12 ++ DOCKER.md | 1 + README.md | 1 + RELEASE_NOTES_TEMPLATE.md | 9 +- code/web/app.py | 1 + code/web/routes/build.py | 11 ++ code/web/static/styles.css | 49 +++++++ .../web/templates/build/_new_deck_ideals.html | 124 ++++++++++++++++++ code/web/templates/build/_new_deck_modal.html | 11 +- docker-compose.yml | 3 + dockerhub-docker-compose.yml | 3 + 12 files changed, 217 insertions(+), 11 deletions(-) create mode 100644 code/web/templates/build/_new_deck_ideals.html diff --git a/.env.example b/.env.example index d9c19cd..c01736e 100644 --- a/.env.example +++ b/.env.example @@ -96,6 +96,9 @@ WEB_AUTO_ENFORCE=0 # dockerhub: WEB_AUTO_ENFORCE="0" # Build Stage Ordering WEB_STAGE_ORDER=new # new|legacy. 'new' (default): creatures → spells → lands → fill. 'legacy': lands → creatures → spells → fill +# Ideals UI Mode +WEB_IDEALS_UI=slider # input|slider. 'slider' (default): range sliders with live value display. 'input': text input boxes + # Tagging Refinement Feature Flags TAG_NORMALIZE_KEYWORDS=1 # dockerhub: TAG_NORMALIZE_KEYWORDS="1" # Normalize keywords & filter specialty mechanics TAG_PROTECTION_GRANTS=1 # dockerhub: TAG_PROTECTION_GRANTS="1" # Protection tag only for cards granting shields diff --git a/CHANGELOG.md b/CHANGELOG.md index 860dcb4..f0c04eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,18 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning ## [Unreleased] ### Summary - Enhanced deck building workflow with improved stage ordering, granular skip controls, and one-click Quick Build automation. +- New Ideal Counts section with interactive sliders or text inputs for customizing deck composition targets. - Stage execution order now prioritizes creatures and spells before lands for better mana curve analysis. - New wizard-only skip controls allow auto-advancing through specific stages (lands, creatures, spells) without approval prompts. - Quick Build button provides one-click full automation with clean 5-phase progress indicator. ### Added +- **Ideal Counts UI**: Dedicated section in New Deck wizard for setting ideal card counts (ramp, lands, creatures, removal, wipes, card advantage, protection). + - **Slider Mode** (default): Interactive range sliders with live value display and expanded ranges (e.g., creatures: 0-70, lands: 25-45). + - **Input Mode**: Text input boxes with placeholder defaults (e.g., "10 (Default)"). + - Smart validation warns when estimated total exceeds 99 cards (accounts for overlap: `Lands + Creatures + Spells/2`). + - Sliders start at recommended defaults and remember user preferences across builds. + - Configurable via `WEB_IDEALS_UI` environment variable (`slider` or `input`). - **Quick Build**: One-click automation button in New Deck wizard with live progress tracking (5 phases: Creatures, Spells, Lands, Final Touches, Summary). - **Skip Controls**: Granular stage-skipping toggles in New Deck wizard (21 flags: all land steps, creature stages, spell categories). - Individual land step controls: basics, staples, fetches, duals, triomes, kindred, misc lands. @@ -24,9 +31,12 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning - **Stage Reordering**: New default build order executes creatures → spells → lands for improved pip analysis (configurable via `WEB_STAGE_ORDER` environment variable). - Background task execution for Quick Build with HTMX polling progress updates. - Mobile-friendly Quick Build with touch device confirmation dialog. +- Commander session cleanup: Commander selection automatically cleared after build completes. ### Changed - **Default Stage Order**: Creatures and ideal spells now execute before land stages (lands can analyze actual pip requirements instead of estimates). +- **Ideal Counts Display**: Removed collapsible "Advanced options (ideals)" section; replaced with prominent fieldset with slider/input modes. +- Slider ranges expanded to support edge-case strategies (e.g., creature-heavy tribal, spell-heavy control). - Skip controls only available in New Deck wizard (disabled during build execution for consistency). - Skip behavior auto-advances through stages without approval prompts (cards still added, just not gated). - Post-spell land adjustment automatically skipped when any skip flag enabled. @@ -35,6 +45,8 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning - Session context properly injected into Quick Build so skip configuration works correctly. - HTMX polling uses continuous trigger (`every 500ms`) instead of one-time (`load delay`) for reliable progress updates. - Progress indicator stops cleanly when build completes (out-of-band swap removes poller div). +- Ideal counts now properly populate from session state, allowing sliders to start at defaults and remember user preferences. +- Commander and commander_name cleared from session after build completes to prevent carryover to next build. ## [2.6.1] - 2025-10-13 ### Summary diff --git a/DOCKER.md b/DOCKER.md index 6447c0c..9ce253d 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -255,6 +255,7 @@ See `.env.example` for the full catalog. Common knobs: | `SHOW_MUST_HAVE_BUTTONS` | `0` | Surface the must include/exclude buttons and quick-add UI (requires `ALLOW_MUST_HAVES=1`). | | `THEME` | `dark` | Initial UI theme (`system`, `light`, or `dark`). | | `WEB_STAGE_ORDER` | `new` | Build stage execution order: `new` (creatures→spells→lands) or `legacy` (lands→creatures→spells). | +| `WEB_IDEALS_UI` | `slider` | Ideal counts interface: `slider` (range inputs with live validation) or `input` (text boxes with placeholders). | ### Random build controls diff --git a/README.md b/README.md index 6e5b757..e12e294 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Start here for interactive deck creation. - `ALLOW_MUST_HAVES=1` (default) enables include/exclude enforcement. - `WEB_AUTO_ENFORCE=1` re-runs bracket enforcement automatically after each build. - `WEB_STAGE_ORDER=new` (default) runs creatures/spells before lands for better pip analysis. Use `legacy` for original lands-first order. +- `WEB_IDEALS_UI=slider` (default) shows interactive range sliders for ideal counts with live validation. Use `input` for traditional text boxes. ### Run a JSON Config Execute saved configs without manual input. diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index 37db04c..6792d98 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -1,10 +1,11 @@ # MTG Python Deckbuilder ${VERSION} ### Summary -- Enhanced deck building workflow with improved stage ordering, granular skip controls, and one-click Quick Build automation. +- Enhanced deck building workflow with improved stage ordering, granular skip controls, one-click Quick Build automation, and interactive Ideal Counts UI. - Stage execution order now prioritizes creatures and spells before lands for better mana curve analysis. - New wizard-only skip controls allow auto-advancing through specific stages (lands, creatures, spells) without approval prompts. - Quick Build button provides one-click full automation with clean 5-phase progress indicator. +- Ideal Counts now feature interactive slider UI with live validation and smart overlap detection. ### Added - **Quick Build**: One-click automation button in New Deck wizard with live progress tracking (5 phases: Creatures, Spells, Lands, Final Touches, Summary). @@ -14,6 +15,12 @@ - Creature stage controls: all creatures, primary, secondary, fill. - Mutual exclusivity enforcement: "Skip All Lands" disables individual land toggles; "Skip to Misc Lands" skips early land steps. - **Stage Reordering**: New default build order executes creatures → spells → lands for improved pip analysis (configurable via `WEB_STAGE_ORDER` environment variable). +- **Ideal Counts UI**: Interactive slider interface with live value display and smart validation (configurable via `WEB_IDEALS_UI` environment variable). + - Slider Mode (default): Range sliders for all ideal counts with expanded ranges (creatures: 0-70, lands: 25-45). + - Input Mode: Traditional text inputs with placeholder defaults showing recommended values. + - Smart Validation: Real-time deck size estimation using overlap-aware calculation (`Lands + Creatures + Spells/2`). + - Visual Warnings: Red alert (>99 cards), orange warning (90-99), no warning (<90). + - Session Persistence: Values persist across builds and initialize at defaults on first wizard load. - Background task execution for Quick Build with HTMX polling progress updates. - Mobile-friendly Quick Build with touch device confirmation dialog. diff --git a/code/web/app.py b/code/web/app.py index afdfc49..3c17093 100644 --- a/code/web/app.py +++ b/code/web/app.py @@ -128,6 +128,7 @@ ENABLE_PRESETS = _as_bool(os.getenv("ENABLE_PRESETS"), False) ALLOW_MUST_HAVES = _as_bool(os.getenv("ALLOW_MUST_HAVES"), True) SHOW_MUST_HAVE_BUTTONS = _as_bool(os.getenv("SHOW_MUST_HAVE_BUTTONS"), False) ENABLE_CUSTOM_THEMES = _as_bool(os.getenv("ENABLE_CUSTOM_THEMES"), True) +WEB_IDEALS_UI = os.getenv("WEB_IDEALS_UI", "slider").strip().lower() # 'input' or 'slider' ENABLE_PARTNER_MECHANICS = _as_bool(os.getenv("ENABLE_PARTNER_MECHANICS"), True) ENABLE_PARTNER_SUGGESTIONS = _as_bool(os.getenv("ENABLE_PARTNER_SUGGESTIONS"), True) RANDOM_MODES = _as_bool(os.getenv("RANDOM_MODES"), True) # initial snapshot (legacy) diff --git a/code/web/routes/build.py b/code/web/routes/build.py index 944c075..a3fca96 100644 --- a/code/web/routes/build.py +++ b/code/web/routes/build.py @@ -13,6 +13,7 @@ from ..app import ( _sanitize_theme, ENABLE_PARTNER_MECHANICS, ENABLE_PARTNER_SUGGESTIONS, + WEB_IDEALS_UI, ) from ..services.build_utils import ( step5_base_ctx, @@ -1356,6 +1357,7 @@ async def build_new_modal(request: Request) -> HTMLResponse: "allow_must_haves": ALLOW_MUST_HAVES, # Add feature flag "show_must_have_buttons": SHOW_MUST_HAVE_BUTTONS, "enable_custom_themes": ENABLE_CUSTOM_THEMES, + "ideals_ui_mode": WEB_IDEALS_UI, # 'input' or 'slider' "form": { "prefer_combos": bool(sess.get("prefer_combos")), "combo_count": sess.get("combo_target_count"), @@ -1364,6 +1366,15 @@ async def build_new_modal(request: Request) -> HTMLResponse: "use_owned_only": bool(sess.get("use_owned_only")), "prefer_owned": bool(sess.get("prefer_owned")), "swap_mdfc_basics": bool(sess.get("swap_mdfc_basics")), + # Add ideal values from session (will be None on first load, triggering defaults) + "ramp": sess.get("ideals", {}).get("ramp"), + "lands": sess.get("ideals", {}).get("lands"), + "basic_lands": sess.get("ideals", {}).get("basic_lands"), + "creatures": sess.get("ideals", {}).get("creatures"), + "removal": sess.get("ideals", {}).get("removal"), + "wipes": sess.get("ideals", {}).get("wipes"), + "card_advantage": sess.get("ideals", {}).get("card_advantage"), + "protection": sess.get("ideals", {}).get("protection"), }, "tag_slot_html": None, } diff --git a/code/web/static/styles.css b/code/web/static/styles.css index 6992feb..4c610c3 100644 --- a/code/web/static/styles.css +++ b/code/web/static/styles.css @@ -678,3 +678,52 @@ img.lqip.loaded { filter: blur(0); opacity: 1; } 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } + +/* Ideals Slider Styling */ +.ideals-slider { + -webkit-appearance: none; + appearance: none; + height: 6px; + background: var(--border); + border-radius: 3px; + outline: none; +} + +.ideals-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 18px; + height: 18px; + background: var(--ring); + border-radius: 50%; + cursor: pointer; + transition: all 0.15s ease; +} + +.ideals-slider::-webkit-slider-thumb:hover { + transform: scale(1.15); + box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.2); +} + +.ideals-slider::-moz-range-thumb { + width: 18px; + height: 18px; + background: var(--ring); + border: none; + border-radius: 50%; + cursor: pointer; + transition: all 0.15s ease; +} + +.ideals-slider::-moz-range-thumb:hover { + transform: scale(1.15); + box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.2); +} + +.slider-value { + display: inline-block; + padding: 0.25rem 0.5rem; + background: var(--panel); + border: 1px solid var(--border); + border-radius: 4px; +} diff --git a/code/web/templates/build/_new_deck_ideals.html b/code/web/templates/build/_new_deck_ideals.html new file mode 100644 index 0000000..38304c8 --- /dev/null +++ b/code/web/templates/build/_new_deck_ideals.html @@ -0,0 +1,124 @@ +
+ Ideal Counts +
+ Sliders start at recommended defaults. Adjust as needed for your deck strategy. + +
+
+ {% set use_sliders = ideals_ui_mode == 'slider' %} + {% set ideals_data = [ + ('ramp', labels.ramp, defaults.ramp, 0, 30), + ('lands', labels.lands, defaults.lands, 25, 45), + ('basic_lands', labels.basic_lands, defaults.basic_lands, 0, 40), + ('creatures', labels.creatures, defaults.creatures, 0, 70), + ('removal', labels.removal, defaults.removal, 0, 30), + ('wipes', labels.wipes, defaults.wipes, 0, 15), + ('card_advantage', labels.card_advantage, defaults.card_advantage, 0, 30), + ('protection', labels.protection, defaults.protection, 0, 20) + ] %} + + {% for field_name, field_label, default_val, min_val, max_val in ideals_data %} +
+ +
+ {% endfor %} +
+ + +
diff --git a/code/web/templates/build/_new_deck_modal.html b/code/web/templates/build/_new_deck_modal.html index 3aaee52..0ae200e 100644 --- a/code/web/templates/build/_new_deck_modal.html +++ b/code/web/templates/build/_new_deck_modal.html @@ -110,6 +110,7 @@
+ {% include "build/_new_deck_ideals.html" %} {% if allow_must_haves %}
Include/Exclude Cards @@ -213,16 +214,6 @@ {% endif %} {% endif %} {% include "build/_new_deck_skip_controls.html" %} -
- Advanced options (ideals) -
- {% for key, label in labels.items() %} - - {% endfor %} -
-