Пример #1
0
def compute_scores(attacker):
    """Computes the scores of a single rental Pokemon against all other rental
    Pokemon and all boss Pokemon. Called by the multiprocessing pool.
    Parameters:
        attacker (Pokemon): the rental Pokemon to be scored.
    """

    logger = logging.getLogger(LOG_NAME)

    attacker_id = attacker.name_id

    # Load a separate dictionary for each process because elements get popped
    # and re-added during the matchup scoring process.
    with open(
        base_dir + '/data/rental_pokemon.json', 'r', encoding='utf8'
    ) as file:
        rental_pokemon = jsonpickle.decode(file.read())
    with open(
        base_dir + '/data/boss_pokemon.json', 'r', encoding='utf8'
    ) as file:
        boss_pokemon = jsonpickle.decode(file.read())

    # First iterate through all boss Pokemon and score the interactions.
    boss_matchups = {}
    for defender_id in tuple(boss_pokemon):
        defender = boss_pokemon[defender_id]
        score = matchup_scoring.evaluate_matchup(
            attacker, defender, rental_pokemon.values()
        )
        boss_matchups[defender_id] = score
        logger.debug(
            f'Matchup between {attacker_id} and {defender_id}: {score:.2f}'
        )

    # Then, iterate through all rental Pokemon and score the interactions.
    rental_matchups = {}
    rental_score = 0
    for defender_id in tuple(rental_pokemon):
        defender = rental_pokemon[defender_id]
        score = matchup_scoring.evaluate_matchup(
            attacker, defender, rental_pokemon.values()
        )
        rental_matchups[defender_id] = score
        # We sum the attacker's score which will later be normalized.
        rental_score += score
        logger.debug(
            f'Matchup between {attacker_id} and {defender_id}: {score:.2f}'
        )
    rental_score /= len(rental_pokemon)
    logger.debug(f'Score for {attacker_id}: {rental_score:.3f}')

    # Return the results as a tuple which will be unpacked and repacked in
    # dicts later, with the first element (the Pokemon's name identifier) as
    # the key and the other elements as values in their respective dicts.
    logger.info('Finished computing matchups for %s', attacker)
    return (attacker_id, boss_matchups, rental_matchups, rental_score)
def scientist(ctrlr) -> str:
    """Take (or not) a Pokemon from the scientist."""
    run = ctrlr.current_run

    ctrlr.log('Scientist encountered.', 'DEBUG')

    # Consider the amount of remaining minibosses when scoring each rental
    # Pokemon, at the start of the run, there are 3 - num_caught minibosses
    # and 1 final boss. We weigh the boss more heavily because it is more
    # difficult than the other bosses.
    rental_weight = 3 - run.num_caught
    boss_weight = 2

    # Calculate scores for an average and existing Pokemon.
    pokemon_scores = []
    for name_id in run.rental_pokemon:
        score = matchup_scoring.get_weighted_score(
            run.rental_scores[name_id], rental_weight,
            run.boss_matchups[name_id][ctrlr.boss], boss_weight
        )
        pokemon_scores.append(score)
    average_score = sum(pokemon_scores) / len(pokemon_scores)

    # TODO: actually read the current Pokemon's health so the bot can
    # decide to switch if it's low.
    existing_score = matchup_scoring.get_weighted_score(
        run.rental_scores[run.pokemon.name_id], rental_weight,
        matchup_scoring.evaluate_matchup(
            run.pokemon, run.boss_pokemon[ctrlr.boss],
            run.team_pokemon, run.lives
        ), boss_weight
    )
    ctrlr.log(f'Score for average pokemon: {average_score:.2f}', 'DEBUG')
    ctrlr.log(
        f'Score for {run.pokemon.name_id}: {existing_score:.2f}', 'DEBUG')

    if average_score > existing_score:
        # Note: a long delay is required here so the bot doesn't think a
        # battle started.
        ctrlr.push_buttons((None, 3), (b'a', 2 + VIDEO_EXTRA_DELAY))
        run.pokemon = None
        ctrlr.log('Took a Pokemon from the scientist.')
    else:
        # Note: a long delay is required here so the bot doesn't think a
        # battle started.
        ctrlr.push_buttons((None, 3), (b'b', 2 + VIDEO_EXTRA_DELAY))
        ctrlr.log(f'Decided to keep going with {run.pokemon.name_id}')

    # Read teammates.
    ctrlr.identify_team_pokemon()
    # If we took a Pokemon from the scientist, try to identify it.
    # Note: as of Python 3.6, dicts remember insertion order so using an
    # OrderedDict is unnecessary.
    if average_score > existing_score:
        ctrlr.log(f'Identified {run.pokemon.name_id} as our new Pokemon.')
    ctrlr.push_button(None, 5)

    return 'detect'
Пример #3
0
    def calculate_score(self, possible_pokemon, legendary):
        # just pass things through some kind of algorithm that determines an average score
        # *just* based on type

        the_score = 0.0

        for pokemon in possible_pokemon:
            the_score += matchup_scoring.evaluate_matchup(pokemon, legendary)

        the_score /= len(possible_pokemon)

        return the_score
Пример #4
0
def scientist(ctrlr) -> str:
    """Take (or not) a Pokemon from the scientist."""
    run = ctrlr.current_run

    ctrlr.log('Scientist encountered.', 'DEBUG')

    if run.pokemon is not None:
        # Consider the amount of remaining minibosses when scoring each rental
        # Pokemon, at the start of the run, there are 3 - num_caught minibosses
        # and 1 final boss. We weigh the boss more heavily because it is more
        # difficult than the other bosses.
        rental_weight = 3 - run.num_caught
        boss_weight = 2

        # Calculate scores for an average and existing Pokemon.
        pokemon_scores = []
        for name_id in run.rental_pokemon:
            score = matchup_scoring.get_weighted_score(
                run.rental_scores[name_id], rental_weight,
                run.boss_matchups[name_id][ctrlr.boss], boss_weight)
            pokemon_scores.append(score)
        average_score = sum(pokemon_scores) / len(pokemon_scores)

        # TODO: actually read the current Pokemon's health so the bot can
        # decide to switch if it's low.
        existing_score = matchup_scoring.get_weighted_score(
            run.rental_scores[run.pokemon.name_id], rental_weight,
            matchup_scoring.evaluate_matchup(
                run.pokemon, run.boss_pokemon[ctrlr.boss], run.rental_pokemon),
            boss_weight) * run.HP
        ctrlr.log(f'Score for average pokemon: {average_score:.2f}', 'DEBUG')
        ctrlr.log(f'Score for {run.pokemon.name_id}: {existing_score:.2f}',
                  'DEBUG')

    # If current pokemon is None, it means we just already talked to scientist
    # Also it means we took the pokemon from scientist.
    # So let's try to pick it up again
    if run.pokemon is None or average_score > existing_score:
        # Note: a long delay is required here so the bot doesn't think a
        # battle started.
        ctrlr.push_buttons((None, 3), (b'a', 7 + VIDEO_EXTRA_DELAY))
        run.pokemon = None
        ctrlr.log('Took a Pokemon from the scientist.')
    else:
        # Note: a long delay is required here so the bot doesn't think a
        # battle started.
        ctrlr.push_buttons((None, 3), (b'b', 7 + VIDEO_EXTRA_DELAY))
        ctrlr.log(f'Decided to keep going with {run.pokemon.name_id}')
    return 'detect'
def catch(ctrlr) -> str:
    """Catch each boss after defeating it."""
    run = ctrlr.current_run
    # Check if we need to skip catching a the final boss.
    # This scenario is used by Ball Saver mode when it can't afford to reset
    # the game.
    if (
        run.num_caught == 3 and ctrlr.mode == 'ball saver'
        and not ctrlr.check_sufficient_ore(1)
    ):
        ctrlr.log('Finishing the run without wasting a ball on the boss.')
        ctrlr.push_buttons((b'v', 2), (b'a', 10))
        ctrlr.log('Congratulations!')
        return 'select_pokemon'

    # Catch the boss in almost all cases.
    ctrlr.log(f'Catching boss #{run.num_caught + 1}.')
    # Start by navigating to the ball selection screen
    ctrlr.push_button(b'a', 2)
    # then navigate to the ball specified in the config file
    while (ctrlr.get_target_ball() not in ctrlr.check_ball()):
        ctrlr.push_button(b'<', 2 + VIDEO_EXTRA_DELAY)
    ctrlr.push_button(b'a', 30)
    ctrlr.record_ball_use()

    # If the caught Pokemon was not the final boss, check out the Pokemon and
    # decide whether to keep it.
    if run.num_caught < 4:
        # Note that read_selectable_pokemon returns a list of preconfigured
        # Pokemon objects with types, abilities, stats, moves, et cetera.
        #
        # In this stage the list contains only 1 item.
        pokemon = ctrlr.read_selectable_pokemon('catch')[0]
        run.caught_pokemon.append(pokemon.name_id)

        # Update the list of potential minibosses that we might encounter later
        # in the den.
        run.prune_potential_minibosses()
        ctrlr.log(
            'The following Pokemon have been encountered and will not appear '
            f'again: {run.all_encountered_pokemon}', 'DEBUG'
        )
        ctrlr.log(
            'The following Pokemon may still appear along the target path: '
            f'{[x for x in run.potential_boss_pokemon]}', 'DEBUG'
        )
        # Consider the amount of remaining minibosses when scoring each rental
        # Pokemon, at the start of the run, there are 3 - num_caught minibosses
        # and 1 final boss. We weigh the boss more heavily because it is more
        # difficult than the other bosses.
        rental_weight = 3 - run.num_caught
        boss_weight = 2

        # Calculate scores for every potential team resulting from the decision
        # to take or leave the new Pokemon.
        team = run.team_pokemon
        # Re-measure team HP.
        for i, HP in enumerate(ctrlr.measure_team_HP()):
            if i == 0:
                run.pokemon.HP = HP
            else:
                team[i - 1].HP = HP
        team_scores = []
        potential_teams = (
            (pokemon, team[0], team[1], team[2]),
            (run.pokemon, team[0], team[1], team[2]),
            (run.pokemon, pokemon, team[1], team[2]),
            (run.pokemon, team[0], pokemon, team[2]),
            (run.pokemon, team[0], team[1], pokemon)
        )
        ctrlr.log(f'HP of current team: {[x.HP for x in team]}.', 'DEBUG')
        for potential_team in potential_teams:
            score = matchup_scoring.get_weighted_score(
                matchup_scoring.evaluate_average_matchup(
                    potential_team[0], run.potential_boss_pokemon.values(),
                    potential_team[1:], run.lives
                ), rental_weight,
                matchup_scoring.evaluate_matchup(
                    potential_team[0], run.boss_pokemon[run.boss],
                    potential_team[1:], run.lives
                ), boss_weight
            )
            ctrlr.log(
                'Score for potential team: '
                f'{[x.name_id for x in potential_team]}: {score:.2f}', 'DEBUG')
            team_scores.append(score)
        # Choosing not to take the Pokemon may result in a teammate choosing
        # it, or none may choose it. The mechanics of your teammates choosing
        # is currently not known. Qualitatively, it seems like they choose yes
        # or no randomly with about 50% chance. Using this assumption, the
        # chance that none of them take the Pokemon is (1/2)^3 or 12.5%.
        choose_score = team_scores[0]
        leave_score = 0.125 * team_scores[1] + 0.875 * sum(team_scores[2:]) / 3
        ctrlr.log(
            f'Score for taking {pokemon.name_id}: {choose_score:.2f}', 'DEBUG')
        ctrlr.log(
            f'Score for declining {pokemon.name_id}: {leave_score:.2f}',
            'DEBUG')

        # Compare the scores for the two options and choose the best one.
        if choose_score > leave_score:
            # Choose to swap your existing Pokemon for the new Pokemon.
            run.pokemon = pokemon
            ctrlr.log(f'Decided to swap for {run.pokemon.name_id}.')
            # Note: a long delay is required here so the bot doesn't think a
            # battle started.
            ctrlr.push_button(b'a', 6)

        else:
            ctrlr.log(f'Decided to keep going with {run.pokemon.name_id}.')
            # Note: a long delay is required here so the bot doesn't think a
            # battle started.
            ctrlr.push_button(b'b', 6)

        # Re-read teammates in case something changed.
        ctrlr.identify_team_pokemon()
        run.prune_potential_minibosses()

        # Move on to the detect stage.
        return 'detect'

    # If the final boss was the caught Pokemon, wrap up the run and check the
    # Pokemon caught along the way.
    else:
        run.caught_pokemon.append(ctrlr.boss)
        ctrlr.push_button(None, 10)
        ctrlr.log('Congratulations!')
        return 'select_pokemon'
Пример #6
0
def catch(ctrlr) -> str:
    """Catch each boss after defeating it."""
    run = ctrlr.current_run
    # Check if we need to skip catching a the final boss.
    # This scenario is used by Ball Saver mode when it can't afford to reset
    # the game.
    if (run.num_caught == 3 and ctrlr.mode == 'ball saver'
            and not ctrlr.check_sufficient_ore(1)):
        ctrlr.log('Finishing the run without wasting a ball on the boss.')
        ctrlr.push_buttons((b'v', 2), (b'a', 30))
        ctrlr.log('Congratulations!')
        return 'select_pokemon'

    # Catch the boss in almost all cases.
    ctrlr.log(f'Catching boss #{run.num_caught + 1}.')
    # Start by navigating to the ball selection screen
    ctrlr.push_button(b'a', 2)
    # then navigate to the ball specified in the config file
    while (ctrlr.get_target_ball().lower() != 'default'
           and ctrlr.get_target_ball() not in ctrlr.check_ball()):
        ctrlr.push_button(b'<', 2 + VIDEO_EXTRA_DELAY)
    ctrlr.push_button(b'a', 30)
    ctrlr.record_ball_use()

    # If the caught Pokemon was not the final boss, check out the Pokemon and
    # decide whether to keep it.
    if run.num_caught < 4:
        # Note that read_selectable_pokemon returns a list of preconfigured
        # Pokemon objects with types, abilities, stats, moves, et cetera.
        #
        # In this stage the list contains only 1 item.
        pokemon = ctrlr.read_selectable_pokemon('catch')[0]
        # Consider the amount of remaining minibosses when scoring each rental
        # Pokemon, at the start of the run, there are 3 - num_caught minibosses
        # and 1 final boss. We weigh the boss more heavily because it is more
        # difficult than the other bosses.
        rental_weight = 3 - run.num_caught
        boss_weight = 2
        # Calculate scores for the new and existing Pokemon.
        # TODO: actually read the current Pokemon's health so the bot can
        # decide to switch if it's low.
        score = matchup_scoring.get_weighted_score(
            run.rental_scores[pokemon.name_id], rental_weight,
            run.boss_matchups[pokemon.name_id][ctrlr.boss], boss_weight)
        existing_score = matchup_scoring.get_weighted_score(
            run.rental_scores[run.pokemon.name_id], rental_weight,
            matchup_scoring.evaluate_matchup(
                run.pokemon, run.boss_pokemon[ctrlr.boss], run.rental_pokemon),
            boss_weight) * run.HP
        ctrlr.log(f'Score for {pokemon.name_id}: {score:.2f}', 'DEBUG')
        ctrlr.log(f'Score for {run.pokemon.name_id}: {existing_score:.2f}',
                  'DEBUG')

        run.caught_pokemon.append(pokemon.name_id)

        # Compare the scores for the two options and choose the best one.
        if score > existing_score:
            # Choose to swap your existing Pokemon for the new Pokemon.
            run.pokemon = pokemon
            # Note: a long delay is required here so the bot doesn't think a
            # battle started.
            ctrlr.push_button(b'a', 7)
            ctrlr.log(f'Decided to swap for {run.pokemon.name_id}.')
        else:
            # Note: a long delay is required here so the bot doesn't think a
            # battle started.
            ctrlr.push_button(b'b', 7)
            ctrlr.log(f'Decided to keep going with {run.pokemon.name_id}.')

        # Move on to the detect stage.
        return 'detect'

    # If the final boss was the caught Pokemon, wrap up the run and check the
    # Pokemon caught along the way.
    else:
        run.caught_pokemon.append(ctrlr.boss)
        ctrlr.push_button(None, 10)
        ctrlr.log('Congratulations!')
        return 'select_pokemon'