Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] release_above_cp #5337

Closed
theresthatguy opened this issue Sep 9, 2016 · 4 comments
Closed

[Feature Request] release_above_cp #5337

theresthatguy opened this issue Sep 9, 2016 · 4 comments

Comments

@theresthatguy
Copy link

theresthatguy commented Sep 9, 2016

Short Description

Release Pokemon above user defined CP or IV instead of below a certain threshold. I keep certain low CP pokemon for power leveling gyms by hand; as it is now, i must keep all of those pokemon and manually sort after closing bot.

Possible solution

As far as how it would look in the config, here's an example.

"Diglett": {"release_above_cp": 10, "release_below_iv": 0.9, "logic": "and"},
"Horsea": {"release_above_cp": 20, "release_below_iv": 0.9, "logic": "and"},
"Krabby": {"release_above_cp": 20, "release_below_iv": 0.9, "logic": "and"},
"Poliwag": {"release_above_cp": 20, "release_below_iv": 0.9, "logic": "and"}

This should allow to keep all pokemon below the specified cp and above specified IV, so for this example keep all below 10-20 cp (regardless of IV) and above 90% (regardless of cp) aka everything in the middle is transferred.

How it would help others

Less manual sorting of Pokemon.

@theresthatguy theresthatguy changed the title [Feature Request] release_above_x [Feature Request] release_above_cp Sep 9, 2016
@julienlavergne
Copy link
Contributor

Your "logic" in your example should be "and" because you are defining what you want to release.

@theresthatguy
Copy link
Author

Thanks!

@theresthatguy
Copy link
Author

theresthatguy commented Sep 10, 2016

This is what I have so far but it doesn't work as expected. Based on this release rule {"release_below_cp": 0, "release_below_iv": 0.35, "logic": "or"}, anything above IV should be kept but that doesn't happen. Full config example here.

import json
import os

from pokemongo_bot import inventory
from pokemongo_bot.human_behaviour import action_delay
from pokemongo_bot.base_task import BaseTask
from pokemongo_bot.inventory import Pokemons, Pokemon, Attack
from operator import attrgetter
from random import randrange


class TransferPokemon(BaseTask):
    SUPPORTED_TASK_API_VERSION = 1

    def __init__(self, bot, config):
        super(TransferPokemon, self).__init__(bot, config)

    def initialize(self):
        self.min_free_slot = self.config.get('min_free_slot', 5)
        self.transfer_wait_min = self.config.get('transfer_wait_min', 1)
        self.transfer_wait_max = self.config.get('transfer_wait_max', 4)

    def work(self):
        if not self._should_work():
            return

        pokemon_groups = self._release_pokemon_get_groups()
        for pokemon_id, group in pokemon_groups.iteritems():
            pokemon_name = Pokemons.name_for(pokemon_id)
            self._release_pokemon_worst_in_group(group, pokemon_name)

        if self.bot.config.release.get('all'):
            group = [p for p in inventory.pokemons().all()
                     if p.in_fort is False and p.is_favorite is False]
            self._release_pokemon_worst_in_group(group, 'all')

    def _should_work(self):
        random_number = randrange (0,20,1)
        return inventory.Pokemons.get_space_left() <= self.min_free_slot - random_number

    def _release_pokemon_get_groups(self):
        pokemon_groups = {}
        for pokemon in inventory.pokemons().all():
            if pokemon.in_fort or pokemon.is_favorite:
                continue

            group_id = pokemon.pokemon_id

            if group_id not in pokemon_groups:
                pokemon_groups[group_id] = []

            pokemon_groups[group_id].append(pokemon)

        return pokemon_groups

    def _release_pokemon_worst_in_group(self, group, pokemon_name):
        keep_best, keep_best_cp, keep_best_iv = self._validate_keep_best_config(
            pokemon_name)
        # TODO continue list possible criteria
        keep_best_possible_criteria = ['cp', 'iv', 'iv_attack', 'iv_defense', 'iv_stamina',
                                       'moveset.attack_perfection', 'moveset.defense_perfection', 'hp', 'hp_max']
        keep_best_custom, keep_best_criteria, keep_amount = self._validate_keep_best_config_custom(
            pokemon_name, keep_best_possible_criteria)

        best_pokemon_ids = set()
        order_criteria = 'none'
        if keep_best:
            if keep_best_cp >= 1:
                cp_limit = keep_best_cp
                best_cp_pokemons = sorted(group, key=lambda x: (
                    x.cp, x.iv), reverse=True)[:cp_limit]
                best_pokemon_ids = set(
                    pokemon.unique_id for pokemon in best_cp_pokemons)
                order_criteria = 'cp'

            if keep_best_iv >= 1:
                iv_limit = keep_best_iv
                best_iv_pokemons = sorted(group, key=lambda x: (
                    x.iv, x.cp), reverse=True)[:iv_limit]
                best_pokemon_ids |= set(
                    pokemon.unique_id for pokemon in best_iv_pokemons)
                if order_criteria == 'cp':
                    order_criteria = 'cp and iv'
                else:
                    order_criteria = 'iv'
        elif keep_best_custom:
            limit = keep_amount
            # not sure if the u of unicode will stay, so make it go away
            keep_best_criteria = [str(keep_best_criteria[x])
                                  for x in range(len(keep_best_criteria))]
            best_pokemons = sorted(group, key=attrgetter(
                *keep_best_criteria), reverse=True)[:limit]
            best_pokemon_ids = set(
                pokemon.unique_id for pokemon in best_pokemons)
            order_criteria = ' then '.join(keep_best_criteria)

        if keep_best or keep_best_custom:
            # remove best pokemons from all pokemons array
            all_pokemons = group
            best_pokemons = []
            for best_pokemon_id in best_pokemon_ids:
                for pokemon in all_pokemons:
                    if best_pokemon_id == pokemon.unique_id:
                        all_pokemons.remove(pokemon)
                        best_pokemons.append(pokemon)

            transfer_pokemons = [
                pokemon for pokemon in all_pokemons if self.should_release_pokemon(pokemon, True)]

            if transfer_pokemons:
                if best_pokemons:
                    self.emit_event(
                        'keep_best_release',
                        formatted="Keeping best {amount} {pokemon}, based on {criteria}",
                        data={
                            'amount': len(best_pokemons),
                            'pokemon': pokemon_name,
                            'criteria': order_criteria
                        }
                    )
                for pokemon in transfer_pokemons:
                    self.release_pokemon(pokemon)
        else:
            group = sorted(group, key=lambda x: x.cp, reverse=True)
            for pokemon in group:
                if self.should_release_pokemon(pokemon):
                    self.release_pokemon(pokemon)

    def should_release_pokemon(self, pokemon, keep_best_mode=False):
        release_config = self._get_release_config_for(pokemon.name)

        if (keep_best_mode
                and not release_config.has_key('never_release')
                and not release_config.has_key('always_release')
                and not release_config.has_key('release_below_cp')
                and not release_config.has_key('release_below_iv')):
            return True

        cp_iv_logic = release_config.get('logic')
        if not cp_iv_logic:
            cp_iv_logic = self._get_release_config_for(
                'any').get('logic', 'and')

        release_results = {
            'cp': False,
            'iv': False,
        }

        if release_config.get('never_release', False):
            return False

        if release_config.get('always_release', False):
            return True

        release_cpA = release_config.get('release_above_cp', 0)
        if pokemon.cp > release_cpA:
            release_results['cpA'] = True

        release_cpB = release_config.get('release_below_cp', 0)
        if pokemon.cp < release_cpB:
            release_results['cpB'] = True

        release_iv = release_config.get('release_below_iv', 0)
        if pokemon.iv < release_iv:
            release_results['iv'] = True

        logic_to_function = {
            'or': lambda x, y, z: (x or z) or (y or z),
            'and': lambda x, y, z: (x and z) or (y and z)
        }

        if logic_to_function[cp_iv_logic](*release_results.values()):
            self.emit_event(
                'future_pokemon_release',
                formatted="Releasing {pokemon} [CP {cp}] [IV {iv}] based on rule: CP < {below_cp} {cp_iv_logic} IV < {below_iv}",
                data={
                    'pokemon': pokemon.name,
                    'cp': pokemon.cp,
                    'iv': pokemon.iv,
                    'below_cp': release_cpB,
                    'cp_iv_logic': cp_iv_logic.upper(),
                    'below_iv': release_iv
                }
            )
        else:
            self.emit_event(
                'future_pokemon_release',
                formatted="Releasing {pokemon} [CP {cp}] [IV {iv}] based on rule: CP > {above_cp} {cp_iv_logic} IV < {below_iv}",
                data={
                    'pokemon': pokemon.name,
                    'cp': pokemon.cp,
                    'iv': pokemon.iv,
                    'above_cp': release_cpA,
                    'cp_iv_logic': cp_iv_logic.upper(),
                    'below_iv': release_iv
                }
            )

        return logic_to_function[cp_iv_logic](*release_results.values())

    def release_pokemon(self, pokemon):
        """

        :type pokemon: Pokemon
        """
        try:
            if self.bot.config.test:
                candy_awarded = 1
            else:
                response_dict = self.bot.api.release_pokemon(
                    pokemon_id=pokemon.unique_id)
                candy_awarded = response_dict['responses'][
                    'RELEASE_POKEMON']['candy_awarded']
        except KeyError:
            return

        # We could refresh here too, but adding 1 saves a inventory request
        candy = inventory.candies().get(pokemon.pokemon_id)
        candy.add(candy_awarded)
        inventory.pokemons().remove(pokemon.unique_id)
        self.bot.metrics.released_pokemon()
        self.emit_event(
            'pokemon_release',
            formatted='Exchanged {pokemon} [IV {iv}] [CP {cp}] [{candy} candies]',
            data={
                'pokemon': pokemon.name,
                'iv': pokemon.iv,
                'cp': pokemon.cp,
                'candy': candy.quantity
            }
        )
        with self.bot.database as conn:
            c = conn.cursor()
            c.execute(
                "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'")

        result = c.fetchone()

        while True:
            if result[0] == 1:
                conn.execute('''INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)''',
                             (pokemon.name, pokemon.iv, pokemon.cp))
                break
            else:
                self.emit_event(
                    'transfer_log',
                    sender=self,
                    level='info',
                    formatted="transfer_log table not found, skipping log"
                )
                break
        action_delay(self.transfer_wait_min, self.transfer_wait_max)

    def _get_release_config_for(self, pokemon):
        release_config = self.bot.config.release.get(pokemon)
        if not release_config:
            release_config = self.bot.config.release.get('any')
        if not release_config:
            release_config = {}
        return release_config

    def _validate_keep_best_config_custom(self, pokemon_name, keep_best_possible_custom):
        keep_best = False

        release_config = self._get_release_config_for(pokemon_name)
        keep_best_custom = release_config.get('keep_best_custom', '')
        keep_amount = release_config.get('amount', 0)

        if keep_best_custom and keep_amount:
            keep_best = True

            keep_best_custom = keep_best_custom.replace(' ','').split(',')
            for _str in keep_best_custom:
                if _str not in keep_best_possible_custom:
                    keep_best = False
                    break

            try:
                keep_amount = int(keep_amount)
            except ValueError:
                keep_best = False

            if keep_amount < 0:
                keep_best = False

        return keep_best, keep_best_custom, keep_amount

    def _validate_keep_best_config(self, pokemon_name):
        keep_best = False

        release_config = self._get_release_config_for(pokemon_name)

        keep_best_cp = release_config.get('keep_best_cp', 0)
        keep_best_iv = release_config.get('keep_best_iv', 0)

        if keep_best_cp or keep_best_iv:
            keep_best = True
            try:
                keep_best_cp = int(keep_best_cp)
            except ValueError:
                keep_best_cp = 0

            try:
                keep_best_iv = int(keep_best_iv)
            except ValueError:
                keep_best_iv = 0

            if keep_best_cp < 0 or keep_best_iv < 0:
                keep_best = False

            if keep_best_cp == 0 and keep_best_iv == 0:
                keep_best = False

        return keep_best, keep_best_cp, keep_best_iv

@Gobberwart
Copy link
Contributor

Should be addressed by PokemonOptimizer #5365

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants