From 1e22ae1b128a653caa6cfb55de89035c13091bbc Mon Sep 17 00:00:00 2001 From: Zhou Date: Mon, 1 Aug 2016 12:46:11 -0700 Subject: [PATCH] Evolve only if evolvable candidates no less than a certain number. Previously, when user configured use_lucky_egg as true, the bot will use lucky egg even evolvale candidates is zero. That's kinda of waste. Meanwhile, if user didn't configured use_lucky_egg, the bot will try to evolve after sort by cp&iv, but sometimes no good potential pokemon in that batch, it's better delay the evolution after a while. By adding a config item evovle_num_min can cope above two problems. The bot evolves only if evolvable candidates no less than a certain number. 1. Evolve only if evolvable candidates no less than a certain number. 2. Dragoniar's configuration is abnormal in pokemon.json, which lack of "Previous evolution(s)" attribute, while the other middle-tier pokemon has. Fixed some issues after: Refactor evolve_all worker #2244 1. The refactoring try to sort by pokemon index desc which is not good. For example, why Venusaur which is #003 should be with lower priority than Pidgey(#016)? 2. The refactoring always use dict.get(key, {}).get(key, {}) which will eventually return a {} which will cause side effect. It's better fail fast when the dict has no attribute which is unexpected by the developer. 3. The reafctoring try to use cache when the pokemon is not evovlable. That developer doesn't know previously we didn't caculate the candy before evovling, thus caused a lot of failure when evovling. After caculating candy requirements by the refactoring and this patch, the failure rarely happens. Thus cache is not necessary when the pokemon is not evovlable. --- configs/config.json.cluster.example | 1 + configs/config.json.example | 1 + configs/config.json.path.example | 1 + configs/config.json.pokemon.example | 1 + pokemongo_bot/cell_workers/evolve_pokemon.py | 80 +++++++++++++------- 5 files changed, 58 insertions(+), 26 deletions(-) diff --git a/configs/config.json.cluster.example b/configs/config.json.cluster.example index 1bedbcbab9..5d4c741e35 100644 --- a/configs/config.json.cluster.example +++ b/configs/config.json.cluster.example @@ -48,6 +48,7 @@ "first_evolve_by": "cp", "evolve_above_cp": 500, "evolve_above_iv": 0.8, + "evolve_num_min": 5, "logic": "or", "evolve_speed": 20, "use_lucky_egg": false diff --git a/configs/config.json.example b/configs/config.json.example index 838acc1d4b..3576bf2a73 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -55,6 +55,7 @@ "first_evolve_by": "cp", "evolve_above_cp": 500, "evolve_above_iv": 0.8, + "evolve_num_min": 5, "logic": "or", "evolve_speed": 20, "use_lucky_egg": false diff --git a/configs/config.json.path.example b/configs/config.json.path.example index 2581862b31..254eff6be5 100644 --- a/configs/config.json.path.example +++ b/configs/config.json.path.example @@ -48,6 +48,7 @@ "first_evolve_by": "cp", "evolve_above_cp": 500, "evolve_above_iv": 0.8, + "evolve_num_min": 5, "logic": "or", "evolve_speed": 20, "use_lucky_egg": false diff --git a/configs/config.json.pokemon.example b/configs/config.json.pokemon.example index 2ad81a7369..c656a0a6c2 100644 --- a/configs/config.json.pokemon.example +++ b/configs/config.json.pokemon.example @@ -48,6 +48,7 @@ "first_evolve_by": "cp", "evolve_above_cp": 500, "evolve_above_iv": 0.8, + "evolve_num_min": 5, "logic": "or", "evolve_speed": 20, "use_lucky_egg": false diff --git a/pokemongo_bot/cell_workers/evolve_pokemon.py b/pokemongo_bot/cell_workers/evolve_pokemon.py index 4de40fdb7d..13299f0ed8 100644 --- a/pokemongo_bot/cell_workers/evolve_pokemon.py +++ b/pokemongo_bot/cell_workers/evolve_pokemon.py @@ -15,6 +15,7 @@ def initialize(self): self.first_evolve_by = self.config.get('first_evolve_by', 'cp') self.evolve_above_cp = self.config.get('evolve_above_cp', 500) self.evolve_above_iv = self.config.get('evolve_above_iv', 0.8) + self.evolve_num_min = self.config.get('evolve_num_min', 5) self.cp_iv_logic = self.config.get('logic', 'or') self.use_lucky_egg = self.config.get('use_lucky_egg', False) self._validate_config() @@ -27,29 +28,26 @@ def work(self): if not self._should_run(): return - evolve_list = self._sort_and_filter() - - if self.evolve_all[0] != 'all': - # filter out non-listed pokemons - evolve_list = filter(lambda x: x.name in self.evolve_all, evolve_list) - - cache = {} - for pokemon in evolve_list: - if pokemon.can_evolve_now(): - self._execute_pokemon_evolve(pokemon, cache) + cache = set() - def _should_run(self): - if not self.evolve_all or self.evolve_all[0] == 'none': - return False + evolve_list = self._sort_and_filter() + # filter out non-listed pokemons, top-tier pokemons and those with not enough candy + evolve_list = [x for x in evolve_list if self._is_evolvable(x, inventory.candies().deepcopy())] - # Evolve all is used - Use Lucky egg only at the first tick - if self.bot.tick_count is not 1 or not self.use_lucky_egg: - return True + # Don't evolve unless the evolvable candidates number is no less than evolve_num_min + if len(evolve_list) < self.evolve_num_min: + return - lucky_egg_count = self.bot.item_inventory_count(Item.ITEM_LUCKY_EGG.value) + if self.use_lucky_egg: + lucky_egg_count = self.bot.item_inventory_count(Item.ITEM_LUCKY_EGG.value) + # Sometimes remaining lucky egg count get changed, check again for sure + if lucky_egg_count <= 0: + self.emit_event( + 'skip_evolve', + formatted='Skipping evolve because has no lucky egg.' + ) + return - # Make sure the user has a lucky egg and skip if not - if lucky_egg_count > 0: response_dict_lucky_egg = self.bot.use_lucky_egg() if response_dict_lucky_egg: result = response_dict_lucky_egg.get('responses', {}).get('USE_ITEM_XP_BOOST', {}).get('result', 0) @@ -58,32 +56,45 @@ def _should_run(self): 'used_lucky_egg', formatted='Used lucky egg ({amount_left} left).', data={ - 'amount_left': lucky_egg_count - 1 + 'amount_left': lucky_egg_count - 1 } ) - return True else: self.emit_event( 'lucky_egg_error', level='error', formatted='Failed to use lucky egg!' ) - return False - else: - # Skipping evolve so they aren't wasted + return + + for pokemon in evolve_list: + self._execute_pokemon_evolve(pokemon, cache) + + def _should_run(self): + # Don't run after the first tick + # Lucky Egg should only be popped at the first tick + if not self.evolve_all or self.evolve_all[0] == 'none' or self.bot.tick_count is 1: + return False + + # Will skip evolving if user wants to use an egg and there is none + lucky_egg_count = self.bot.item_inventory_count(Item.ITEM_LUCKY_EGG.value) + if self.use_lucky_egg and lucky_egg_count <= 0: self.emit_event( 'skip_evolve', formatted='Skipping evolve because has no lucky egg.' ) + return False + # Otherwise try evolving + return True + def _sort_and_filter(self): pokemons = [] logic_to_function = { 'or': lambda pokemon: pokemon.cp >= self.evolve_above_cp or pokemon.iv >= self.evolve_above_iv, 'and': lambda pokemon: pokemon.cp >= self.evolve_above_cp and pokemon.iv >= self.evolve_above_iv } - for pokemon in inventory.pokemons().all(): if pokemon.id > 0 and pokemon.has_next_evolution() and (logic_to_function[self.cp_iv_logic](pokemon)): pokemons.append(pokemon) @@ -95,6 +106,22 @@ def _sort_and_filter(self): return pokemons + + def _is_evolvable(self, pokemon, candies): + # filter out non-listed pokemen + if self.evolve_all[0] != 'all' and pokemon.name not in self.evolve_all: + return False + + if not pokemon.has_seen_next_evolution(): + return False + + if candies.get(pokemon.pokemon_id).quantity >= pokemon.evolution_cost: + candies.get(pokemon.pokemon_id).consume(pokemon.evolution_cost) + return True + # not enough candies + return False + + def _execute_pokemon_evolve(self, pokemon, cache): if pokemon.name in cache: return False @@ -118,10 +145,11 @@ def _execute_pokemon_evolve(self, pokemon, cache): inventory.pokemons().remove(pokemon.id) pokemon = Pokemon(response_dict.get('responses', {}).get('EVOLVE_POKEMON', {}).get('evolved_pokemon_data', {})) inventory.pokemons().add(pokemon) + sleep(self.evolve_speed) return True else: # cache pokemons we can't evolve. Less server calls - cache[pokemon.name] = 1 + cache.add(pokemon.name) sleep(0.7) return False