Skip to content

Commit

Permalink
feat: add get_copied_card_id_by_format
Browse files Browse the repository at this point in the history
  • Loading branch information
beheh committed Aug 31, 2023
1 parent ad91a08 commit 1466220
Show file tree
Hide file tree
Showing 2 changed files with 259 additions and 2 deletions.
140 changes: 138 additions & 2 deletions hearthstone/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
# 2534 and 2523 set. However, in practice, one of these is still encoded using the Race tag,
# so likely such a card would have RACE = 11 (UNDEAD) and 2523 = 1 (DRAGON).
#
# If a new race is introduced, you're expected to add the tag here. You can find out the
# If a new race is introduced, you"re expected to add the tag here. You can find out the
# mapping by running patch processing and looking at the RaceTagMap.xml in the output
# directory.
CARDRACE_TAG_MAP = {
Expand Down Expand Up @@ -326,7 +326,7 @@
"DMF_248t": "DMF_248", # Felsteel Executioner
"DMF_064t": "DMF_064", # Carousel Gryphon
"DMF_124t": "DMF_124", # Horrendous Growth
"DMF_090t": "DMF_090", # Don't Feed the Animals
"DMF_090t": "DMF_090", # Don"t Feed the Animals
"DMF_105t": "DMF_105", # Ring Toss
"DMF_701t": "DMF_701", # Dunk Tank
"DMF_080t": "DMF_080", # Fleethoof Pearltusk
Expand Down Expand Up @@ -402,6 +402,142 @@ def get_original_card_id(card_id):
return UPGRADABLE_CARDS_MAP.get(card_id, card_id)


# A map of card ids that have been reprinted in the Wild format.
# Generated via scripts/dump_reprints.py
COPIED_CARDS_MAP_WILD = {
"WON_003": "AT_041",
"WON_009": "OG_313",
"WON_010": "OG_188",
"WON_011": "GVG_035",
"WON_012": "AT_045",
"EX1_609": "WON_018",
"VAN_EX1_609": "WON_018",
"WON_021": "AT_062",
"WON_022": "LOE_105",
"WON_023": "AT_061",
"CORE_AT_061": "AT_061",
"WON_024": "AT_063",
"WON_025": "AT_063t",
"WON_029": "AT_006",
"NEW1_012": "WON_031",
"VAN_NEW1_012": "WON_031",
"WON_033": "GVG_123",
"WON_035": "GVG_004",
"WON_036": "OG_087",
"WON_037": "OG_090",
"WON_038": "GVG_007",
"WON_045": "KAR_057",
"WON_046": "CFM_639",
"EX1_354": "WON_048",
"VAN_EX1_354": "WON_048",
"WON_049": "AT_078",
"WON_056": "LOE_006",
"WON_057": "KAR_204",
"WON_058": "AT_012",
"WON_061": "AT_014",
"WON_062": "GVG_009",
"WON_063": "AT_018",
"WON_067": "CFM_691",
"WON_070": "CFM_690",
"WON_071": "AT_033",
"WON_073": "BRM_008",
"WON_075": "OG_282",
"WON_076": "AT_036",
"WON_081": "AT_046",
"WON_082": "CFM_707",
"WON_083": "KAR_021",
"WON_084": "CFM_312",
"WON_085": "AT_049",
"WON_086": "CFM_310",
"WON_093": "AT_024",
"WON_095": "GVG_015",
"WON_096": "LOE_023",
"WON_097": "OG_116",
"WON_098": "KAR_205",
"WON_099": "AT_021",
"CORE_AT_021": "AT_021",
"WON_100": "AT_025",
"WON_105": "OG_121",
"WON_108": "CFM_754",
"WON_110": "CFM_752",
"WON_111": "OG_301",
"WON_114": "GVG_056",
"WON_117": "CFM_643",
"WON_118": "CFM_715",
"WON_124": "OG_284",
"WON_125": "OG_283",
"WON_127": "OG_162",
"WON_128": "FP1_012",
"WON_130": "CFM_649",
"WON_131": "OG_321",
"WON_133": "BRM_028",
"WON_134": "OG_131",
"WON_135": "OG_280",
"WON_136": "CFM_902",
"WON_137": "CFM_685",
"WON_162": "GVG_046",
"WON_300": "CFM_816",
"WON_302": "OG_202",
"WON_303": "CFM_343",
"WON_304": "OG_293",
"WON_305": "KAR_065",
"CORE_KAR_065": "KAR_065",
"WON_306": "GVG_073",
"WON_307": "CFM_336",
"WON_308": "CFM_760",
"WON_309": "KAR_077",
"WON_310": "OG_310",
"WON_311": "LOE_017",
"WON_312": "CFM_815",
"WON_313": "OG_334",
"WON_314": "GVG_011",
"WON_315": "OG_234",
"WON_316": "AT_028",
"WON_317": "OG_330",
"WON_318": "AT_034",
"WON_320": "AT_048",
"WON_321": "AT_050",
"WON_322": "OG_302",
"EX1_320": "WON_323",
"VAN_EX1_320": "WON_323",
"WON_324": "CFM_750",
"WON_325": "GVG_050",
"WON_326": "CFM_631",
"WON_328": "AT_090",
"WON_329": "BRM_034",
"WON_330": "OG_295",
"WON_331": "CFM_321",
"WON_332": "CFM_852",
"WON_333": "OG_311",
"WON_334": "AT_079",
"WON_335": "FP1_025",
"WON_336": "OG_209",
"WON_337": "KAR_091",
"WON_338": "BRM_016",
"WON_339": "CFM_756",
"WON_340": "LOE_012",
"CORE_LOE_012": "LOE_012",
"WON_341": "AT_001",
"WON_342": "AT_015",
"WON_344": "AT_007",
"WON_347": "CFM_334",
"WON_350": "CFM_940",
"WON_351": "CFM_325",
"EX1_007": "WON_357",
"VAN_EX1_007": "WON_357",
"CORE_EX1_007": "WON_357",
"WON_365": "CFM_039",
"WON_366": "CFM_665"
}


def get_copied_card_id_by_format(card_id, format_type):
"""Returns a suitable version for stat deduplication in the given FormatType."""
if format_type == 1:
return COPIED_CARDS_MAP_WILD.get(card_id, card_id)
return card_id


SCHEME_CARDS = [
"DAL_007", # Rafaam's Scheme
"DAL_008", # Dr. Boom's Scheme
Expand Down
121 changes: 121 additions & 0 deletions scripts/dump_reprints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from collections import defaultdict

from hearthstone.cardxml import load
from hearthstone.enums import CardSet, GameTag


def dump_reprints():
db, _ = load()
dbf_db = {v.dbf_id: v for k, v in db.items()}
pointers = {} # dbfId -> dict

# First, assemble a list of mappings from card -> copies
for card in db.values():
copy_of_dbf_id = card.tags.get(GameTag.DECK_RULE_COUNT_AS_COPY_OF_CARD_ID)

if not copy_of_dbf_id:
continue

if copy_of_dbf_id not in dbf_db:
continue

pointers[card.dbf_id] = copy_of_dbf_id

# At this point we have a mapping of dbfId -> dbfId
# Now, try to merge these into sets

chains = defaultdict(set)
for k, v in pointers.items():
chains[k].add(k)
chains[k].add(v)

# Now, keep merging
while True:
# Start over

modified = False
for parent, targets in chains.items():
new_targets = set(targets)

# check if any children own lists
for child in targets:
if child == parent:
continue
if child in chains:
new_targets.update(chains[child])
del chains[child]
modified = True
for k, chain in list(chains.items()):
if child in chain and k != parent:
new_targets.update(chain)
del chains[k]
modified = True

# Find the smallest
smallest = min(targets)

# If the parent is the smallest, nothing to do - children will turn up
if smallest == parent:
targets.update(new_targets)
if modified:
break
else:
continue

chains[smallest] = new_targets
del chains[parent]

modified = True
break

if not modified:
break

the_map = {}

for chain in chains.values():
# Map to cards
the_chain = [dbf_db[c] for c in chain]
the_chain = [c for c in the_chain if c.collectible]
if len(the_chain) < 2:
continue

# Get rid of chains without WONDERS cards
if not any([c for c in the_chain if c.card_set == CardSet.WONDERS]):
continue

# Find the best owner
bad_sets = [
CardSet.CORE,
CardSet.PLACEHOLDER_202204,
CardSet.EXPERT1,
CardSet.BASIC,
CardSet.LEGACY,
CardSet.VANILLA
]
owners_from_good_sets = [c for c in the_chain if c.card_set not in bad_sets]

winner = None

assert len(owners_from_good_sets) in (1, 2)

if len(owners_from_good_sets) == 1:
# WONDERS is the only good set, map all others to it
assert owners_from_good_sets[0].card_set == CardSet.WONDERS
winner = owners_from_good_sets[0]
elif len(owners_from_good_sets) == 2:
# Probably one is from WON
old_cards = [c for c in owners_from_good_sets if c.card_set != CardSet.WONDERS]
assert len(old_cards) == 1
winner = old_cards[0]

for c in the_chain:
if c.id == winner.id:
continue
the_map[c.id] = winner.id

print(the_map)


if __name__ == "__main__":
dump_reprints()

0 comments on commit 1466220

Please sign in to comment.