示例#1
0
    def receive_damage(self,
                       amount: int,
                       own_board: PlayerBoard,
                       poisonous: Optional[bool] = False,
                       defer_damage_trigger: Optional[bool] = False):
        """Receive amount of damage which can be poisonous

        Arguments:
            amount {int} -- Amount of damage to receive
            poisonous {bool} -- Whether the damage is poisonous
            own_board {PlayerBoard} -- The board belonging to the minion taking damage

        Returns tuple:
            int -- Current health of the attacked minion (will be negative if its dead, can be used for overkill amount)
        """
        if amount <= 0:
            return self.health

        if self.divine_shield:
            self.divine_shield = False
            own_board.divine_shield_popped(self)
        else:  # Minion took damage
            self.health -= amount
            if self.health <= 0 or poisonous:
                self.dead = True
                self.dead_by_poison = True if poisonous else False
            if not defer_damage_trigger:
                self.on_receive_damage(own_board)
            else:
                own_board.deferred_damage_triggers.append(
                    self.on_receive_damage)
        return self.health
示例#2
0
def test_deathwing(initialized_game):
    initialized_game.player_board[0] = PlayerBoard(0, HeroType.DEATHWING, 1, 1,
                                                   [HarvestGolem()])
    initialized_game.player_board[1] = PlayerBoard(0,
                                                   None,
                                                   1,
                                                   1, [PunchingBag(attack=10)],
                                                   enemy_is_deathwing=True)

    initialized_game.start_of_game()
    initialized_game.single_round()
    assert initialized_game.player_board[0].minions[0].attack == 4

    initialized_game.player_board[0] = PlayerBoard(0,
                                                   HeroType.DEATHWING,
                                                   1,
                                                   1, [HarvestGolem()],
                                                   enemy_is_deathwing=True)
    initialized_game.player_board[1] = PlayerBoard(0,
                                                   HeroType.DEATHWING,
                                                   1,
                                                   1, [PunchingBag(attack=10)],
                                                   enemy_is_deathwing=True)
    initialized_game.start_of_game()
    initialized_game.single_round()
    assert initialized_game.player_board[0].minions[0].attack == 6
示例#3
0
 def update_bonus_attack(self, own_board: PlayerBoard, opposing_board: PlayerBoard):
     murlocs_current = own_board.count_minion_type(MinionType.Murloc) + opposing_board.count_minion_type(MinionType.Murloc) - 1  # Don't count itself
     murlocs_change = murlocs_current - self.number_murlocs
     bonus = murlocs_change * 2 if self.golden else murlocs_change
     
     self.attack += bonus
     self.number_murlocs = murlocs_current
示例#4
0
 def trigger(self, minion: Minion, own_board: PlayerBoard,
             opposing_board: PlayerBoard):
     number_rats = minion.last_attack
     logging.debug(
         f"Rat pack deathrattle triggered, creating {number_rats} rats")
     for rat in range(number_rats):
         own_board.add_minion(Rat(golden=minion.golden),
                              position=minion.position)
示例#5
0
 def trigger_reborn(self, own_board: PlayerBoard):
     """If the minion has reborn and has not triggered, trigger it"""
     if self.reborn and not self.reborn_triggered:
         reborn_at = self.find_deathrattle_reborn_position()
         self.__init__(reborn_triggered=True,
                       token=True,
                       attacked=self.attacked,
                       golden=self.golden,
                       position=reborn_at)
         own_board.add_minion(new_minion=self, position=reborn_at)
示例#6
0
def test_yshaarj(initialized_game):
    initialized_game.player_board[0] = PlayerBoard(0,
                                                   HeroType.YSHAARJ_ACTIVATED,
                                                   1, 1, [])
    initialized_game.player_board[1] = PlayerBoard(0, None, 1, 1, [])
    attacker_board = initialized_game.player_board[0]
    defender_board = initialized_game.player_board[1]

    initialized_game.start_of_game()
    initialized_game.single_round()
    assert attacker_board.minions[0].rank == 1
示例#7
0
 def trigger(self,
             minion: Minion,
             own_board: PlayerBoard,
             opposing_board: PlayerBoard,
             macaw_trigger: Optional[bool] = False):
     triggers = 2 if minion.golden else 1
     for _ in range(triggers):
         for opposing_minion in opposing_board.get_living_minions():
             opposing_minion.receive_damage(1, opposing_board)
         for friendly_minion in own_board.get_living_minions():
             friendly_minion.receive_damage(1, own_board)
示例#8
0
    def on_receive_damage(self, own_board: PlayerBoard):
        from utils.minion_utils import get_minions
        disallowed_demons = [
            "Amalgam", "Fiery Imp", "Imp", "Imp Mama", "Voidwalker"
        ]
        select_demons = lambda x: (MinionType.Demon in x.types) and (
            x.name not in disallowed_demons)
        demons = get_minions(select_demons)

        for _ in range(2 if self.golden else 1):
            demon = demons[random.randint(0, len(demons) - 1)](taunt=True)
            own_board.add_minion(demon, self.position + 1, True)
示例#9
0
def test_kurtrus_ashfallen(initialized_game):
    initialized_game.player_board[0] = PlayerBoard(
        0, HeroType.KURTRUS_ASHFALLEN_ACTIVATED, 1, 1, [AcolyteOfCThun()])
    initialized_game.player_board[1] = PlayerBoard(0, None, 1, 1,
                                                   [PunchingBag(attack=10)])

    initialized_game.start_of_game()
    initialized_game.single_round()
    acolyte = initialized_game.player_board[0].minions[0]

    # Reborn minions get the +2/+2 buff
    assert acolyte.attack == 4
    assert acolyte.health == 3
示例#10
0
def test_greybough(initialized_game):
    initialized_game.player_board[0] = PlayerBoard(0, HeroType.GREYBOUGH, 1, 1,
                                                   [HarvestGolem()])
    initialized_game.player_board[1] = PlayerBoard(0, None, 1, 1,
                                                   [PunchingBag(attack=10)])

    initialized_game.start_of_game()
    initialized_game.single_round()
    golem_token = initialized_game.player_board[0].minions[0]

    assert golem_token.attack == 3
    assert golem_token.health == 3
    assert golem_token.taunt
示例#11
0
    def check_deaths(self, attacking_player_board: PlayerBoard, defending_player_board: PlayerBoard):
        """Check deaths on both sides

        Arguments:
            attacking_player_board {PlayerBoard} -- Player board of attacking player
            defending_player_board {PlayerBoard} -- Player board of defending player
        """
        for minion in attacking_player_board.get_minions():
            if minion.check_death(attacking_player_board, defending_player_board):
                self.kill(minion, attacking_player_board, defending_player_board, minion_defending_player=False)
                return
        for minion in defending_player_board.get_minions():
            if minion.check_death(defending_player_board, attacking_player_board):
                self.kill(minion, attacking_player_board, defending_player_board, minion_defending_player=True)
                return
示例#12
0
 def trigger(self, minion: Minion, own_board: PlayerBoard,
             opposing_board: PlayerBoard):
     logging.debug(f"Spawn of NZoth deathrattle triggered")
     bonus = 2 if minion.golden else 1
     for other_minion in own_board.get_minions():
         other_minion.add_attack(bonus)
         other_minion.add_defense(bonus)
示例#13
0
 def trigger(self,
             minion: Minion,
             own_board: PlayerBoard,
             opposing_board: PlayerBoard,
             macaw_trigger: Optional[bool] = False):
     # Filter out un purchaseable elementals like curator token, water droplet, and elementals above current tech level
     not_allowed_elementals = ["Amalgam", "Water Droplet", "Gentle Djinni"]
     isSpawnable = lambda x: (MinionType.Elemental in x.types) and (
         x.name not in not_allowed_elementals)
     elementals_to_summon = 2 if minion.golden else 1
     # Golden genie summons two distinct random elementals
     Deathrattle.summon_random_minions(minion, own_board,
                                       elementals_to_summon, isSpawnable,
                                       macaw_trigger)
     for _ in range(elementals_to_summon):
         own_board.call_triggers(TriggerType.ON_CARD_PUT_IN_HAND, own_board)
示例#14
0
 def trigger(self,
             minion: Minion,
             own_board: PlayerBoard,
             opposing_board: PlayerBoard,
             macaw_trigger: Optional[bool] = False):
     bonus = 2 if minion.golden else 1
     for other_minion in own_board.get_living_minions():
         other_minion.add_stats(bonus, bonus)
示例#15
0
    def attack(self, attacking_minion: Minion, defending_minion: Minion,
               attacking_board: PlayerBoard, defending_board: PlayerBoard):
        """Let one minion attack the other

        Arguments:
            attacking_minion {Minion} -- Minion that attacks
            defending_minion {Minion} -- Minion that is attacked
        """
        # Pre-attack triggers
        attacking_board.call_triggers(TriggerType.ON_FRIENDLY_BEFORE_ATTACK,
                                      attacking_minion, attacking_board,
                                      defending_minion, defending_board)
        defending_board.call_triggers(TriggerType.ON_FRIENDLY_ATTACKED,
                                      defending_minion, defending_board,
                                      attacking_board)

        # If minion is killed by pyro spawn trigger, attack is cancelled
        if defending_minion.dead:
            attacking_minion.on_attack_after(attacking_board, defending_board)
            return

        # Deal attack damage
        if attacking_minion.cleave:
            # Minions hit with cleave should take damage all at once, but triggers resolve after
            neighbors = defending_board.get_minions_neighbors(defending_minion)
            defenders = [neighbors[0], defending_minion, neighbors[1]]
            for minion in [x for x in defenders if x]:
                self.deal_attack_damage(minion, defending_board,
                                        attacking_minion, attacking_board,
                                        True)
            self.deal_attack_damage(attacking_minion, attacking_board,
                                    defending_minion, defending_board)
        else:
            self.deal_attack_damage(defending_minion, defending_board,
                                    attacking_minion, attacking_board)
            self.deal_attack_damage(attacking_minion, attacking_board,
                                    defending_minion, defending_board)

        # Damage triggers from cleave and herald of flame should be resolved after attack and overkill trigger resolution
        for trigger in defending_board.deferred_damage_triggers:
            trigger(defending_board)
        defending_board.deferred_damage_triggers.clear()

        # Post attack triggers (macaw)
        attacking_minion.on_attack_after(attacking_board, defending_board)
示例#16
0
 def on_friendly_removal_after(self, other_minion: Minion, friendly_board: PlayerBoard, enemy_board: PlayerBoard):
     """After a friendly Demon dies, deal 3 damage to a random enemy minion.
     """
     triggers = 2 if self.golden else 1
     if MinionType.Demon in other_minion.types:
         for _ in range(triggers):
             target = enemy_board.random_minion()
             if target:
                 target.receive_damage(3, enemy_board)
示例#17
0
 def trigger(self,
             minion: Minion,
             own_board: PlayerBoard,
             opposing_board: PlayerBoard,
             macaw_trigger: Optional[bool] = False):
     for _ in range(2 if minion.golden else 1):
         other_minion = own_board.random_minion()
         if other_minion:
             other_minion.add_stats(0, minion.max_health)
示例#18
0
    def set_aside_dead_minions(self, own_board: PlayerBoard,
                               enemy_board: PlayerBoard):
        """Remove any dead minions. Return the List of minions to process any deathrattles and reborn triggers.

        Arguments:
            board {PlayerBoard} -- Player board to check for dead minions
        """
        dead_minions = own_board.select_dead()
        left_neighors = []
        for minion in dead_minions:
            left_neighors.append(minion.left_neighbor)

        # Dead minions care about their left neighbor for deathrattle/reborn positioning
        for index, minion in enumerate(dead_minions):
            own_board.remove_minion(minion, enemy_board)
            minion.left_neighbor = left_neighors[index]

        return dead_minions
示例#19
0
    def resolve_extra_attacks(self, attacking_player_board: PlayerBoard,
                              defending_player_board: PlayerBoard):
        """Resolve any 'attacks immediately' minions and the consequences of those attacks

        Arguments:
            attacking_player_board {PlayerBoard} -- Player board of attacking player
            defending_player_board {PlayerBoard} -- Player board of defending player
        """
        for attacker in attacking_player_board.get_immediate_attack_minions():
            attacker.immediate_attack_pending = False
            defender = defending_player_board.select_defending_minion()
            if defender:
                self.attack(attacker, defender, attacking_player_board,
                            defending_player_board)
                self.check_deaths(attacking_player_board,
                                  defending_player_board)
            else:
                return
示例#20
0
 def trigger(self,
             minion: Minion,
             own_board: PlayerBoard,
             opposing_board: PlayerBoard,
             macaw_trigger: Optional[bool] = False):
     number_bombs = 2 if minion.golden else 1
     for _ in range(number_bombs):
         opposing_minion = opposing_board.random_minion()
         if opposing_minion:
             opposing_minion.receive_damage(amount=4,
                                            own_board=opposing_board)
示例#21
0
    def kill(self, minion: Minion, minion_board: PlayerBoard, opposing_board: PlayerBoard, minion_defending_player: bool):
        """Kill a minion off and update board using deathrattles and other triggers

        Arguments:
            minion {Minion} -- Minion that will die
            minion_board {PlayerBoard} -- Player board belonging to the minion
            opposing_board {PlayerBoard} -- Board opposing of the minion that dies
            minion_defending_player {bool} -- Whether the minion died is on the defending side for trigger orders
        """
        # TODO: Baron
        if minion_defending_player:
            opposing_board.remove_minion(minion)
        else:
            minion_board.remove_minion(minion)
        for deathrattle in minion.deathrattles:
            if minion_defending_player:
                deathrattle.trigger(minion, opposing_board, minion_board)
            else:
                deathrattle.trigger(minion, minion_board, opposing_board)
        self.check_deaths(minion_board, opposing_board)
示例#22
0
 def trigger(self, minion: Minion, own_board: PlayerBoard,
             opposing_board: PlayerBoard):
     target_minion = own_board.random_minion()
     if target_minion:
         target_minion.add_attack(minion.last_attack)
         logging.debug(
             f"Fiendish Servant deathrattle triggers onto {target_minion.minion_string()}"
         )
     else:
         logging.debug(
             "Fiendish Servant deathrattle triggers but there are no targets left"
         )
示例#23
0
def test_illidan_stormrage(initialized_game):
    initialized_game.player_board[0] = PlayerBoard(0,
                                                   HeroType.ILLIDAN_STORMRAGE,
                                                   1, 1, [AcolyteOfCThun()])
    initialized_game.player_board[1] = PlayerBoard(
        0, None, 1, 1,
        [HarvestGolem(), PunchingBag(taunt=True)])
    attacker_board = initialized_game.player_board[0]
    defender_board = initialized_game.player_board[1]

    # NOTE: Illidan player bonus attacks trigger before combat, even though opponent has more minons
    initialized_game.start_of_game()
    acolyte = attacker_board.minions[0]
    punching_bag = defender_board.minions[1]
    assert acolyte.attack == 4
    assert punching_bag.health == 96

    # Deathrattles and pirate attacks should resolve
    punching_bag = PunchingBag(attack=1)
    attacker_board.set_minions([Scallywag(), Alleycat()])
    defender_board.set_minions([punching_bag])
    initialized_game.start_of_game()
    assert punching_bag.health == 92

    # Test windfury and attack order
    scallywag = Scallywag()
    punching_bag = PunchingBag()
    attacker_board.set_minions([scallywag, CracklingCyclone()])
    defender_board.set_minions([punching_bag])
    initialized_game.start_of_game()
    assert punching_bag.health == 84
    first_attacker = attacker_board.select_attacking_minion()
    assert first_attacker == scallywag

    # Single minion only attacks once
    single_minion = PunchingBag()
    attacker_board.set_minions([single_minion])
    defender_board.set_minions([PunchingBag()])
    initialized_game.start_of_game()
    assert single_minion.attack == 2
示例#24
0
 def trigger(self, minion: Minion, own_board: PlayerBoard,
             opposing_board: PlayerBoard):
     if minion.golden:
         logging.debug(
             "Kaboom Bot (golden) deathrattle triggered, dealing 4 damage twice"
         )
     else:
         logging.debug("Kaboom Bot deathrattle triggered, dealing 4 damage")
     number_bombs = 2 if minion.golden else 1
     for bomb in range(number_bombs):
         # TODO: Should kill the unit before throwing a second bomb
         opposing_minion = opposing_board.random_minion()
         opposing_minion.receive_damage(amount=4, poisonous=False)
示例#25
0
 def trigger(self,
             minion: Minion,
             own_board: PlayerBoard,
             opposing_board: PlayerBoard,
             macaw_trigger: Optional[bool] = False):
     iterations = 2 if minion.golden else 1
     for _ in range(iterations):
         unshielded_minions = [
             minion for minion in own_board.get_living_minions()
             if not minion.divine_shield
         ]
         if len(unshielded_minions) > 0:
             unshielded_minions[random.randint(0,
                                               len(unshielded_minions) -
                                               1)].divine_shield = True
示例#26
0
    def on_overkill(self, friendly_board: PlayerBoard,
                    defending_minion: Minion, enemy_board: PlayerBoard):
        overkill_amount = defending_minion.health * -1
        neighbors = [
            x for x in enemy_board.get_minions_neighbors(defending_minion) if x
        ]
        num_neighbors = len(neighbors)

        if num_neighbors > 0:
            if self.golden:
                for i in range(num_neighbors):
                    neighbors[i].receive_damage(overkill_amount, enemy_board)
            else:
                index = random.randint(0, num_neighbors - 1)
                neighbors[index].receive_damage(overkill_amount, enemy_board)
示例#27
0
    def determine_board_deathrattle_multiplier(self, own_board: PlayerBoard,
                                               count_self: bool):
        barons = [
            minion for minion in own_board.minions
            if minion.name == "Baron Rivendare"
        ]

        if count_self:
            barons += [self]

        if any(baron.golden for baron in barons):
            multiplier = 3
        elif len(barons) > 0:
            multiplier = 2
        else:
            multiplier = 1

        own_board.deathrattle_multiplier = multiplier
示例#28
0
def test_tonytwotusk(initialized_game):
    non_golden_pirate = DreadAdmiralEliza()
    bag = PunchingBag(attack=10)
    initialized_game.player_board[0] = PlayerBoard(
        0, HeroType.GREYBOUGH, 1, 1, [
            ReplicatingMenace(),
            HarvestGolem(), non_golden_pirate,
            TonyTwoTusk()
        ])
    attacker_board = initialized_game.player_board[0]
    defender_board = initialized_game.player_board[1]

    defender_board.set_minions([bag])
    initialized_game.start_of_game()
    for _ in range(5):
        initialized_game.single_round()
    assert non_golden_pirate.golden
    assert non_golden_pirate.health == non_golden_pirate.base_health * 2

    initialized_game.single_round()
    initialized_game.single_round()
    assert non_golden_pirate.attack == 16
    assert non_golden_pirate.health == 6
示例#29
0
import logging

from utils.profile import Profile

from game.game_instance import GameInstance
from game.simulation import Simulation
from game.player_board import PlayerBoard

from minions.rank_1 import DragonspawnLieutenant, FiendishServant, RedWhelp, RighteousProtector, Mecharoo

player_board_0 = PlayerBoard(player_id=0,
                             hero=None,
                             life_total=12,
                             rank=4,
                             minions=[
                                 FiendishServant(),
                                 DragonspawnLieutenant(),
                                 DragonspawnLieutenant(),
                                 RedWhelp(),
                                 RedWhelp()
                             ])
player_board_1 = PlayerBoard(player_id=1,
                             hero=None,
                             life_total=12,
                             rank=4,
                             minions=[
                                 DragonspawnLieutenant(),
                                 Mecharoo(),
                                 FiendishServant(),
                                 DragonspawnLieutenant(),
                                 FiendishServant(),
def simulate_game_from_log(logPath):
    logreader = LogReader(logPath)
    turns = 0
    turn_results = {}

    while True:
        board_state = logreader.watch_log_file_for_combat_state()

        if not board_state:
            break

        player_board_0 = PlayerBoard(
            player_id=0,
            hero=board_state.friendlyHero,
            life_total=board_state.friendlyPlayerHealth,
            rank=board_state.friendlyTechLevel,
            minions=board_state.friendlyBoard,
            enemy_is_deathwing=board_state.enemyHero is HeroType.DEATHWING)
        player_board_1 = PlayerBoard(
            player_id=1,
            hero=board_state.enemyHero,
            life_total=board_state.enemyPlayerHealth,
            rank=board_state.enemyTechLevel,
            minions=board_state.enemyBoard,
            enemy_is_deathwing=board_state.friendlyHero is HeroType.DEATHWING)

        try:
            single_threaded = False
            games = 10_000
            game_state = (player_board_0, player_board_1)
            pickled_state = pickle.dumps(game_state)

            if single_threaded:
                results = []
                for _ in range(games):
                    results.append(Simulator.Simulate(pickled_state))
            else:
                pool = Pool()
                results = pool.map(Simulator.Simulate,
                                   repeat(pickled_state, games))
                pool.close()
                pool.join()

            counter = Counter(results)
            results = sorted(counter.items(), key=lambda x: x[0])

            wins, losses, ties, enemy_lethal, friendly_lethal = 0.0, 0.0, 0.0, 0.0, 0.0
            for result in results:
                damage = result[0]
                game_count = result[1]

                if damage > 0:
                    wins += game_count
                    if damage > player_board_1.life_total:
                        enemy_lethal += game_count
                elif damage < 0:
                    losses += game_count
                    if (damage * -1) > player_board_0.life_total:
                        friendly_lethal += game_count
                else:
                    ties += game_count

            turn_results[turns] = [
                100 * enemy_lethal / games, 100 * wins / games,
                100 * ties / games, 100 * losses / games,
                100 * friendly_lethal / games
            ]
            turns += 1
        except Exception as e:
            print(f"Game:{logPath}, turn:{turns}, error:{e}")
            turn_results[turns] = [0, 0, 0, 0, 0]
            turns += 1
    return turn_results