def test_create_game(self): card_set1 = [] card_set2 = [] test_env = self for cardIndex in range(0, 30): card_set1.append(card_lookup("Stonetusk Boar")) card_set2.append(card_lookup("Novice Engineer")) deck1 = Deck(card_set1, Malfurion()) deck2 = Deck(card_set2, Jaina()) checked_cards = [] class MockAgent1: def do_card_check(self, cards): test_env.assertEqual(len(cards), 3) checked_cards.append(list(cards)) return [False, True, True] def set_game(self, game): pass class MockAgent2: def do_card_check(self, cards): test_env.assertEqual(len(cards), 4) checked_cards.append(list(cards)) return [False, True, True, False] def set_game(self, game): pass agent1 = mock.Mock(spec=MockAgent1(), wraps=MockAgent1()) agent2 = mock.Mock(spec=MockAgent2(), wraps=MockAgent2()) game = Game([deck1, deck2], [agent1, agent2]) game.pre_game() self.assertEqual(agent1.method_calls[0][0], "do_card_check", "Agent not asked to select cards") self.assertEqual(agent2.method_calls[0][0], "do_card_check", "Agent not asked to select cards") self.assertTrue(game.players[0].deck == deck1, "Deck not assigned to player") self.assertTrue(game.players[1].deck == deck2, "Deck not assigned to player") self.assertTrue(game.players[0].agent == agent1, "Agent not stored in the hearthbreaker") self.assertTrue(game.players[1].agent == agent2, "Agent not stored in the hearthbreaker") self.assertListEqual(checked_cards[0][1:], game.players[0].hand[1:], "Cards not retained after request") self.assertListEqual(checked_cards[1][1:2], game.players[1].hand[1:2], "Cards not retained after request")
def test_play_with_one_card(self): file = open("AllSets.enUS.json", "r", encoding="UTF-8") card_dict = json.load(file) for card_set in [ 'Classic', "Basic", "Curse of Naxxramas", "Goblins vs Gnomes", "Blackrock Mountain" ]: for card_info in card_dict[card_set]: if 'collectible' in card_info and card_info[ 'collectible'] and card_info["type"] != "Hero": try: card = card_lookup(card_info["name"]) except KeyError: continue game = generate_game_for(type(card), StonetuskBoar, PlayAndAttackAgent, DoNothingAgent) try: while not game.game_ended: game.play_single_turn() except Exception as e: print(card) raise e file.close()
def get_starting_hands(self): starting_hands1 = tuple( itertools.combinations(self.game.players[0].deck.cards, 5)) starting_hands2 = tuple( itertools.combinations(self.game.players[1].deck.cards, 5)) startinghands1 = [] startinghands2 = [] for i in range(len(starting_hands1)): startinghands1.append(starting_hands1[i]) for i in range(len(starting_hands2)): startinghands2.append(starting_hands2[i]) for i in range(len(startinghands2)): coin = engine.card_lookup("The Coin") coin.player = self.game.players[1] t = list(startinghands2[i]) t.append(coin) startinghands2[i] = tuple(t) starting_hands = [] for i in range(len(startinghands1)): for j in range(len(startinghands2)): starting_hands.append((startinghands1[i], startinghands2[j])) return starting_hands
def test_first_turn(self): card_set1 = [] card_set2 = [] for cardIndex in range(0, 30): card_set1.append(card_lookup("Stonetusk Boar")) card_set2.append(card_lookup("Novice Engineer")) deck1 = Deck(card_set1, Malfurion()) deck2 = Deck(card_set2, Jaina()) agent1 = mock.Mock(spec=DoNothingAgent(), wraps=DoNothingAgent()) agent2 = mock.Mock(spec=DoNothingAgent(), wraps=DoNothingAgent()) game = Game([deck1, deck2], [agent1, agent2]) game.start()
def can_add(card): rarity = card_lookup(card).rarity num = cards_in_deck.setdefault(card, 0) if rarity == CARD_RARITY.LEGENDARY: return num < 1 else: return num < 2
def use(self, player, game): from hearthbreaker.engine import card_lookup super().use(player, game) if len(player.graveyard) > 0 and len(player.minions) < 7: card_name = game.random_choice(player.graveyard) card = card_lookup(card_name) card.summon(player, game, len(player.minions))
def can_add(card, deck): rarity = card_lookup(card).rarity num = deck.count(card) if rarity == CARD_RARITY.LEGENDARY: return num < 1 else: return num < 2
def read_json(self, file): """ Read a replay in the complete json format. This format is compatible with the netplay format, and is also designed to be more future proof. For more info, see the `replay format <https://github.com/danielyule/hearthbreaker/blob/master/replay_format.md>`_ :param file: Either a string or an IO object. If a string, then it is assumed to be a filename describing where a replay file is found. If an IO object, then the IO object should be opened for reading. :type file: :class:`str` or :class:`io.TextIOBase` """ from jsonschema import validate was_filename = False if 'read' not in dir(file): was_filename = True file = open(file, 'r') jd = json.load(file) validate(jd, self.schema) self.decks = [] for deck in jd['header']['decks']: deck_size = len(deck['cards']) cards = [card_lookup(deck['cards'][index % deck_size]) for index in range(0, 30)] self.decks.append( Deck(cards, hero_from_name(deck['hero']))) self.random = jd['header']['random'] self.keeps = jd['header']['keep'] if len(self.keeps) == 0: self.keeps = [[0, 1, 2], [0, 1, 2, 3]] self._moves = [Move.from_json(**js) for js in jd['moves']] if was_filename: file.close()
def from_json(name=None, conditions=[], source="collection", source_list=None, make_copy=False, minion=None): from hearthbreaker.engine import card_lookup query = CardQuery.__new__(CardQuery) query.name = name query.conditions = [] for condition in conditions: query.conditions.append(Condition.from_json(**condition)) else: query.condition = None query.source = CARD_SOURCE.from_str(source) if source_list: query.source_list = [card_lookup(item) for item in source_list] else: query.source_list = None query.make_copy = make_copy if minion: query.minion = Selector.from_json(**minion) else: query.minion = None return query
def read_json(self, file): """ Read a replay in the complete json format. This format is compatible with the netplay format, and is also designed to be more future proof. For more info, see the `replay format <https://github.com/danielyule/hearthbreaker/blob/master/replay_format.md>`_ :param file: Either a string or an IO object. If a string, then it is assumed to be a filename describing where a replay file is found. If an IO object, then the IO object should be opened for reading. :type file: :class:`str` or :class:`io.TextIOBase` """ from jsonschema import validate was_filename = False if 'read' not in dir(file): was_filename = True file = open(file, 'r') jd = json.load(file) validate(jd, self.schema) self.decks = [] for deck in jd['header']['decks']: deck_size = len(deck['cards']) cards = [ card_lookup(deck['cards'][index % deck_size]) for index in range(0, 30) ] self.decks.append(Deck(cards, hero_from_name(deck['hero']))) self.random = jd['header']['random'] self.keeps = jd['header']['keep'] if len(self.keeps) == 0: self.keeps = [[0, 1, 2], [0, 1, 2, 3]] self._moves = [Move.from_json(**js) for js in jd['moves']] if was_filename: file.close()
def from_json(card, actions, selector, condition=None): from hearthbreaker.engine import card_lookup actions = [Action.from_json(**action) for action in actions] selector = Selector.from_json(**selector) if condition: condition = Condition.from_json(**condition) card = card_lookup(card) return Choice(card, actions, selector, condition)
def __from_json__(wd, player): from hearthbreaker.engine import card_lookup weapon_card = card_lookup(wd['name']) weapon = weapon_card.create_weapon(player) weapon.base_attack = wd['attack'] weapon.durability = wd['durability'] weapon.card = weapon_card GameObject.__from_json__(weapon, **wd) return weapon
def get_card(self, player, owner): from hearthbreaker.engine import card_lookup, get_cards if self.name: return card_lookup(self.name) if self.source == CARD_SOURCE.COLLECTION: card_list = get_cards() elif self.source == CARD_SOURCE.MY_DECK: card_list = filter(lambda c: not c.drawn, player.deck.cards) elif self.source == CARD_SOURCE.MY_HAND: card_list = player.hand elif self.source == CARD_SOURCE.OPPONENT_DECK: card_list = filter(lambda c: not c.drawn, player.opponent.deck.cards) elif self.source == CARD_SOURCE.OPPONENT_HAND: card_list = player.opponent.hand elif self.source == CARD_SOURCE.LIST: card_list = self.source_list elif self.source == CARD_SOURCE.LAST_CARD: return type(player.game.last_card)() elif self.source == CARD_SOURCE.MINION: return self.minion.get_targets(owner, owner)[0].card else: card_list = [] # TODO Throw an exception in any other case? def check_condition(condition): return lambda c: condition.evaluate(player, c) for condition in self.conditions: card_list = filter(check_condition(condition), card_list) card_list = [card for card in card_list] card_len = len(card_list) if card_len == 1: chosen_card = card_list[0] elif card_len == 0: return None else: chosen_card = player.game.random_choice(card_list) if self.source == CARD_SOURCE.COLLECTION or self.source == CARD_SOURCE.LIST or self.make_copy: return chosen_card elif self.source == CARD_SOURCE.MY_DECK: chosen_card.drawn = True player.deck.left -= 1 return chosen_card elif self.source == CARD_SOURCE.OPPONENT_DECK: chosen_card.drawn = True player.opponent.deck.left -= 1 return chosen_card elif self.source == CARD_SOURCE.MY_HAND: player.hand.remove(chosen_card) return chosen_card elif self.source == CARD_SOURCE.OPPONENT_HAND: player.opponent.hand.remove(chosen_card) return chosen_card
def deck_valid(deck): counter = collections.defaultdict(lambda: 0) for card in deck.deck: card_ob = card_lookup(card) rarity = card_ob.rarity if card_ob.character_class != 0 and card_ob.character_class != deck.hero: return False counter[card] += 1 if rarity == CARD_RARITY.LEGENDARY: if not counter[card] == 1: return False else: if not counter[card] <= 2: return False return True
def test_play_with_one_card(self): file = open("AllSets.enUS.json", "r", encoding="UTF-8") card_dict = json.load(file) for card_set in ['Expert', "Basic", "Curse of Naxxramas", "Goblins vs Gnomes"]: for card_info in card_dict[card_set]: if 'collectible' in card_info and card_info['collectible'] and card_info["type"] != "Hero": try: card = card_lookup(card_info["name"]) except KeyError: continue game = generate_game_for(type(card), StonetuskBoar, PlayAndAttackAgent, DoNothingAgent) while not game.game_ended: game.play_single_turn() file.close()
def load_deck(filename): cards = [] with open(filename, "r") as deck_file: contents = deck_file.read() items = contents.splitlines() for line in items[0:]: parts = line.split(" ", 1) count = int(parts[0]) for i in range(0, count): card = card_lookup(parts[1]) cards.append(card) if len(cards) > 20: pass return cards
def __from_json__(md, player, game): from hearthbreaker.engine import card_lookup minion = Minion(md['attack'], md['max_health']) GameObject.__from_json__(minion, **md) minion.health = md['max_health'] - md['damage'] minion.exhausted = md['exhausted'] minion.attacks_performed = not md['attacks_performed'] minion.born = md['sequence_id'] if 'enrage' in md: minion.enrage = [Aura.from_json(**enrage) for enrage in md['enrage']] minion.deathrattle = [] for rattle in md['deathrattles']: minion.deathrattle.append(Deathrattle.from_json(**rattle)) minion.card = card_lookup(md["name"]) minion.game = game minion.player = player return minion
def load_deck(self, filename): cards = [] character_class = CHARACTER_CLASS.MAGE with open(filename, "r") as deck_file: contents = deck_file.read() items = contents.splitlines() for line in items[0:]: parts = line.split(" ", 1) count = int(parts[0]) for i in range(0, count): card = card_lookup(parts[1]) if card.character_class != CHARACTER_CLASS.ALL: character_class = card.character_class cards.append(card) if len(cards) > 30: pass return Deck(cards, hero_for_class(character_class))
def __from_json__(md, player, game): from hearthbreaker.engine import card_lookup minion = Minion(md['attack'], md['max_health']) GameObject.__from_json__(minion, **md) minion.health = md['max_health'] - md['damage'] minion.exhausted = md['exhausted'] minion.attacks_performed = not md['attacks_performed'] minion.born = md['sequence_id'] if 'enrage' in md: minion.enrage = [ Aura.from_json(**enrage) for enrage in md['enrage'] ] minion.deathrattle = [] for rattle in md['deathrattles']: minion.deathrattle.append(Deathrattle.from_json(**rattle)) minion.card = card_lookup(md["name"]) minion.game = game minion.player = player return minion
def load_deck(filename): cards = [] character_class = CHARACTER_CLASS.MAGE with open(filename, "r") as deck_file: contents = deck_file.read() items = contents.splitlines() for line in items[0:]: parts = line.split(" ", 1) count = int(parts[0]) for i in range(0, count): card = card_lookup(parts[1]) if card.character_class != CHARACTER_CLASS.ALL: character_class = card.character_class cards.append(card) if len(cards) > 30: pass return Deck(cards, hero_for_class(character_class))
def __from_json__(cards): from hearthbreaker.engine import card_lookup return CardList([card_lookup(card) for card in cards])
def test_all_cards(self): fake_game = generate_game_for(StonetuskBoar, StonetuskBoar, DoNothingAgent, DoNothingAgent) overload_re = re.compile("Overload: \\((\\d)\\)") spell_damage_re = re.compile("Spell Damage \\+(\\d)") battlecry_re = re.compile("Battlecry: .*") deathrattle_re = re.compile("Deathrattle: .*") split_re = re.compile("\\s*\\.\\s*") bold_tag_re = re.compile("</?b>") file = open("AllSets.enUS.json", "r", encoding="UTF-8") card_dict = json.load(file) not_implemented = [] total_cards = 0 for card_set in [ 'Expert', "Basic", "Curse of Naxxramas", "Goblins vs Gnomes", "Reward", "Promotion" ]: for card_info in card_dict[card_set]: if card_info["type"] in [ 'Minion', 'Spell', 'Weapon', 'Secret' ]: total_cards += 1 try: card = card_lookup(id_mappings[card_info["id"]]) except KeyError: not_implemented.append("{}: ({})".format( card_info["name"], card_info['id'])) continue if "cost" in card_info: self.assertEqual( int(card_info["cost"]), card.mana, "Expected {} to have cost {}. Got {}".format( card_info["name"], card_info["cost"], card.mana)) if "playerClass" in card_info: self.assertEqual( CHARACTER_CLASS.from_str(card_info["playerClass"]), card.character_class, "Expected {} to have class {}. Got {}".format( card_info["name"], card_info["playerClass"], CHARACTER_CLASS.to_str(card.character_class))) else: self.assertEqual( CHARACTER_CLASS.ALL, card.character_class, "Expected {} to have no class. Got {}".format( card_info["name"], CHARACTER_CLASS.to_str(card.character_class))) if "rarity" in card_info: self.assertEqual( CARD_RARITY.from_str(card_info["rarity"]), card.rarity, "Expected card {} to have rarity {}. Got {}". format(card_info["name"], card_info["rarity"], CARD_RARITY.to_str(card.rarity))) if "collectible" in card_info: if card_info['collectible']: self.assertTrue( card.collectible, "Expected card {} to be collectible".format( card_info['name'])) else: self.assertFalse( card.collectible, "Expected card {} not to be collectible". format(card_info['name'])) if card_info["type"] == "Minion": minion = card.create_minion(fake_game.current_player) minion.player = fake_game.current_player minion.game = fake_game minion.card = card minion.add_to_board(0) if "text" in card_info: for effect in split_re.split( re.sub(bold_tag_re, "", card_info["text"])): if effect == "Taunt": self.assertTrue( minion.taunt, "Expected {:s} to have taunt".format( card_info["name"])) elif effect == "Divine Shield": self.assertTrue( minion.divine_shield, "Expected {:s} to have divine shield". format(card_info["name"])) elif effect == "Stealth": self.assertTrue( minion.stealth, "Expected {:s} to have stealth".format( card_info["name"])) elif effect == "Windfury": self.assertTrue( minion.windfury(), "Expected {:s} to have windfury". format(card_info["name"])) elif effect == "Charge": self.assertTrue( minion.charge(), "Expected {:s} to have charge".format( card_info["name"])) elif battlecry_re.match(effect): self.assertTrue( card.battlecry is not None, "Expected {:s} to have a battlecry". format(card_info["name"])) elif deathrattle_re.match(effect): self.assertTrue( minion.deathrattle is not None, "Expected {:s} to have a deathrattle". format(card_info["name"])) elif overload_re.match(effect) is not None: self.assertEqual( int( overload_re.match(effect).group( 1)), card.overload, ("Expected {:s} to have overload of" + " {:s}, but had {:d}").format( card_info["name"], overload_re.match(effect).group( 1), card.overload)) elif spell_damage_re.match(effect) is not None: self.assertEqual( int( spell_damage_re.match( effect).group(1)), minion.player.spell_damage, ("Expected {:s} to have spell damage of" + " {}, but had {}").format( card_info["name"], spell_damage_re.match( effect).group(1), minion.player.spell_damage)) minion.silence() self.assertEqual( int(card_info["attack"]), minion.calculate_attack(), "Expected {} to have attack of {}. Got {}".format( card_info["name"], card_info["attack"], minion.calculate_attack())) self.assertEqual( int(card_info["health"]), minion.health, "Expected {} to have health of {}. Got {}".format( card_info["name"], card_info["health"], minion.health)) if "race" in card_info: self.assertEqual( MINION_TYPE.from_str(card_info["race"]), card.minion_type, "Expected {} to have race {}. Got {}".format( card_info["name"], card_info["race"], MINION_TYPE.to_str(card.minion_type))) else: self.assertEqual( MINION_TYPE.NONE, card.minion_type, "Expected {} to have no race. Got {}".format( card_info["name"], MINION_TYPE.to_str(card.minion_type))) elif card_info["type"] == "Weapon": weapon = card.create_weapon(fake_game.current_player) self.assertEqual( int(card_info["attack"]), weapon.base_attack, "Expected {} to have attack of {}. Got {}".format( card_info["name"], card_info["attack"], weapon.base_attack)) self.assertEqual( int(card_info["durability"]), weapon.durability, "Expected {} to have durability of {}. Got {}". format(card_info["name"], card_info["durability"], weapon.durability)) file.close() if len(not_implemented) > 0: print("{} of {} cards implemented".format( total_cards - len(not_implemented), total_cards)) print("Cards not implemented:") for card in not_implemented: print(" - {}".format(card))
def read(self, file): """ Read a replay in the compact format. This format is a series of directives, and isn't as flexible or well structured as the json format (in :meth:write_json). For more info, see the `replay format <https://github.com/danielyule/hearthbreaker/blob/master/replay_format.md>`_ :param file: Either a string or an IO object. If a string, then it is assumed to be a filename describing where a replay file is to be found. If an IO object, then the IO object should be opened for reading. :type file: :class:`str` or :class:`io.TextIOBase` """ was_filename = False if 'read' not in dir(file): was_filename = True file = open(file, 'r') line_pattern = re.compile("\s*(\w*)\s*\(([^)]*)\)\s*(;.*)?$") for line in file: (move, args) = line_pattern.match(line).group(1, 2) args = [arg.strip() for arg in args.split(",")] if move == 'play': card = args[0] if len(args) > 1: target = args[1] else: target = None self._moves.append( PlayMove(hearthbreaker.proxies.ProxyCard(card), target=target)) elif move == 'summon': card = args[0] index = int(args[1]) if len(args) > 2: target = args[2] else: target = None self._moves.append( PlayMove(hearthbreaker.proxies.ProxyCard(card), index, target)) elif move == 'attack': self._moves.append(AttackMove(args[0], args[1])) elif move == 'power': if len(args) > 0 and args[0] != '': self._moves.append(PowerMove(args[0])) else: self._moves.append(PowerMove()) elif move == 'end': self._moves.append(TurnEndMove()) elif move == 'start': self._moves.append(TurnStartMove()) elif move == 'random': if len(self._moves) == 0: if len(args[0]) > 0: for num in args: self.random.append(int(num)) else: for num in args: if num.isdigit(): self._moves[-1].random_numbers.append(int(num)) else: self._moves[-1].random_numbers.append( hearthbreaker.proxies.ProxyCharacter(num)) elif move == 'deck': if len(self.decks) > 1: raise Exception("Maximum of two decks per file") deck_size = len(args) - 1 cards = [ card_lookup(args[1 + index % deck_size]) for index in range(0, 30) ] self.decks.append(Deck(cards, hero_from_name(args[0]))) elif move == 'keep': if len(self.keeps) > 1: raise Exception("Maximum of two keep directives per file") self.keeps.append([int(a) for a in args]) elif move == 'concede': self._moves.append(ConcedeMove()) if was_filename: file.close() if len(self.keeps) is 0: self.keeps = [[0, 1, 2], [0, 1, 2, 3]]
def get_card(self, target, player, owner): from hearthbreaker.engine import card_lookup return card_lookup(self.card)
def read(self, file): """ Read a replay in the compact format. This format is a series of directives, and isn't as flexible or well structured as the json format (in :meth:write_json). For more info, see the `replay format <https://github.com/danielyule/hearthbreaker/blob/master/replay_format.md>`_ :param file: Either a string or an IO object. If a string, then it is assumed to be a filename describing where a replay file is to be found. If an IO object, then the IO object should be opened for reading. :type file: :class:`str` or :class:`io.TextIOBase` """ was_filename = False if 'read' not in dir(file): was_filename = True file = open(file, 'r') line_pattern = re.compile("\s*(\w*)\s*\(([^)]*)\)\s*(;.*)?$") for line in file: (move, args) = line_pattern.match(line).group(1, 2) args = [arg.strip() for arg in args.split(",")] if move == 'play': card = args[0] if len(args) > 1: target = args[1] else: target = None self._moves.append(PlayMove(hearthbreaker.proxies.ProxyCard(card), target=target)) elif move == 'summon': card = args[0] index = int(args[1]) if len(args) > 2: target = args[2] else: target = None self._moves.append(PlayMove(hearthbreaker.proxies.ProxyCard(card), index, target)) elif move == 'attack': self._moves.append(AttackMove(args[0], args[1])) elif move == 'power': if len(args) > 0 and args[0] != '': self._moves.append(PowerMove(args[0])) else: self._moves.append(PowerMove()) elif move == 'end': self._moves.append(TurnEndMove()) elif move == 'start': self._moves.append(TurnStartMove()) elif move == 'random': if len(self._moves) == 0: if len(args[0]) > 0: for num in args: self.random.append(int(num)) else: for num in args: if num.isdigit(): self._moves[-1].random_numbers.append(int(num)) else: self._moves[-1].random_numbers.append(hearthbreaker.proxies.ProxyCharacter(num)) elif move == 'deck': if len(self.decks) > 1: raise Exception("Maximum of two decks per file") deck_size = len(args) - 1 cards = [card_lookup(args[1 + index % deck_size]) for index in range(0, 30)] self.decks.append( Deck(cards, hero_from_name(args[0]))) elif move == 'keep': if len(self.keeps) > 1: raise Exception("Maximum of two keep directives per file") self.keeps.append([int(a) for a in args]) elif move == 'concede': self._moves.append(ConcedeMove()) if was_filename: file.close() if len(self.keeps) is 0: self.keeps = [[0, 1, 2], [0, 1, 2, 3]]
def get_card(self, player, owner): from hearthbreaker.engine import card_lookup, get_cards if self.name: return card_lookup(self.name) if self.source == CARD_SOURCE.COLLECTION: card_list = get_cards() elif self.source == CARD_SOURCE.MY_DECK: card_list = filter(lambda c: not c.drawn, player.deck.cards) elif self.source == CARD_SOURCE.MY_HAND: card_list = player.hand elif self.source == CARD_SOURCE.OPPONENT_DECK: card_list = filter(lambda c: not c.drawn, player.opponent.deck.cards) elif self.source == CARD_SOURCE.OPPONENT_HAND: card_list = player.opponent.hand elif self.source == CARD_SOURCE.LIST: card_list = self.source_list elif self.source == CARD_SOURCE.LAST_CARD: return type(player.game.last_card)() elif self.source == CARD_SOURCE.LAST_DRAWN: chosen_card = player.hand[-1] player.hand.remove(chosen_card) chosen_card.unattach() return chosen_card elif self.source == CARD_SOURCE.MINION: card_list = [ minion.card for minion in self.minion.get_targets(owner, owner) ] elif self.source == CARD_SOURCE.MY_SECRETS: card_list = [secret.card for secret in player.secrets] elif self.source == CARD_SOURCE.ENEMY_SECRETS: card_list = [secret for secret in player.opponent.secrets] else: card_list = [] # TODO Throw an exception in any other case? def check_condition(condition): return lambda c: condition.evaluate(player, c) for condition in self.conditions: card_list = filter(check_condition(condition), card_list) card_list = [card for card in card_list] card_len = len(card_list) if card_len == 1: chosen_card = card_list[0] elif card_len == 0: return None else: chosen_card = player.game.random_choice(card_list) if self.source == CARD_SOURCE.COLLECTION or self.source == CARD_SOURCE.LIST or self.make_copy: return chosen_card elif self.source == CARD_SOURCE.MY_DECK: chosen_card.drawn = True player.deck.left -= 1 return chosen_card elif self.source == CARD_SOURCE.OPPONENT_DECK: chosen_card.drawn = True player.opponent.deck.left -= 1 return chosen_card elif self.source == CARD_SOURCE.MY_HAND: player.hand.remove(chosen_card) chosen_card.unattach() return chosen_card elif self.source == CARD_SOURCE.OPPONENT_HAND: player.opponent.hand.remove(chosen_card) chosen_card.unattach() return chosen_card elif self.source == CARD_SOURCE.MY_SECRETS: if player is player.game.other_player: chosen_card.deactivate(player) player.secrets.remove(chosen_card) return chosen_card elif self.source == CARD_SOURCE.ENEMY_SECRETS: if player.opponent is player.game.other_player: chosen_card.deactivate(player.opponent) player.opponent.secrets.remove(chosen_card) return chosen_card
def test_all_cards(self): fake_game = generate_game_for(StonetuskBoar, StonetuskBoar, DoNothingAgent, DoNothingAgent) overload_re = re.compile("Overload: \\((\\d)\\)") spell_damage_re = re.compile("Spell Damage \\+(\\d)") battlecry_re = re.compile("Battlecry: .*") deathrattle_re = re.compile("Deathrattle: .*") split_re = re.compile("\\s*\\.|\n\\s*") bold_tag_re = re.compile("</?b>") file = open("AllSets.enUS.json", "r", encoding="UTF-8") card_dict = json.load(file) not_implemented = [] total_cards = 0 for card_set in ['Classic', "Basic", "Curse of Naxxramas", "Goblins vs Gnomes", "Blackrock Mountain", "The Grand Tournament", "Reward", "Promotion"]: for card_info in card_dict[card_set]: if card_info["type"] in ['Minion', 'Spell', 'Weapon', 'Secret']: total_cards += 1 try: card = card_lookup(id_mappings[card_info["id"]]) except KeyError: if 'collectible' in card_info and card_info['collectible']: not_implemented.append("{}: ({})".format(card_info["name"], card_info['id'])) continue if "cost" in card_info: self.assertEqual(int(card_info["cost"]), card.mana, "Expected {} to have cost {}. Got {}".format( card_info["name"], card_info["cost"], card.mana)) if "playerClass" in card_info: self.assertEqual(CHARACTER_CLASS.from_str(card_info["playerClass"]), card.character_class, "Expected {} to have class {}. Got {}".format( card_info["name"], card_info["playerClass"], CHARACTER_CLASS.to_str(card.character_class))) else: self.assertEqual(CHARACTER_CLASS.ALL, card.character_class, "Expected {} to have no class. Got {}".format( card_info["name"], CHARACTER_CLASS.to_str(card.character_class))) if "rarity" in card_info: self.assertEqual(CARD_RARITY.from_str(card_info["rarity"]), card.rarity, "Expected card {} to have rarity {}. Got {}".format( card_info["name"], card_info["rarity"], CARD_RARITY.to_str(card.rarity))) if "collectible" in card_info: if card_info['collectible']: self.assertTrue(card.collectible, "Expected card {} to be collectible".format( card_info['name'])) else: self.assertFalse(card.collectible, "Expected card {} not to be collectible".format( card_info['name'])) if card_info["type"] == "Minion": minion = card.create_minion(fake_game.current_player) minion.player = fake_game.current_player minion.game = fake_game minion.card = card minion.add_to_board(0) if "text" in card_info: if card_info['name'] == "Argent Horserider": print(split_re.split(re.sub(bold_tag_re, "", card_info["text"]))) for effect in split_re.split(re.sub(bold_tag_re, "", card_info["text"])): if effect == "Taunt": self.assertTrue(minion.taunt, "Expected {:s} to have taunt".format(card_info["name"])) elif effect == "Divine Shield": self.assertTrue(minion.divine_shield, "Expected {:s} to have divine shield".format(card_info["name"])) elif effect == "Stealth": self.assertTrue(minion.stealth, "Expected {:s} to have stealth".format(card_info["name"])) elif effect == "Windfury": self.assertTrue(minion.windfury(), "Expected {:s} to have windfury".format(card_info["name"])) elif effect == "Charge": self.assertTrue(minion.charge(), "Expected {:s} to have charge".format(card_info["name"])) elif battlecry_re.match(effect): self.assertTrue(card.battlecry is not None, "Expected {:s} to have a battlecry".format (card_info["name"])) elif deathrattle_re.match(effect): self.assertTrue(minion.deathrattle is not None, "Expected {:s} to have a deathrattle".format (card_info["name"])) elif overload_re.match(effect) is not None: self.assertEqual(int(overload_re.match(effect).group(1)), card.overload, ("Expected {:s} to have overload of" + " {:s}, but had {:d}").format(card_info["name"], overload_re.match(effect).group(1), card.overload)) elif spell_damage_re.match(effect) is not None: self.assertEqual(int(spell_damage_re.match(effect).group(1)), minion.player.spell_damage, ("Expected {:s} to have spell damage of" + " {}, but had {}").format(card_info["name"], spell_damage_re.match(effect).group(1), minion.player.spell_damage)) minion.silence() self.assertEqual(int(card_info["attack"]), minion.calculate_attack(), "Expected {} to have attack of {}. Got {}".format( card_info["name"], card_info["attack"], minion.calculate_attack())) self.assertEqual(int(card_info["health"]), minion.health, "Expected {} to have health of {}. Got {}".format( card_info["name"], card_info["health"], minion.health)) if "race" in card_info: self.assertEqual(MINION_TYPE.from_str(card_info["race"]), card.minion_type, "Expected {} to have race {}. Got {}".format( card_info["name"], card_info["race"], MINION_TYPE.to_str(card.minion_type))) else: self.assertEqual(MINION_TYPE.NONE, card.minion_type, "Expected {} to have no race. Got {}".format( card_info["name"], MINION_TYPE.to_str(card.minion_type))) elif card_info["type"] == "Weapon": weapon = card.create_weapon(fake_game.current_player) self.assertEqual(int(card_info["attack"]), weapon.base_attack, "Expected {} to have attack of {}. Got {}".format( card_info["name"], card_info["attack"], weapon.base_attack)) self.assertEqual(int(card_info["durability"]), weapon.durability, "Expected {} to have durability of {}. Got {}".format( card_info["name"], card_info["durability"], weapon.durability)) file.close() if len(not_implemented) > 0: print("{} of {} cards implemented".format(total_cards - len(not_implemented), total_cards)) print("Cards not implemented:") for card in not_implemented: print(" - {}".format(card))
def get_card(self, target, player, owner): from hearthbreaker.engine import card_lookup, get_cards if self.name: chosen_card = card_lookup(self.name) chosen_card.attach(chosen_card, player) return chosen_card if self.source == CARD_SOURCE.COLLECTION: card_list = get_cards() elif self.source == CARD_SOURCE.MY_DECK: card_list = filter(lambda c: not c.drawn, player.deck.cards) elif self.source == CARD_SOURCE.MY_HAND: card_list = player.hand elif self.source == CARD_SOURCE.OPPONENT_DECK: card_list = filter(lambda c: not c.drawn, player.opponent.deck.cards) elif self.source == CARD_SOURCE.OPPONENT_HAND: card_list = player.opponent.hand elif self.source == CARD_SOURCE.LIST: card_list = self.source_list elif self.source == CARD_SOURCE.LAST_CARD: chosen_card = type(player.game.last_card)() chosen_card.attach(chosen_card, player) return chosen_card elif self.source == CARD_SOURCE.LAST_DRAWN: chosen_card = player.hand[-1] player.hand.remove(chosen_card) chosen_card.unattach() return chosen_card elif self.source == CARD_SOURCE.MINION: chosen_card = self.minion.get_targets(owner, owner)[0].card chosen_card.attach(chosen_card, player) return chosen_card elif self.source == CARD_SOURCE.MY_SECRETS: card_list = [secret.card for secret in player.secrets] elif self.source == CARD_SOURCE.ENEMY_SECRETS: card_list = [secret for secret in player.opponent.secrets] else: card_list = [] # TODO Throw an exception in any other case? def check_condition(condition): return lambda c: condition.evaluate(target, c) for condition in self.conditions: card_list = filter(check_condition(condition), card_list) card_list = [card for card in card_list] card_len = len(card_list) if card_len == 1: chosen_card = card_list[0] elif card_len == 0: return None else: chosen_card = player.game.random_choice(card_list) chosen_card.attach(chosen_card, player) if self.source == CARD_SOURCE.COLLECTION or self.source == CARD_SOURCE.LIST \ or self.source == CARD_SOURCE.MINION or self.make_copy: return chosen_card elif self.source == CARD_SOURCE.MY_DECK: chosen_card.drawn = True player.deck.left -= 1 return chosen_card elif self.source == CARD_SOURCE.OPPONENT_DECK: chosen_card.drawn = True player.opponent.deck.left -= 1 return chosen_card elif self.source == CARD_SOURCE.MY_HAND: player.hand.remove(chosen_card) chosen_card.unattach() return chosen_card elif self.source == CARD_SOURCE.OPPONENT_HAND: player.opponent.hand.remove(chosen_card) chosen_card.unattach() return chosen_card elif self.source == CARD_SOURCE.MY_SECRETS: if player is player.game.other_player: chosen_card.deactivate(player) player.secrets.remove(chosen_card) return chosen_card elif self.source == CARD_SOURCE.ENEMY_SECRETS: if player.opponent is player.game.other_player: chosen_card.deactivate(player.opponent) player.opponent.secrets.remove(chosen_card) return chosen_card