From b234f4eecf45a01284b8c62f68160d2ae8dddf25 Mon Sep 17 00:00:00 2001 From: Julien Lavergne Date: Sat, 20 Aug 2016 11:11:32 +0800 Subject: [PATCH] Add "names" filter to apply sorting rules to selected families only Make sure optimizer try its best to reach the minimum evolve count if we want to use lucky eggs Do not transfer favorite pokemon and pokemon in fort Add database logging stuff Update pokemon optimizer config example Update pokemon.json --- configs/config.json.optimizer.example | 20 ++-- data/pokemon.json | 8 +- .../cell_workers/pokemon_optimizer.py | 98 ++++++++++++++----- 3 files changed, 91 insertions(+), 35 deletions(-) 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