SqFKYo

MTGAHelper booster EV

Feb 19th, 2021 (edited)
1,166
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.53 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2.  
  3. from collections import defaultdict, namedtuple
  4. from csv import DictReader
  5. from statistics import mean, StatisticsError
  6.  
  7.  
  8. COLLECTION_FILE = r"C:\Users\sqfky\Desktop\mtg_collection.txt"
  9. CURRENT_SETS = [
  10.     "KHM",
  11.     "ZNR",
  12.     "M21",
  13.     "IKO",
  14.     "THB",
  15.     "ELD",
  16.     "M20",
  17.     "WAR",
  18.     "RNA",
  19.     "GRN",
  20.     "M19",
  21.     "DOM",
  22.     "RIX",
  23.     "XLN",
  24.     "AKR",
  25.     "KLR",
  26. ]
  27.  
  28. # ToDo: Expand this to filter out also rares/mythics you can't get from boosters
  29. # Other sides of MDFCs to filter out
  30. MDFC_OTHER = {
  31.     # ZNR rares
  32.     'Boulderloft Pathway',
  33.     'Glasspool Shore',
  34.     'Grimclimb Pathway',
  35.     'Hagra Broodpit',
  36.     'Kazandu Valley',
  37.     'Lavaglide Pathway',
  38.     'Murkwater Pathway',
  39.     'Ondu Skyruins',
  40.     'Pillarverge Pathway',
  41.     'Timbercrown Pathway',
  42.     'Valakut Stoneforge',
  43.     # ZNR mythics
  44.     'Agadeem, the Undercrypt',
  45.     'Emeria, Shattered Skyclave',
  46.     'Sea Gate, Reborn',
  47.     'Shatterskull, the Hammer Pass',
  48.     'Turntimber, Serpentine Wood',
  49.     # KHM rares
  50.     'Harnfel, Horn of Bounty',
  51.     'Hengegate Pathway',
  52.     'Kaldring, the Rimestaff',
  53.     'Searstep Pathway',
  54.     'Slitherbore Pathway',
  55.     "Tergrid's Lantern",
  56.     'The Omenkeel',
  57.     'The Ringhart Crest',
  58.     'Throne of Death',
  59.     'Tidechannel Pathway',
  60.     "Valkmira, Protector's Shield",
  61.     # KHM mythics
  62.     'Hakka, Whispering Raven',
  63.     'Sword of the Realms',
  64.     'The Prismatic Bridge',
  65.     'Tibalt, Cosmic Impostor',
  66.     "Toralf's Hammer",
  67. }
  68.  
  69. MISSING_FILE = r"C:\Users\sqfky\Desktop\mtg_missing.txt"
  70. # MISSING_FILE = r"C:\Users\sqfky\Desktop\mtg_missing_historic.txt"
  71.  
  72.  
  73. MissingCard = namedtuple("MissingCard", "name weight")
  74.  
  75.  
  76. def from_boosters(line):
  77.     if line["Rarity"] in ["Uncommon", "Common"]:
  78.         return False
  79.     elif line["Set"] not in CURRENT_SETS:
  80.         return False
  81.     return True
  82.  
  83.  
  84. def mythic_chance(mtg_set):
  85.     if mtg_set == "AKR":
  86.         return 1 / 6
  87.     elif mtg_set == "KLR":
  88.         return 1 / 7
  89.     elif mtg_set in ["M21",
  90.                      "IKO",
  91.                      "THB",
  92.                      "ELD",
  93.                      "M20",
  94.                      "WAR",
  95.                      "RNA",
  96.                      "GRN",
  97.                      "M19",
  98.                      "DOM",
  99.                      "RIX",
  100.                      "XLN",
  101.                      ]:
  102.         return 1 / 8
  103.     else:
  104.         return 1 / 7.4
  105.  
  106.  
  107. class Collection:
  108.     def __init__(self, collection_file, missing_file):
  109.         # Set name as key into card names into amount, since we can have multiple sources of the name name of card
  110.         self.collection = defaultdict(lambda: defaultdict(int))
  111.         self.missing_cards = {}
  112.  
  113.         self._read_missing(missing_file)
  114.         self._read_collection(collection_file)
  115.         self._filter_collection()
  116.  
  117.     def _filter_collection(self):
  118.         """
  119.        Removes all cards we have 4 of already.
  120.        Removes other sides from ZNR/KHM Modal Dual-Faced rare/mythic cards.
  121.        Removes also the extra printings of the split cards.
  122.        """
  123.         for set_name, set_cards in self.collection.items():
  124.             self.collection[set_name] = {
  125.                 card: amount for card, amount in set_cards.items() if amount < 4 and card.name not in MDFC_OTHER
  126.             }
  127.  
  128.         for set_name, set_cards in self.collection.items():
  129.             for missing_card in list(set_cards.keys()):
  130.                 if "//" in missing_card.name:
  131.                     split_start, split_end = missing_card.name.split(" // ")
  132.                     split_start.strip()
  133.                     split_end.strip()
  134.                     del set_cards[MissingCard(split_start, weight=0)]
  135.                     del set_cards[MissingCard(split_end, weight=0)]
  136.  
  137.     def _read_collection(self, collection_file):
  138.         with open(collection_file, "r") as f:
  139.             reader = DictReader(f, delimiter=",", quotechar='"')
  140.             for line in reader:
  141.                 if not from_boosters(line):
  142.                     continue
  143.                 name = line["Name"]
  144.                 try:
  145.                     weight = self.missing_cards[name]
  146.                 except KeyError:
  147.                     weight = 0
  148.                 self.collection[f"{line['Set']}_{line['Rarity']}"][
  149.                     MissingCard(name=name, weight=weight)
  150.                 ] += int(line["Amount"])
  151.  
  152.     def _read_missing(self, missing_file):
  153.         with open(missing_file, "r") as f:
  154.             reader = DictReader(f, delimiter="\t", quotechar='"')
  155.             for line in reader:
  156.                 if not from_boosters(line):
  157.                     continue
  158.                 avg_weight = int(line["Weight"].replace(",", "")) / int(line["Missing"])
  159.                 self.missing_cards[line["Card"]] = avg_weight
  160.  
  161.     def estimated_weights(self, debug=False):
  162.         """
  163.        Prints the estimated weights for boosters for each set taking into consideration
  164.        that rares and mythics have different chances
  165.        """
  166.         booster_weights = {}
  167.         for mtg_set in CURRENT_SETS:
  168.             set_mythic_chance = mythic_chance(mtg_set=mtg_set)
  169.             set_rare_chance = 1 - set_mythic_chance
  170.             try:
  171.                 mean_rare_weight = mean(
  172.                     card.weight for card in self.collection[f"{mtg_set}_Rare"]
  173.                 )
  174.             except StatisticsError:
  175.                 mean_rare_weight = 0
  176.             try:
  177.                 mean_mythic_weight = mean(
  178.                     card.weight for card in self.collection[f"{mtg_set}_Mythic"]
  179.                 )
  180.             except StatisticsError:
  181.                 mean_mythic_weight = 0
  182.             booster_weights[mtg_set] = (
  183.                 set_rare_chance * mean_rare_weight
  184.                 + set_mythic_chance * mean_mythic_weight
  185.             )
  186.         for mtg_set, weight in sorted(
  187.             booster_weights.items(), key=lambda x: x[1], reverse=True
  188.         ):
  189.             print(f"\n{mtg_set} avg weight is {weight:.0f}")
  190.             good_rares = len(
  191.                 [card for card in self.collection[f"{mtg_set}_Rare"] if card.weight > 0]
  192.             )
  193.             bad_rares = len(
  194.                 [
  195.                     card
  196.                     for card in self.collection[f"{mtg_set}_Rare"]
  197.                     if card.weight == 0
  198.                 ]
  199.             )
  200.             good_mythics = len(
  201.                 [
  202.                     card
  203.                     for card in self.collection[f"{mtg_set}_Mythic"]
  204.                     if card.weight > 0
  205.                 ]
  206.             )
  207.             bad_mythics = len(
  208.                 [
  209.                     card
  210.                     for card in self.collection[f"{mtg_set}_Mythic"]
  211.                     if card.weight == 0
  212.                 ]
  213.             )
  214.             print(
  215.                 f"Unique cards left with nonzero weight: {good_rares} rares and {good_mythics} mythics"
  216.             )
  217.             print(
  218.                 f"Unique cards left with zero weight: {bad_rares} rares and {bad_mythics} mythics"
  219.             )
  220.             if debug:
  221.                 print("\nDebug info:")
  222.                 print("\nRares left:")
  223.                 for rare in self.collection[f"{mtg_set}_Rare"]:
  224.                     print(rare)
  225.                 print("\nMythics left:")
  226.                 for mythic in self.collection[f"{mtg_set}_Mythic"]:
  227.                     print(mythic)
  228.  
  229.  
  230. if __name__ == "__main__":
  231.     collection = Collection(collection_file=COLLECTION_FILE, missing_file=MISSING_FILE)
  232.     collection.estimated_weights(debug=False)
  233.  
Add Comment
Please, Sign In to add comment