diff --git a/configs/config.json.optimizer.example b/configs/config.json.optimizer.example index d3749ea3b5..128f6b6902 100644 --- a/configs/config.json.optimizer.example +++ b/configs/config.json.optimizer.example @@ -60,6 +60,7 @@ { "type": "PokemonOptimizer", "config": { + "enabled": true, "// the 'transfer' parameter activate or deactivate the transfer of pokemons": {}, "// at false, no pokemon is going to be transfered, ever": {}, "// at false, you will still get the log information of what the optimizer": {}, @@ -114,12 +115,16 @@ "// 'cp' = combat power (can be increased with candies)": {}, "// 'cp_exact' = combar power (not rounded)": {}, "// 'ncp' (normalized cp) or 'cp_percent' = ratio cp / max_cp": {}, - "// iv_attack = attach component of iv": {}, - "// iv_defense = defense component of iv": {}, - "// iv_stamina = stamina component of iv": {}, - "// dps = raw dps based on the moves of the pokemon": {}, - "// dps_attack = average dps when attacking": {}, - "// dps_defense = average dps when defending": {}, + "// 'iv_attack' = attach component of iv": {}, + "// 'iv_defense' = defense component of iv": {}, + "// 'iv_stamina' = stamina component of iv": {}, + "// 'dps' = raw dps based on the moves of the pokemon": {}, + "// 'dps_attack' = average dps when attacking": {}, + "// 'attack_perfection' = ratio dps_attack / best_dps_attack. Return same order as 'dps_attack'": {}, + "// 'dps_defense' = average dps when defending": {}, + "// 'defense_perfection' = ratio dps_defense / best_dps_defense. Return same order as 'dps_defense'": {}, + "// 'hp' = current health points": {}, + "// 'hp_max' = max health points": {}, "// Note that the more criteria you add to this list, the less likely Pokemons": {}, "// will be equals": {}, "sort": ["iv"] @@ -134,6 +139,9 @@ { "// Following setting let you keep keep the best cp of the family.": {}, "// But will not evolve it further (in favor of the best ncp)": {}, + "// It will only applies to the family of 'Dragonite', 'Arcanine' and 'Muk'": {}, + "// Other families are not following this setting": {}, + "names": ["Dragonite", "Arcanine", "Muk"], "top": 1, "evolve": false, "sort": ["cp"] diff --git a/data/pokemon.json b/data/pokemon.json index ccc4a61161..160ee5baab 100644 --- a/data/pokemon.json +++ b/data/pokemon.json @@ -1609,6 +1609,7 @@ ], "Special Attack(s)": [ "Body Slam", + "Dazzling Gleam", "Disarming Voice", "Play Rough" ], @@ -3539,8 +3540,8 @@ "Grass" ], "Fast Attack(s)": [ - "Lick", "Ice Shard", + "Lick", "Water Gun" ], "Weight": "90.0 kg", @@ -4804,8 +4805,8 @@ "Grass" ], "Fast Attack(s)": [ - "Tackle", "Quick Attack", + "Tackle", "Water Gun" ], "Weight": "34.5 kg", @@ -4864,7 +4865,8 @@ "Special Attack(s)": [ "Hydro Pump", "Power Gem", - "Psybeam" + "Psybeam", + "Psychic" ], "BaseAttack": 194, "BaseDefense": 192, diff --git a/pokemongo_bot/cell_workers/pokemon_optimizer.py b/pokemongo_bot/cell_workers/pokemon_optimizer.py index 4c52ed8b62..2d837bf0b6 100644 --- a/pokemongo_bot/cell_workers/pokemon_optimizer.py +++ b/pokemongo_bot/cell_workers/pokemon_optimizer.py @@ -6,6 +6,7 @@ from pokemongo_bot import inventory from pokemongo_bot.base_dir import _base_dir from pokemongo_bot.base_task import BaseTask +from pokemongo_bot.datastore import Datastore from pokemongo_bot.human_behaviour import sleep, action_delay from pokemongo_bot.item_list import Item from pokemongo_bot.worker_result import WorkerResult @@ -17,9 +18,12 @@ ERROR_LOCATION_UNSET = 5 -class PokemonOptimizer(BaseTask): +class PokemonOptimizer(Datastore, BaseTask): SUPPORTED_TASK_API_VERSION = 1 + def __init__(self, bot, config): + super(PokemonOptimizer, self).__init__(bot, config) + def initialize(self): self.family_by_family_id = {} self.max_pokemon_storage = inventory.get_pokemon_inventory_size() @@ -36,8 +40,8 @@ def initialize(self): {"top": 1, "evolve": True, "sort": ["ncp"]}, {"top": 1, "evolve": False, "sort": ["cp"]}]) - self.transfer_wait_min = self.config.get('transfer_wait_min', 1) - self.transfer_wait_max = self.config.get('transfer_wait_max', 4) + self.config_transfer_wait_min = self.config.get("transfer_wait_min", 1) + self.config_transfer_wait_max = self.config.get("transfer_wait_max", 4) def get_pokemon_slot_left(self): pokemon_count = inventory.Pokemons.get_space_used() @@ -97,15 +101,21 @@ def save_web_inventory(self): def get_family_optimized(self, family_id, family): evolve_best = [] keep_best = [] + family_names = self.get_family_names(family_id) for criteria in self.config_keep: + names = criteria.get("names", []) + + if names and not any(n in family_names for n in names): + continue + if criteria.get("evolve", True): evolve_best += self.get_top_rank(family, criteria) else: keep_best += self.get_top_rank(family, criteria) - evolve_best = self.unique_pokemons(evolve_best) - keep_best = self.unique_pokemons(keep_best) + evolve_best = self.unique_pokemon(evolve_best) + keep_best = self.unique_pokemon(keep_best) return self.get_evolution_plan(family_id, family, evolve_best, keep_best) @@ -119,14 +129,20 @@ def get_multi_family_optimized(self, family_id, family, nb_branch): if not self.config_evolve: transfer, evo_best, evo_crap = self.get_family_optimized(family_id, other_family) elif len(senior_pids) < nb_branch: - # We did not get every combination yet = All other Pokemons are potentially good to keep + # We did not get every combination yet = All other Pokemon are potentially good to keep transfer, evo_best, evo_crap = self.get_evolution_plan(family_id, [], other_family, []) evo_best.sort(key=lambda p: p.iv * p.ncp, reverse=True) else: evolve_best = [] keep_best = [] + names = self.get_family_names(family_id) for criteria in self.config_keep: + family_names = criteria.get("names", []) + + if names and not any(n in family_names for n in names): + continue + top = [] for f in senior_grouped_family.values(): @@ -139,8 +155,8 @@ def get_multi_family_optimized(self, family_id, family, nb_branch): else: keep_best += self.get_better_rank(family, criteria, worst) - evolve_best = self.unique_pokemons(evolve_best) - keep_best = self.unique_pokemons(keep_best) + evolve_best = self.unique_pokemon(evolve_best) + keep_best = self.unique_pokemon(keep_best) transfer, evo_best, evo_crap = self.get_evolution_plan(family_id, other_family, evolve_best, keep_best) for senior_pid, senior_family in senior_grouped_family.items(): @@ -148,6 +164,12 @@ def get_multi_family_optimized(self, family_id, family, nb_branch): return (transfer, evo_best, evo_crap) + def get_family_names(self, family_id): + ids = [family_id] + ids += inventory.Pokemons.data_for(family_id).next_evolutions_all[:] + datas = [inventory.Pokemons.data_for(x) for x in ids] + return [x.name for x in datas] + def get_top_rank(self, family, criteria): sorted_family = self.get_sorted_family(family, criteria) index = criteria.get("top", 1) - 1 @@ -170,7 +192,7 @@ def get_rank(self, pokemon, criteria): def get_pokemon_max_cp(self, pokemon_name): return int(self.pokemon_max_cp.get(pokemon_name, 0)) - def unique_pokemons(self, l): + def unique_pokemon(self, l): seen = set() return [p for p in l if not (p.unique_id in seen or seen.add(p.unique_id))] @@ -181,11 +203,12 @@ def get_evolution_plan(self, family_id, family, evolve_best, keep_best): crap = family[:] crap = [p for p in crap if p not in evolve_best] crap = [p for p in crap if p not in keep_best] + crap = [p for p in crap if not p.in_fort and not p.is_favorite] crap.sort(key=lambda p: p.iv * p.ncp, reverse=True) candies += len(crap) - # Let's see if we can evolve our best pokemons + # Let's see if we can evolve our best Pokemon can_evolve_best = [] for pokemon in evolve_best: @@ -237,7 +260,7 @@ def get_evolution_plan(self, family_id, family, evolve_best, keep_best): return (transfer, can_evolve_best, evo_crap) def apply_optimization(self, transfer, evo): - self.logger.info("Transferring %s Pokemons", len(transfer)) + self.logger.info("Transferring %s Pokemon", len(transfer)) for pokemon in transfer: self.transfer_pokemon(pokemon) @@ -246,19 +269,26 @@ def apply_optimization(self, transfer, evo): return if self.config_evolve and self.config_may_use_lucky_egg and (not self.bot.config.test): - if len(evo) >= self.config_evolve_count_for_lucky_egg: - lucky_egg = inventory.items().get(Item.ITEM_LUCKY_EGG.value) # @UndefinedVariable + lucky_egg = inventory.items().get(Item.ITEM_LUCKY_EGG.value) # @UndefinedVariable - if lucky_egg.count > 0: - self.use_lucky_egg() - elif self.config_evolve_only_with_lucky_egg: - self.logger.info("Skipping evolution step. No lucky egg available") + if lucky_egg.count == 0: + if self.config_evolve_only_with_lucky_egg: + self.emit_event("skip_evolve", + formatted="Skipping evolution step. No lucky egg available") + return + elif len(evo) < self.config_evolve_count_for_lucky_egg: + if self.config_evolve_only_with_lucky_egg: + self.emit_event("skip_evolve", + formatted="Skipping evolution step. Not enough Pokemon to evolve with lucky egg: %s/%s" % (len(evo), self.config_evolve_count_for_lucky_egg)) + return + elif self.get_pokemon_slot_left() > 5: + self.emit_event("skip_evolve", + formatted="Waiting for more Pokemon to evolve with lucky egg: %s/%s" % (len(evo), self.config_evolve_count_for_lucky_egg)) return - elif self.config_evolve_only_with_lucky_egg: - self.logger.info("Skipping evolution step. Not enough Pokemons (%s) to evolve with lucky egg", len(evo)) - return + else: + self.use_lucky_egg() - self.logger.info("Evolving %s Pokemons", len(evo)) + self.logger.info("Evolving %s Pokemon", len(evo)) for pokemon in evo: self.evolve_pokemon(pokemon) @@ -281,16 +311,23 @@ def transfer_pokemon(self, pokemon): "dps": round(pokemon.dps, 2)}) if self.config_transfer and (not self.bot.config.test): + candy = response_dict.get("responses", {}).get("RELEASE_POKEMON", {}).get("candy_awarded", 0) - inventory.candies().get(pokemon.pokemon_id).add(self.get_candy_gained_count(response_dict)) + inventory.candies().get(pokemon.pokemon_id).add(candy) inventory.pokemons().remove(pokemon.unique_id) - action_delay(self.transfer_wait_min, self.transfer_wait_max) + with self.bot.database as db: + cursor = db.cursor() + cursor.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'") - return True + db_result = cursor.fetchone() + + if db_result[0] == 1: + db.execute("INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)", (pokemon.name, pokemon.iv, pokemon.cp)) + + action_delay(self.config_transfer_wait_min, self.config_transfer_wait_max) - def get_candy_gained_count(self, response_dict): - return response_dict['responses']['RELEASE_POKEMON']['candy_awarded'] + return True def use_lucky_egg(self): lucky_egg = inventory.items().get(Item.ITEM_LUCKY_EGG.value) # @UndefinedVariable @@ -360,6 +397,15 @@ def evolve_pokemon(self, pokemon): new_pokemon = inventory.Pokemon(evolution) inventory.pokemons().add(new_pokemon) + with self.bot.database as db: + cursor = db.cursor() + cursor.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='evolve_log'") + + db_result = cursor.fetchone() + + if db_result[0] == 1: + db.execute("INSERT INTO evolve_log (pokemon, iv, cp) VALUES (?, ?, ?)", (pokemon.name, pokemon.iv, pokemon.cp)) + sleep(self.config_evolve_time) return True