Created
September 12, 2025 10:05
-
-
Save Trusted97/fa9ac8af61142d267af3f269a9b6acf6 to your computer and use it in GitHub Desktop.
Revisions
-
Trusted97 created this gist
Sep 12, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,108 @@ # Monte Carlo method # Goal: # 1) Find best opening hands # 2) Estimate combo-read hands import random, collections, itertools, textwrap, pandas as pd deck_list = [ "1 Abrade","1 Ad Nauseam","1 Agatha's Soul Cauldron","1 All Will Be One","1 Arcane Signet","1 Barbarian Ring","1 Beseech the Mirror","1 Birgi, God of Storytelling", "1 Blackcleave Cliffs","1 Blazemire Verge","1 Blightstep Pathway","1 Blood Crypt","1 Bloodstained Mire","1 Cabal Ritual","1 Cavern of Souls","1 Command Tower", "1 Coruscation Mage","1 Dark Ritual","1 Dauthi Voidwalker","1 Deadly Rollick","1 Defense Grid","1 Deflecting Swat","1 Demonic Tutor","1 Desperate Ritual","1 Diabolic Intent", "1 Dismember","1 Dragonskull Summit","1 Dualcaster Mage","1 Emergence Zone","1 Exotic Orchard","1 Fellwar Stone","1 Final Fortune","1 Fire Covenant","1 Firebrand Archer", "1 Flare of Duplication","1 Gamble","1 Gemstone Caverns","1 Go for the Throat","1 Goblin Engineer","1 Grapeshot","1 Grinding Station","1 Imperial Recruiter","1 Jeska's Will", "1 Kederekt Parasite","1 Kessig Flamebreather","1 Lightning Bolt","1 Lotus Petal","1 Luxury Suite","1 Mana Vault","1 Marionette Apprentice","1 Marsh Flats","1 Mayhem Devil", "1 Molten Duplication","1 Mount Doom","3 Mountain","1 Mox Amber","1 Necropotence","1 Opposition Agent","1 Orcish Bowmasters","1 Party Thrasher","1 Praetor's Grasp","1 Prosper, Tome-Bound", "1 Pyretic Ritual","1 Ragavan, Nimble Pilferer","1 Raucous Theater","1 Razorkin Needlehead","1 Red Elemental Blast","1 Reflecting Pool","1 Rite of Flame","1 Roiling Vortex", "1 Sensei's Divining Top","1 Shizo, Death's Storehouse","1 Simian Spirit Guide","1 Slaughter Pact","1 Smoldering Marsh","1 Snuff Out","1 Sokenzan, Crucible of Defiance","1 Sol Ring", "1 Sulfurous Springs","5 Swamp","1 Tainted Peak","1 Takenuma, Abandoned Mire","1 Talisman of Indulgence","1 Underworld Breach","1 Unstable Amulet","1 Untimely Malfunction", "1 Urza's Saga","1 Vampiric Tutor","1 Vexing Bauble","1 Volrath's Stronghold","1 Walking Ballista","1 Wheel of Fortune","1 Wooded Foothills","1 Ob Nixilis, Captive Kingpin" ] expanded = [] for entry in deck_list: parts = entry.split(" ",1) count = int(parts[0]) name = parts[1].strip() for _ in range(count): expanded.append(name) deck = expanded[:] len(deck) # Define sets fast_mana = set(["Mana Vault","Sol Ring","Mox Amber","Lotus Petal","Gemstone Caverns","Fellwar Stone","Talisman of Indulgence","Unstable Amulet","Arcane Signet","Simian Spirit Guide"]) rituals = set(["Dark Ritual","Cabal Ritual","Desperate Ritual","Pyretic Ritual","Rite of Flame","Jeska's Will"]) tutors = set(["Vampiric Tutor","Demonic Tutor","Diabolic Intent","Gamble","Beseech the Mirror","Praetor's Grasp","Imperial Recruiter"]) combo_pieces = set(["Underworld Breach","Grinding Station","All Will Be One","Walking Ballista","Ad Nauseam"]) # Simulation import math N=100000 random.seed(42) counts = collections.Counter() top_hands = collections.Counter() for _ in range(N): d = deck[:] random.shuffle(d) hand = tuple(sorted(d[:7])) handset = set(hand) has_fast = len([c for c in hand if c in fast_mana]) + len([c for c in hand if c in rituals]) # Criteria A: Breach + Grinding Station + at least one mana source (land or fast mana/ritual) has_land = any(name in hand for name in ["Swamp","Mountain","Blood Crypt","Dragonskull Summit","Smoldering Marsh","Tainted Peak","Takenuma, Abandoned Mire","Blackcleave Cliffs","Sokenzan, Crucible of Defiance","Gemstone Caverns","Blightstep Pathway","Reflecting Pool","Exotic Orchard","Command Tower","Luxury Suite","Mount Doom","Wooded Foothills","Marsh Flats"]) criteria_A = ("Underworld Breach" in handset) and ("Grinding Station" in handset) and (has_land or any(c in fast_mana or c in rituals for c in hand)) # Criteria B: All Will + Ballista + mana criteria_B = ("All Will Be One" in handset) and ("Walking Ballista" in handset) and (has_land or any(c in fast_mana or c in rituals for c in hand)) # Criteria C: Ad Nauseam + at least 2 fast mana/rituali fastcount = sum(1 for c in hand if c in fast_mana or c in rituals) criteria_C = ("Ad Nauseam" in handset) and (fastcount >= 2) # Criteria D: tutor + at least 1 fast mana or land criteria_D = any(c in handset for c in tutors) and (has_land or fastcount>=1) combo_ready = criteria_A or criteria_B or criteria_C or criteria_D if combo_ready: counts['combo_ready'] += 1 top_hands[hand] += 1 else: counts['not_ready'] += 1 # Compute percentages combo_pct = counts['combo_ready']/N*100.0 not_pct = counts['not_ready']/N*100.0 # Top 10 most frequent combo-ready opening hands observed top_combo_hands = [(hand, cnt) for hand,cnt in top_hands.most_common(30)] top_combo_hands = top_combo_hands[:10] # Prepare output table for top hands (show only card names and frequency) rows = [] for hand, cnt in top_combo_hands: rows.append({"hand": ", ".join(hand), "count": cnt, "percent_of_sim": cnt/N*100.0}) df = pd.DataFrame(rows) from caas_jupyter_tools import display_dataframe_to_user display_dataframe_to_user("Top combo-ready opening hands (sampled)", df) # Print summary stats print(f"Simulation: {N}") print(f"Openings combo-ready: {counts['combo_ready']} ({combo_pct:.2f}%)") print(f"Openings not combo-ready: {counts['not_ready']} ({not_pct:.2f}%)") # Breakdown of which criteria were hit (approximate) - re-run to count each criterion individually criteria_counts = collections.Counter() random.seed(42) for _ in range(N): d = deck[:]; random.shuffle(d); hand = d[:7]; handset=set(hand) has_land = any(name in hand for name in ["Swamp","Mountain","Blood Crypt","Dragonskull Summit","Smoldering Marsh","Tainted Peak","Takenuma, Abandoned Mire","Blackcleave Cliffs","Sokenzan, Crucible of Defiance","Gemstone Caverns","Blightstep Pathway","Reflecting Pool","Exotic Orchard","Command Tower","Luxury Suite","Mount Doom","Wooded Foothills","Marsh Flats"]) fastcount = sum(1 for c in hand if c in fast_mana or c in rituals) if ("Underworld Breach" in handset) and ("Grinding Station" in handset) and (has_land or any(c in fast_mana or c in rituals for c in hand)): criteria_counts['Breach_line'] += 1 if ("All Will Be One" in handset) and ("Walking Ballista" in handset) and (has_land or any(c in fast_mana or c in rituals for c in hand)): criteria_counts['AllWill_Ballista'] += 1 if ("Ad Nauseam" in handset) and (fastcount >= 2): criteria_counts['AdNauseam'] += 1 if any(c in handset for c in tutors) and (has_land or fastcount>=1): criteria_counts['Tutor_plus_accel'] += 1 print("Breakdown (hits per criterion):") for k,v in criteria_counts.items(): print(f" {k}: {v} ({v/N*100:.2f}%)")