コード例 #1
0
    def find_best_move(self):
        state = self.create_state()
        my_options = self.get_all_options()[0]

        moves = []
        switches = []
        for option in my_options:
            if option.startswith(constants.SWITCH_STRING + " "):
                switches.append(option)
            else:
                moves.append(option)

        if self.force_switch or not moves:
            return format_decision(self, switches[0])

        most_damage = -1
        choice = None
        for move in moves:
            damage_amounts = calculate_damage(state, constants.SELF, move, constants.DO_NOTHING_MOVE)

            damage = damage_amounts[0] if damage_amounts else 0

            if damage > most_damage:
                choice = move
                most_damage = damage

        return format_decision(self, choice)
コード例 #2
0
    def test_burn_modifier_properly_halves_physical_damage(self):
        move = 'rockslide'

        self.venusaur.status = constants.BURN

        dmg = calculate_damage(self.venusaur, self.charizard, move, calc_type='max')
        self.assertEqual([134], dmg)
コード例 #3
0
    def test_burn_does_not_modify_special_move(self):
        move = 'fireblast'

        self.venusaur.status  = constants.BURN

        dmg = calculate_damage(self.charizard, self.venusaur, move, calc_type='max')
        self.assertEqual([300], dmg)
コード例 #4
0
    def test_earthquake_into_levitate_does_zero_damage(self):
        self.state.self.active.ability = 'levitate'

        damage_amounts = calculate_damage(self.state, constants.OPPONENT,
                                          'earthquake', 'splash')

        self.assertEqual(0, damage_amounts[0])
コード例 #5
0
    def test_boosts_properly_affect_damage_calculation(self):
        self.charizard.special_attack_boost = 2

        move = 'fireblast'

        dmg = calculate_damage(self.charizard, self.venusaur, move, calc_type='max')
        self.assertEqual([597], dmg)
コード例 #6
0
def check_choice_band_or_specs(battle, damage_dealt):
    if (battle.opponent.active is None
            or battle.opponent.active.item != constants.UNKNOWN_ITEM
            or damage_dealt.crit
            or damage_dealt.move in constants.WEIGHT_BASED_MOVES
            or damage_dealt.move in constants.SPEED_BASED_MOVES
            or not battle.opponent.active.can_have_choice_item):
        return

    try:
        move_dict = all_move_json[damage_dealt.move]
    except KeyError:
        logger.debug(
            "Could not find the move {}, skipping choice item check".format(
                move))
        return

    if move_dict[constants.CATEGORY] == constants.PHYSICAL:
        choice_item = 'choiceband'
        spread = 'adamant', '0,252,0,0,0,0'
    elif move_dict[constants.CATEGORY] == constants.SPECIAL:
        choice_item = 'choicespecs'
        spread = 'modest', '0,0,0,252,0,0'
    else:
        # don't guess anything if the move was neither physical nor special
        return

    if battle.battle_type == constants.RANDOM_BATTLE:
        spread = 'serious', '85,85,85,85,85,85'

    max_damage = float('-inf')
    potential_battles = battle.prepare_battles(guess_mega_evo_opponent=False,
                                               join_moves_together=True)

    battle_copy = deepcopy(battle)
    battle_copy.user.from_json(battle.request_json)
    for b in potential_battles:
        if b.opponent.active.item != choice_item:
            b.opponent.active.set_spread(*spread)
            b.user.active.stats = battle_copy.user.active.stats

            state = b.create_state()

            damage = calculate_damage(state,
                                      constants.OPPONENT,
                                      damage_dealt.move,
                                      battle.user.last_used_move.move,
                                      calc_type='max')[0]
            max_damage = max(max_damage, damage)

    # dont infer if we did not find a damage amount
    if max_damage == float('-inf'):
        return

    if (damage_dealt.percent_damage * battle.user.active.max_hp) > (
            max_damage * 1.2):  # multiply to avoid rounding errors
        logger.debug("{} has {}".format(battle.opponent.active.name,
                                        choice_item))
        battle.opponent.active.item = choice_item
コード例 #7
0
    def test_moldbreaker_ignores_levitate(self):
        self.state.self.active.ability = 'levitate'
        self.state.opponent.active.ability = 'moldbreaker'

        damage_amounts = calculate_damage(self.state, constants.OPPONENT,
                                          'earthquake', 'splash')

        self.assertNotEqual(0, damage_amounts[0])
コード例 #8
0
    def test_bots_reflect_does_not_reduce_its_own_damage(self):
        self.state.opponent.side_conditions[constants.REFLECT] = 1

        damage_amounts = calculate_damage(self.state, constants.OPPONENT,
                                          'earthquake', 'splash')

        # should do normal damage of 68
        # the attacker (opponent) having reflect up shouldn't change anything
        self.assertEqual(68, damage_amounts[0])
コード例 #9
0
    def test_sun_stab_and_2x_weakness(self):

        conditions = {
            'weather': constants.SUN
        }

        move = 'fireblast'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')
        self.assertEqual([450], dmg)
コード例 #10
0
    def test_rain_properly_amplifies_water_damage(self):

        conditions = {
            'weather': constants.RAIN
        }

        move = 'surf'

        dmg = calculate_damage(self.venusaur, self.charizard, move, conditions, calc_type='max')
        self.assertEqual([261], dmg)
コード例 #11
0
    def test_aurora_veil_properly_halves_damage(self):

        conditions = {
            'auroraveil': 1
        }

        move = 'fireblast'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')
        self.assertEqual([150], dmg)
コード例 #12
0
    def test_light_screen_properly_halves_damage(self):

        conditions = {
            'lightscreen': 1
        }

        move = 'psychic'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')
        self.assertEqual([82], dmg)
コード例 #13
0
    def test_reflect_properly_halves_damage(self):

        conditions = {
            'reflect': 1
        }

        move = 'rockslide'

        dmg = calculate_damage(self.venusaur, self.charizard, move, conditions, calc_type='max')
        self.assertEqual([134], dmg)
コード例 #14
0
    def test_sand_increases_rock_spdef(self):

        self.venusaur.types = ['rock']

        conditions = {
            'weather': constants.SAND
        }

        move = 'fireblast'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')
        self.assertEqual([51], dmg)
コード例 #15
0
    def test_damage_is_not_increased_if_attacker_is_not_grounded(self):
        self.charizard.types = ['fire', 'flying']

        conditions = {
            constants.TERRAIN: constants.PSYCHIC_TERRAIN
        }

        move = 'psychic'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')

        self.assertEqual([164], dmg)
コード例 #16
0
    def test_psychic_terrain_makes_priority_move_do_nothing(self):
        self.charizard.types = ['fire']

        conditions = {
            constants.TERRAIN: constants.PSYCHIC_TERRAIN
        }

        move = 'machpunch'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')

        self.assertEqual([0], dmg)
コード例 #17
0
    def test_sand_does_not_double_ground_spdef(self):

        self.venusaur.types = ['water']

        conditions = {
            'weather': constants.SAND
        }

        move = 'fireblast'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')
        self.assertEqual([75], dmg)
コード例 #18
0
    def test_grassy_terrain_increases_grass_type_move(self):
        self.charizard.types = ['fire']

        conditions = {
            constants.TERRAIN: constants.GRASSY_TERRAIN
        }

        move = 'gigadrain'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')

        # normally this is 17
        self.assertEqual([25], dmg)
コード例 #19
0
    def test_psychic_terrain_increases_psychic_damage(self):
        self.charizard.types = ['fire']

        conditions = {
            constants.TERRAIN: constants.PSYCHIC_TERRAIN
        }

        move = 'psychic'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')

        # normally this is 164
        self.assertEqual([246], dmg)
コード例 #20
0
    def test_misty_terrain_halves_dragon_moves(self):
        self.charizard.types = ['fire']

        conditions = {
            constants.TERRAIN: constants.MISTY_TERRAIN
        }

        move = 'outrage'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')

        # normally this is 103
        self.assertEqual([51], dmg)
コード例 #21
0
    def test_electric_terrain_increases_electric_damage_for_grounded_pokemon(self):
        self.charizard.types = ['fire']

        conditions = {
            constants.TERRAIN: constants.ELECTRIC_TERRAIN
        }

        move = 'thunderbolt'

        dmg = calculate_damage(self.charizard, self.venusaur, move, conditions, calc_type='max')

        # normally this is 41
        self.assertEqual([61], dmg)
コード例 #22
0
    def find_best_move(self):
        state = self.create_state()
        my_options = self.get_all_options()[0]

        moves = []
        switches = []
        for option in my_options:
            if option.startswith(constants.SWITCH_STRING + " "):
                switches.append(option)
            else:
                moves.append(option)

        if self.force_switch or not moves:
            return format_decision(self, switches[0])

        conditions = {
            constants.REFLECT:
            state.opponent.side_conditions[constants.REFLECT],
            constants.LIGHT_SCREEN:
            state.opponent.side_conditions[constants.LIGHT_SCREEN],
            constants.AURORA_VEIL:
            state.opponent.side_conditions[constants.AURORA_VEIL],
            constants.WEATHER:
            state.weather,
            constants.TERRAIN:
            state.field
        }

        most_damage = -1
        choice = None
        for move in moves:
            move_dict = all_move_json[move]
            attacking_move = update_attacking_move(state.self.active,
                                                   state.opponent.active,
                                                   move_dict, {}, False,
                                                   state.weather)
            damage_amounts = calculate_damage(state.self.active,
                                              state.opponent.active,
                                              attacking_move,
                                              conditions=conditions)
            damage = damage_amounts[0] if damage_amounts else 0

            if damage > most_damage:
                choice = move
                most_damage = damage

        return format_decision(self, choice)
コード例 #23
0
    def test_4x_resistance_calculates_properly(self):
        move = 'gigadrain'

        dmg = calculate_damage(self.venusaur, self.charizard, move, calc_type='max')
        self.assertEqual([27], dmg)
コード例 #24
0
    def test_flashfire_increases_fire_move_damage(self):
        move = 'fireblast'
        self.charizard.volatile_status.add('flashfire')

        dmg = calculate_damage(self.charizard, self.venusaur, move, calc_type='max')
        self.assertEqual([450], dmg)
コード例 #25
0
    def test_stab_without_weakness_calculates_properly(self):
        move = 'sludgebomb'

        dmg = calculate_damage(self.venusaur, self.charizard, move, calc_type='max')
        self.assertEqual([130], dmg)
コード例 #26
0
    def test_4x_weakness_calculates_properly(self):
        move = 'rockslide'

        dmg = calculate_damage(self.venusaur, self.charizard, move, calc_type='max')
        self.assertEqual([268], dmg)
コード例 #27
0
    def test_solarbeam_move_produces_damage_amount(self):
        damage_amounts = calculate_damage(self.state, constants.OPPONENT,
                                          'solarbeam', 'splash')

        self.assertNotEqual(0, damage_amounts[0])
コード例 #28
0
def check_choice_band_or_specs(battle, damage_dealt):
    if (battle.opponent.active is None
            or battle.opponent.active.item != constants.UNKNOWN_ITEM
            or damage_dealt.crit
            or damage_dealt.move in constants.WEIGHT_BASED_MOVES
            or damage_dealt.move in constants.SPEED_BASED_MOVES
            or not battle.opponent.active.can_have_choice_item):
        return

    try:
        move_dict = all_move_json[damage_dealt.move]
    except KeyError:
        logger.debug(
            "Could not find the move {}, skipping choice item check".format(
                move))
        return

    if move_dict[constants.CATEGORY] == constants.PHYSICAL:
        choice_item = 'choiceband'
        spread = 'adamant', '0,252,0,0,0,0'
    elif move_dict[constants.CATEGORY] == constants.SPECIAL:
        choice_item = 'choicespecs'
        spread = 'modest', '0,0,0,252,0,0'
    else:
        # don't guess anything if the move was neither physical nor special
        return

    if battle.battle_type == constants.RANDOM_BATTLE:
        spread = 'serious', '85,85,85,85,85,85'

    min_damage_with_choice_item = float('inf')
    max_damage_without_choice_item = float('-inf')
    potential_battles = battle.prepare_battles(guess_mega_evo_opponent=False,
                                               join_moves_together=True)

    battle_copy = deepcopy(battle)
    battle_copy.user.from_json(battle.request_json)
    for b in potential_battles:

        # if the item is not the choice item - use it to find the max damage roll possible for all items
        if b.opponent.active.item != choice_item:
            b.opponent.active.set_spread(*spread)
            b.user.active.stats = battle_copy.user.active.stats

            state = b.create_state()

            damage = calculate_damage(state,
                                      constants.OPPONENT,
                                      damage_dealt.move,
                                      battle.user.last_used_move.move,
                                      calc_type='max')[0]
            max_damage_without_choice_item = max(
                max_damage_without_choice_item, damage)

        # also find the min damage roll possible for the choice-item
        b.opponent.active.item = choice_item
        b.opponent.active.set_spread(*spread)
        b.user.active.stats = battle_copy.user.active.stats

        state = b.create_state()

        damage = calculate_damage(state,
                                  constants.OPPONENT,
                                  damage_dealt.move,
                                  battle.user.last_used_move.move,
                                  calc_type='min')[0]
        min_damage_with_choice_item = min(min_damage_with_choice_item, damage)

    # dont infer if we did not find a damage amount
    if max_damage_without_choice_item == float(
            '-inf') or min_damage_with_choice_item == float('inf'):
        return

    actual_damage_dealt = damage_dealt.percent_damage * battle.user.active.max_hp

    # if the damage dealt is more than 1.2x the max-roll WITHOUT a choice item then the pkmn DOES have a choice-item
    if actual_damage_dealt > (max_damage_without_choice_item *
                              1.2):  # multiply to avoid rounding errors
        logger.debug("{} has {}".format(battle.opponent.active.name,
                                        choice_item))
        battle.opponent.active.item = choice_item

    # if the damage dealt is less than 0.8x the min-roll given a choice-item then the pkmn DOES NOT have a choice-item
    if (actual_damage_dealt < (min_damage_with_choice_item * 0.8)
            and  # multiply to avoid rounding errors
        (battle.user.active.hp - actual_damage_dealt) >
            1  # this is checking if the move KO-ed
            # if it did, we do not want to set this flag
            # Check for greater than 1 to avoid rounding errors
        ):
        logger.debug("{} did not do enough damage to have {}".format(
            battle.opponent.active.name, choice_item))
        if choice_item == "choiceband":
            battle.opponent.active.can_not_have_band = True
        elif choice_item == "choicespecs":
            battle.opponent.active.can_not_have_specs = True
        else:
            raise ValueError("{} is neither 'choiceband' or 'choicespecs'")
コード例 #29
0
    def test_phantomforce_move_produces_damage_amount(self):
        damage_amounts = calculate_damage(self.state, constants.OPPONENT,
                                          'phantomforce', 'splash')

        self.assertNotEqual(0, damage_amounts[0])
コード例 #30
0
    def test_immunity_calculates_properly(self):
        move = 'earthquake'

        dmg = calculate_damage(self.venusaur, self.charizard, move, calc_type='max')
        self.assertEqual([0], dmg)