def _useless_self_hit(self, battle, order: BattleOrder):

        # Eliminate easy conditions in which this is not a useless self hit
        if not order or not order.is_move(): return False
        if not (order.order.damage or order.order.base_power > 0): return False
        if order.order.self_switch: return False

        # If it's a self-hit
        affected_targets = BattleOrder.get_affected_targets(battle, order)
        if affected_targets and min(affected_targets) < 0:

            # Get the mon who is going to be hit
            target_mon = battle.active_pokemon[min(affected_targets)]

            # Only allow this as a potential move under these conditions
            if target_mon.item == 'weaknesspolicy' and order.order.type.damage_multiplier(
                    *target_mon.types) >= 2:
                return True
            elif target_mon.ability == 'Berserk':
                return False
            elif target_mon.ability == 'Justified' and order.order.type == 'DARK':
                return False
            elif target_mon.ability == 'Water Absorb' and order.order.type == 'WATER':
                return False
            elif target_mon.ability == 'Volt Absorb' and order.order.type == 'ELECTRIC':
                return False
            elif target_mon.ability == 'Flash Fire' and order.order.type == 'FIRE':
                return False
            else:
                return True

        return False
예제 #2
0
    def choose_random_singles_move(self, battle: Battle) -> BattleOrder:
        available_orders = [BattleOrder(move) for move in battle.available_moves]
        available_orders.extend(
            [BattleOrder(switch) for switch in battle.available_switches]
        )

        if battle.can_mega_evolve:
            available_orders.extend(
                [BattleOrder(move, mega=True) for move in battle.available_moves]
            )

        if battle.can_dynamax:
            available_orders.extend(
                [BattleOrder(move, dynamax=True) for move in battle.available_moves]
            )

        if battle.can_z_move and battle.active_pokemon:
            available_z_moves = set(battle.active_pokemon.available_z_moves)
            available_orders.extend(
                [
                    BattleOrder(move, z_move=True)
                    for move in battle.available_moves
                    if move in available_z_moves
                ]
            )

        if available_orders:
            return available_orders[int(random.random() * len(available_orders))]
        else:
            return self.choose_default_move(battle)
예제 #3
0
    def _action_to_single_move(self, action: int, index: int, battle):

        if action < 24:
            # If either there is no mon or we're forced to switch, there's nothing to do
            if not battle.active_pokemon[index] or battle.force_switch[index]:
                return None
            dynamax, remaining = action % 2 == 1, int(action / 2)
            if battle.active_pokemon[index] and int(remaining / 3) < len(
                    battle.active_pokemon[index].moves):
                move, initial_target = list(
                    battle.active_pokemon[index].moves.values())[int(
                        remaining / 3)], remaining % 3

                # If there's no target needed, we create the action as we normally would. It doesn't matter what our AI returned as target since there's only one possible target
                if move.deduced_target not in [
                        'adjacentAlly', 'adjacentAllyOrSelf', 'any', 'normal'
                ]:
                    return BattleOrder(order=move,
                                       actor=battle.active_pokemon[index],
                                       dynamax=dynamax)

                # If we are targeting a single mon, there are three cases: your other mon, the opponents mon or their other mon.
                # 2 corresponds to your mon and 0/1 correspond to the opponents mons (index in opponent_active_mon)
                # For the self-taret case, we ensure there's another mon on our side to hit (otherwise we leave action1 as None)
                elif initial_target == 2:
                    if battle.active_pokemon[1] is not None:
                        return BattleOrder(order=move,
                                           move_target=battle.
                                           active_pokemon_to_showdown_target(
                                               1 - index, opp=False),
                                           actor=battle.active_pokemon[index],
                                           dynamax=dynamax)

                # In the last case (if initial_target is 0 or 1), we target the opponent, and we do it regardless of what slot was
                # chosen if there's only 1 mon left. In the following cases, we handle whether there are two mons left or one mon left
                elif len(battle.opponent_active_pokemon) == 2 and all(
                        battle.opponent_active_pokemon):
                    return BattleOrder(
                        order=move,
                        move_target=battle.active_pokemon_to_showdown_target(
                            initial_target, opp=True),
                        actor=battle.active_pokemon[index],
                        dynamax=dynamax)
                elif len(battle.opponent_active_pokemon) < 2 and any(
                        battle.opponent_active_pokemon):
                    initial_target = 1 if battle.opponent_active_pokemon[
                        0] is not None else 0
                    return BattleOrder(
                        order=move,
                        move_target=battle.active_pokemon_to_showdown_target(
                            initial_target, opp=True),
                        actor=battle.active_pokemon[index],
                        dynamax=dynamax)

        elif 25 - action < len(battle.available_switches[index]):
            return BattleOrder(order=battle.available_switches[index][25 -
                                                                      action],
                               actor=battle.active_pokemon[index])

        return None
예제 #4
0
    def choose_random_doubles_move(self, battle: DoubleBattle) -> BattleOrder:
        active_orders = [[], []]

        for (
                idx,
            (orders, mon, switches, moves, can_mega, can_z_move, can_dynamax),
        ) in enumerate(
                zip(
                    active_orders,
                    battle.active_pokemon,
                    battle.available_switches,
                    battle.available_moves,
                    battle.can_mega_evolve,
                    battle.can_z_move,
                    battle.can_dynamax,
                )):
            if mon:
                targets = {
                    move: battle.get_possible_showdown_targets(move, mon)
                    for move in moves
                }
                orders.extend([
                    BattleOrder(move, move_target=target) for move in moves
                    for target in targets[move]
                ])
                orders.extend([BattleOrder(switch) for switch in switches])

                if can_mega:
                    orders.extend([
                        BattleOrder(move, move_target=target, mega=True)
                        for move in moves for target in targets[move]
                    ])
                if can_z_move:
                    available_z_moves = set(mon.available_z_moves)
                    orders.extend([
                        BattleOrder(move, move_target=target, z_move=True)
                        for move in moves for target in targets[move]
                        if move in available_z_moves
                    ])

                if can_dynamax:
                    orders.extend([
                        BattleOrder(move, move_target=target, dynamax=True)
                        for move in moves for target in targets[move]
                    ])

                if sum(battle.force_switch) == 1:
                    if orders:
                        return orders[int(random.random() * len(orders))]
                    return self.choose_default_move()

        orders = DoubleBattleOrder.join_orders(*active_orders)

        if orders:
            return orders[int(random.random() * len(orders))]
        else:
            return DefaultBattleOrder()
예제 #5
0
def test_single_orders():
    move = Move("flamethrower")
    assert BattleOrder(move).message == "/choose move flamethrower"

    assert BattleOrder(move,
                       mega=True).message == "/choose move flamethrower mega"

    assert BattleOrder(
        move, z_move=True).message == "/choose move flamethrower zmove"

    mon = Pokemon(species="charizard")
    assert BattleOrder(mon).message == "/choose switch charizard"
예제 #6
0
    def create_order(
        order: Union[Move, Pokemon],
        mega: bool = False,
        z_move: bool = False,
        dynamax: bool = False,
        move_target: int = DoubleBattle.EMPTY_TARGET_POSITION,
    ) -> BattleOrder:
        """Formats an move order corresponding to the provided pokemon or move.

        :param order: Move to make or Pokemon to switch to.
        :type order: Move or Pokemon
        :param mega: Whether to mega evolve the pokemon, if a move is chosen.
        :type mega: bool
        :param z_move: Whether to make a zmove, if a move is chosen.
        :type z_move: bool
        :param dynamax: Whether to dynamax, if a move is chosen.
        :type dynamax: bool
        :param move_target: Target Pokemon slot of a given move
        :type move_target: int
        :return: Formatted move order
        :rtype: str
        """
        return BattleOrder(
            order, mega=mega, move_target=move_target, z_move=z_move, dynamax=dynamax
        )
예제 #7
0
def test_double_orders():
    move = BattleOrder(Move("selfdestruct"), move_target=2)
    mon = BattleOrder(Pokemon(species="lugia"))

    assert (DoubleBattleOrder(
        move, mon).message == "/choose move selfdestruct 2, switch lugia")
    assert (DoubleBattleOrder(
        mon, move).message == "/choose switch lugia, move selfdestruct 2")
    assert DoubleBattleOrder(mon).message == "/choose switch lugia, default"
    assert (DoubleBattleOrder(
        None, move).message == "/choose move selfdestruct 2, default")
    assert DoubleBattleOrder().message == "/choose default"

    orders = [move, mon]

    both = {
        order.message
        for order in DoubleBattleOrder.join_orders(orders, orders)
    }
    first = {
        order.message
        for order in DoubleBattleOrder.join_orders(orders, [])
    }
    second = {
        order.message
        for order in DoubleBattleOrder.join_orders([], orders)
    }
    none = {order.message for order in DoubleBattleOrder.join_orders([], [])}

    assert both == {
        "/choose move selfdestruct 2, switch lugia",
        "/choose switch lugia, move selfdestruct 2",
    }
    assert first == {
        "/choose move selfdestruct 2, default",
        "/choose switch lugia, default",
    }
    assert second == {
        "/choose move selfdestruct 2, default",
        "/choose switch lugia, default",
    }
    assert none == {"/choose default"}
    def _useless_battle_condition(self, battle, order: BattleOrder):
        if not order or not order.is_move(): return False

        if order.order.side_condition and order.order.side_condition in battle.side_conditions:
            return True
        if order.order.weather and battle.weather and order.order.weather == battle.weather:
            return True
        if order.order.terrain and battle.fields and order.order.terrain in battle.fields:
            return True
        if order.order.pseudo_weather and battle.fields and order.order.pseudo_weather in battle.fields:
            return True
        return False
    def _useless_self_boost(self, order: BattleOrder):
        if order and order.is_move():

            # Only consider self- or ally-boosting moves if you have boosts left, or if you dont, if the other pokemon has sucker punch
            if order.order.boosts and order.order.target == 'self':
                num_failed = 0
                for stat in order.order.boosts:
                    if (order.actor.boosts[stat] == 6
                            and order.order.boosts[stat] > 0) or (
                                order.order.boosts[stat] == -6
                                and order.order.boosts[stat] < 0):
                        num_failed += 1
                if num_failed < len(order.order.boosts):
                    return True
        return False
예제 #10
0
    def _convertPartialMessage(self, battle, msg, index):
        if msg == self._DEFAULT_ORDER: return DefaultBattleOrder()

        order, target, mega, dynamax, z_move = None, None, False, False, False
        if 'switch' in msg:
            order = Pokemon(species=re.search("switch\s([a-zA-Z\-\_0-9]+).*",
                                              msg).group(1).lower())
        elif 'move' in msg:
            tokens = msg.split(" ")
            order = Move(tokens[1])

            if len(tokens) <= 2: target = DoubleBattle.EMPTY_TARGET_POSITION
            else:
                target = tokens[2]
                if target.strip().isdigit(): target = int(target)
                elif battle.opponent_active_pokemon[0].species == target.strip(
                ):
                    target = active_pokemon_to_showdown_target(0, opp=True)
                elif battle.opponent_active_pokemon[1].species == target.strip(
                ):
                    target = active_pokemon_to_showdown_target(1, opp=True)
                elif battle.active_pokemon[1 -
                                           index].species == target.strip():
                    target = active_pokemon_to_showdown_target(1 - index,
                                                               opp=False)

        if ' mega' in msg: mega = True
        if 'dynamax' in msg: dynamax = True
        if 'z_move' in msg: z_move = True

        return BattleOrder(order,
                           move_target=target,
                           actor=battle.active_pokemon[index],
                           mega=mega,
                           dynamax=dynamax,
                           z_move=z_move)
    def choose_move(self, battle):

        best_order = DefaultBattleOrder()

        # If we're not being forced to switch and are choosing our moves
        if not any(battle.force_switch):

            # Go through and get actions, filter them down to what's possible, and then eliminate ones that dont make sense
            orders = self.get_all_doubles_moves(battle)
            filtered_orders = list(
                filter(lambda x: x and DoubleBattleOrder.is_valid(battle, x),
                       orders))

            # Iterate through all actions to pick best short-term move. These are our stored values
            most_damage, best_switch_multiplier = 0, 0

            # Go through every reasonable pair of actions and pick the pair that does the most high damage moves and multipliers of switch
            for double_order in filtered_orders:
                damage, switch_multiplier = 0, 0

                # Add up damage I'm probably going to do and switch multipliers compared to active pokemon
                for order in [
                        double_order.first_order, double_order.second_order
                ]:
                    if not order: continue

                    # If damaging move, Go through each potential target and add up damage (subtract if self-damage)
                    if order.is_move() and (order.order.damage
                                            or order.order.base_power > 0):
                        targets = BattleOrder.get_affected_targets(
                            battle, order)
                        if targets == None: targets = []

                        for target in targets:
                            stab = 1.5 if order.order.type in order.actor.types else 1
                            target_mon = utils.showdown_target_to_mon(
                                battle, target)

                            effectiveness = order.order.type.damage_multiplier(
                                *target_mon.types
                            ) if target_mon is not None else 1
                            base_power = order.order.base_power

                            damage += base_power * stab * effectiveness * (
                                -1 if target < 0 else 1)

                            if order.dynamax: damage += 1

                    # Calculate whether we're going to switch into an good environment (wrt types)
                    elif order.is_switch():
                        switch_multiplier += np.mean([
                            compute_type_advantage(order.actor, opp)
                            for opp in filter(lambda x: x is not None,
                                              battle.opponent_active_pokemon)
                        ])

                # Choose move if it does highest damage, and then if tied, the one that has the best switch
                if damage > most_damage:
                    best_order, most_damage, best_switch_multiplier = double_order, damage, switch_multiplier
                elif damage == most_damage and switch_multiplier >= best_switch_multiplier:
                    best_order, most_damage, best_switch_multiplier = double_order, damage, switch_multiplier

        # Force Switch situation; pick switch that has the best type advantage against the opponent's active mons
        else:
            orders = self.get_all_doubles_moves(battle)
            filtered_orders = list(
                filter(lambda x: DoubleBattleOrder.is_valid(battle, x),
                       orders))

            # Go through every possible switch and choose one that has best type advantage against opponent's mon
            multiplier = -np.inf
            for double_order in filtered_orders:
                if not double_order.first_order or not double_order.first_order.is_switch(
                ):
                    continue

                # Store the score if there's a better switch
                multipliers = []
                for opp in battle.opponent_active_pokemon:
                    if opp is not None:
                        multipliers.append(
                            compute_type_advantage(
                                double_order.first_order.actor, opp))

                if np.mean(multipliers) > multiplier:
                    best_order, multiplier = double_order, np.mean(multipliers)

        return best_order
예제 #12
0
def test_recharge_order():
    recharge = SPECIAL_MOVES["recharge"]
    assert BattleOrder(recharge).message == "/choose move 1"
예제 #13
0
    def get_all_doubles_moves(self,
                              battle: DoubleBattle) -> List[DoubleBattleOrder]:
        active_orders = [[], []]

        for (
                idx,
            (orders, mon, switches, moves, can_mega, can_z_move, can_dynamax),
        ) in enumerate(
                zip(
                    active_orders,
                    battle.active_pokemon,
                    battle.available_switches,
                    battle.available_moves,
                    battle.can_mega_evolve,
                    battle.can_z_move,
                    battle.can_dynamax,
                )):
            if mon:
                targets = {
                    move: battle.get_possible_showdown_targets(move, mon)
                    for move in moves
                }
                orders.extend([
                    BattleOrder(move, actor=mon, move_target=target)
                    for move in moves for target in targets[move]
                ])
                orders.extend(
                    [BattleOrder(switch, actor=mon) for switch in switches])

                if can_mega:
                    orders.extend([
                        BattleOrder(move,
                                    actor=mon,
                                    move_target=target,
                                    mega=True) for move in moves
                        for target in targets[move]
                    ])
                if can_z_move:
                    available_z_moves = set(mon.available_z_moves)
                    orders.extend([
                        BattleOrder(move,
                                    actor=mon,
                                    move_target=target,
                                    z_move=True) for move in moves
                        for target in targets[move]
                        if move in available_z_moves
                    ])

                if can_dynamax:
                    orders.extend([
                        BattleOrder(move,
                                    actor=mon,
                                    move_target=target,
                                    dynamax=True) for move in moves
                        for target in battle.get_possible_showdown_targets(
                            move, mon, dynamax=True)
                    ])

                if sum(battle.force_switch) == 1:
                    if orders:
                        return list(
                            map(
                                lambda x: DoubleBattleOrder(first_order=x,
                                                            second_order=None),
                                orders))
                    return None

        return DoubleBattleOrder.join_orders(*active_orders)