diff --git a/configs/config.json.optimizer.example b/configs/config.json.optimizer.example
index 287df3c843..80e801896d 100644
--- a/configs/config.json.optimizer.example
+++ b/configs/config.json.optimizer.example
@@ -18,6 +18,9 @@
"evolve_only_with_lucky_egg": false,
"evolve_count_for_lucky_egg": 80,
"may_use_lucky_egg": true,
+ "may_evolve_favorites": true,
+ "may_upgrade_favorites": true,
+ "may_unfavor_pokemon": false,
"upgrade": true,
"upgrade_level": 30,
"groups": {
diff --git a/docs/pokemon_optimizer.md b/docs/pokemon_optimizer.md
index dbb28d6b1a..0ccefad359 100644
--- a/docs/pokemon_optimizer.md
+++ b/docs/pokemon_optimizer.md
@@ -17,6 +17,9 @@
- [evolve_only_with_lucky_egg](#evolve_only_with_lucky_egg)
- [evolve_count_for_lucky_egg](#evolve_count_for_lucky_egg)
- [may_use_lucky_egg](#may_use_lucky_egg)
+ - [may_evolve_favorites](#may_evolve_favorites)
+ - [may_upgrade_favorites](#may_upgrade_favorites)
+ - [may_unfavor_pokemon](#may_unfavor_pokemon)
- [upgrade](#upgrade)
- [upgrade_level](#upgrade_level)
- [groups](#groups)
@@ -29,6 +32,7 @@
- [evolve](#rule-evolve)
- [upgrade](#rule-upgrade)
- [buddy](#rule-buddy)
+ - [favorite](#rule-favorite)
- [Eevee case](#eevee-case)
- [FAQ](#faq)
@@ -42,7 +46,7 @@ There is only one pass at each action.
It will also collect the candies from your Buddy and select the next buddy.
-In case that logging will be enabled, look for .log file in data folder.
+In case that logging will be enabled, look for .log file in data folder.
[[back to top](#pokemon-optimizer)]
@@ -69,6 +73,9 @@ In case that logging will be enabled, look for .log file in data folder.
"evolve_only_with_lucky_egg": false,
"evolve_count_for_lucky_egg": 80,
"may_use_lucky_egg": true,
+ "may_evolve_favorites": true,
+ "may_upgrade_favorites": true,
+ "may_unfavor_pokemon": false,
"upgrade": true,
"upgrade_level": 30,
"groups": {
@@ -333,6 +340,39 @@ Define whether you allow the Pokemon Optimizer to use a lucky egg before evolvin
[[back to top](#pokemon-optimizer)]
+### may_evolve_favorites
+| Parameter | Possible values | Default |
+|---------------------|-----------------|---------|
+| `may_evolve_favorites` | `true`, `false` | `true` |
+
+Define whether you allow the Pokemon Optimizer to evolve favorite Pokemon or not.
+
At `true`, the Pokemon Optimizer will evolve favorite Pokemon according to the rules.
+
At `false`, the Pokemon Optimizer will not evolve favorite Pokemon.
+
+[[back to top](#pokemon-optimizer)]
+
+### may_upgrade_favorites
+| Parameter | Possible values | Default |
+|---------------------|-----------------|---------|
+| `may_upgrade_favorites` | `true`, `false` | `true` |
+
+Define whether you allow the Pokemon Optimizer to upgrade favorite Pokemon or not.
+
At `true`, the Pokemon Optimizer will upgrade favorite Pokemon according to the rules.
+
At `false`, the Pokemon Optimizer will not upgrade favorite Pokemon.
+
+[[back to top](#pokemon-optimizer)]
+
+### may_unfavor_pokemon
+| Parameter | Possible values | Default |
+|---------------------|-----------------|---------|
+| `may_unfavor_pokemon` | `true`, `false` | `false` |
+
+Define whether you allow the Pokemon Optimizer to unmark favorite Pokemon as favorite or not.
+
At `true`, the Pokemon Optimizer will unmark favorite Pokemon if it no longer matches favorite rules.
+
At `false`, the Pokemon Optimizer will not unmark favorite Pokemon.
+
+[[back to top](#pokemon-optimizer)]
+
### upgrade
| Parameter | Possible values | Default |
|-----------|-----------------|---------|
@@ -637,6 +677,8 @@ By default, if `evolve` is not provided or is empty, no Pokemon will be evolved.
The parameter can be a boolean value (`true` or `false`) or a list a criteria.
The available criteria are the same as for the [`sort`](#available-criteria) parameter.
+*Note!* If [may_evolve_favorites](#may_evolve_favorites) is `false`, favorite Pokemon will never be evolved!
+
The minimum requirement values can be a single value or a range.
They can also be a negative value if you wish to evolve Pokemon below a certain criteria:
@@ -663,8 +705,10 @@ By default, if `upgrade` is not provided or is empty, no Pokemon will be upgrade
The parameter can be a boolean value (`true` or `false`) or a list a criteria.
The available criteria are the same as for the [`sort`](#available-criteria) parameter.
+*Note!* If [may_upgrade_favorites](#may_upgrade_favorites) is `false`, favorite Pokemon will never be upgraded!
+
The minimum requirement values can be a single value or a range.
-
They can also be a negative value if you wish to evolve Pokemon below a certain criteria:
+
They can also be a negative value if you wish to upgrade Pokemon below a certain criteria:
- `"upgrade": false` will not try to upgrade any of the Pokemon selected.
- `"upgrade": true` will try to upgrade all Pokemon selected.
@@ -677,6 +721,32 @@ The minimum requirement values can be a single value or a range.
[[back to top](#pokemon-optimizer)]
+#### rule favorite
+| Parameter | Possible values | Default |
+|-----------|-----------------|---------|
+| `favorite` | (see below) | `false` |
+
+Define minimum requirements to favorite the Pokemon.
+Only Pokemon meeting these minimum requirements will be marked as favorite.
+By default, if `favorite` is not provided or is empty, no Pokemon will be marked favorite.
+
+The parameter can be a boolean value (`true` or `false`) or a list a criteria.
+The available criteria are the same as for the [`sort`](#available-criteria) parameter.
+
+The minimum requirement values can be a single value or a range.
+
They can also be a negative value if you wish to mark Pokemon below a certain criteria as favorite:
+
+- `"favorite": false` will not try to mark any of the Pokemon selected as favorite.
+- `"favorite": true` will try to mark all Pokemon selected as favorite.
+- `"favorite": {"iv": 0.9}` will only favorite Pokemon with `iv` greater than `0.9`.
+- `"favorite": {"iv": 0.9, "cp": 1200}` will only favorite Pokemon with `iv` greater than `0.9` and `cp` greater than `1200`.
+- `"favorite": {"iv": 0.9}` will only favorite Pokemon with `iv` greater than `0.9`.
+- `"favorite": {"cp": -20}` will only favorite Pokemon with `cp` lower than `20`.
+- `"favorite": {"cp": [10, 20]}` will only favorite Pokemon with `cp` between `10` and `20`.
+- `"favorite": {"iv": [[0.3, 0.5], [0.9, 1.0]]}` will only favorite Pokemon with `iv` between `0.3` and `0.5` or between `0.9` and `1.0`.
+
+[[back to top](#pokemon-optimizer)]
+
#### rule buddy
| Parameter | Possible values | Default |
|-----------|-----------------|---------|
diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py
index 1ce7c0bf09..45d873c179 100644
--- a/pokemongo_bot/__init__.py
+++ b/pokemongo_bot/__init__.py
@@ -509,6 +509,14 @@ def _register_events(self):
'pokemon_evolved',
parameters=('pokemon', 'iv', 'cp', 'candy', 'xp')
)
+ self.event_manager.register_event(
+ 'pokemon_favored',
+ parameters=('pokemon', 'iv', 'cp')
+ )
+ self.event_manager.register_event(
+ 'pokemon_unfavored',
+ parameters=('pokemon', 'iv', 'cp')
+ )
self.event_manager.register_event(
'pokemon_evolve_check',
parameters=('has', 'needs')
diff --git a/pokemongo_bot/cell_workers/pokemon_optimizer.py b/pokemongo_bot/cell_workers/pokemon_optimizer.py
index 4e92e02f4f..bc21849104 100644
--- a/pokemongo_bot/cell_workers/pokemon_optimizer.py
+++ b/pokemongo_bot/cell_workers/pokemon_optimizer.py
@@ -70,6 +70,9 @@ def initialize(self):
self.config_evolve_only_with_lucky_egg = self.config.get("evolve_only_with_lucky_egg", False)
self.config_evolve_count_for_lucky_egg = self.config.get("evolve_count_for_lucky_egg", 80)
self.config_may_use_lucky_egg = self.config.get("may_use_lucky_egg", False)
+ self.config_may_evolve_favorites = self.config.get("may_evolve_favorites", True)
+ self.config_may_upgrade_favorites = self.config.get("may_upgrade_favorites", True)
+ self.config_may_unfavor_pokemon = self.config.get("may_unfavor_pokemon", False)
self.config_upgrade = self.config.get("upgrade", False)
self.config_upgrade_level = self.config.get("upgrade_level", 30)
self.config_groups = self.config.get("groups", {"gym": ["Dragonite", "Snorlax", "Lapras", "Arcanine"]})
@@ -124,108 +127,136 @@ def work(self):
if not self.enabled:
return WorkerResult.SUCCESS
- self.check_buddy()
+ # Repeat the optimizer 2 times, to get rid of the trash evolved.
+ for _ in itertools.repeat(None, 2):
+ self.check_buddy()
+ self.open_inventory()
- if self.lock_buddy and (self.get_pokemon_slot_left() > self.config_min_slots_left):
- return WorkerResult.SUCCESS
+ keep_all = []
+ try_evolve_all = []
+ try_upgrade_all = []
+ buddy_all = []
+ favor_all = []
- self.open_inventory()
+ for rule in self.config_rules:
+ mode = rule.get("mode", "by_family")
+ names = rule.get("names", [])
+ check_top = rule.get("top", "all")
+ check_keep = rule.get("keep", True)
+ whitelist, blacklist = self.get_colorlist(names)
- keep_all = []
- try_evolve_all = []
- try_upgrade_all = []
- buddy_all = []
+ if check_top == "all" and names == [] and check_keep:
+ self.logger.info("WARNING!! Will not transfer any Pokemon!!")
+ self.logger.info(rule)
+ self.logger.info("This rule is set to keep (`keep` is true) all Pokemon (no `top` and no `names` set!!)")
+ self.logger.info("Are you sure you want this?")
- for rule in self.config_rules:
- mode = rule.get("mode", "by_family")
- names = rule.get("names", [])
- whitelist, blacklist = self.get_colorlist(names)
+ if mode == "by_pokemon":
+ for pokemon_id, pokemon_list in self.group_by_pokemon_id(inventory.pokemons().all()):
+ name = inventory.pokemons().name_for(pokemon_id)
- if mode == "by_pokemon":
- for pokemon_id, pokemon_list in self.group_by_pokemon_id(inventory.pokemons().all()):
- name = inventory.pokemons().name_for(pokemon_id)
+ if name in blacklist:
+ continue
- if name in blacklist:
- continue
+ if whitelist and (name not in whitelist):
+ continue
- if whitelist and (name not in whitelist):
- continue
+ sorted_list = self.score_and_sort(pokemon_list, rule)
- sorted_list = self.score_and_sort(pokemon_list, rule)
+ if len(sorted_list) == 0:
+ continue
- if len(sorted_list) == 0:
- continue
+ keep, try_evolve, try_upgrade, buddy, favor = self.get_best_pokemon_for_rule(sorted_list, rule)
+ keep_all += keep
+ try_evolve_all += try_evolve
+ try_upgrade_all += try_upgrade
+ buddy_all += buddy
+ favor_all += favor
+ elif mode == "by_family":
+ for family_id, pokemon_list in self.group_by_family_id(inventory.pokemons().all()):
+ matching_names = self.get_family_names(family_id)
- keep, try_evolve, try_upgrade, buddy = self.get_best_pokemon_for_rule(sorted_list, rule)
- keep_all += keep
- try_evolve_all += try_evolve
- try_upgrade_all += try_upgrade
- buddy_all += buddy
- elif mode == "by_family":
- for family_id, pokemon_list in self.group_by_family_id(inventory.pokemons().all()):
- matching_names = self.get_family_names(family_id)
+ if any(n in blacklist for n in matching_names):
+ continue
- if any(n in blacklist for n in matching_names):
- continue
+ if whitelist and not any(n in whitelist for n in matching_names):
+ continue
- if whitelist and not any(n in whitelist for n in matching_names):
- continue
+ sorted_list = self.score_and_sort(pokemon_list, rule)
+
+ if len(sorted_list) == 0:
+ continue
+
+ if family_id == 133: # "Eevee"
+ keep, try_evolve, try_upgrade, buddy, favor = self.get_multi_best_pokemon_for_rule(sorted_list, rule, 3)
+ else:
+ keep, try_evolve, try_upgrade, buddy, favor = self.get_best_pokemon_for_rule(sorted_list, rule)
+
+ keep_all += keep
+ try_evolve_all += try_evolve
+ try_upgrade_all += try_upgrade
+ buddy_all += buddy
+ favor_all += favor
+ elif mode == "overall":
+ pokemon_list = []
+
+ for pokemon in inventory.pokemons().all():
+ name = pokemon.name
+
+ if name in blacklist:
+ continue
+
+ if whitelist and (name not in whitelist):
+ continue
+
+ pokemon_list.append(pokemon)
sorted_list = self.score_and_sort(pokemon_list, rule)
if len(sorted_list) == 0:
continue
- if family_id == 133: # "Eevee"
- keep, try_evolve, try_upgrade, buddy = self.get_multi_best_pokemon_for_rule(sorted_list, rule, 3)
- else:
- keep, try_evolve, try_upgrade, buddy = self.get_best_pokemon_for_rule(sorted_list, rule)
-
+ keep, try_evolve, try_upgrade, buddy, favor = self.get_best_pokemon_for_rule(sorted_list, rule)
keep_all += keep
try_evolve_all += try_evolve
try_upgrade_all += try_upgrade
buddy_all += buddy
- elif mode == "overall":
- pokemon_list = []
-
+ favor_all += favor
+
+ keep_all = self.unique_pokemon_list(keep_all)
+ try_evolve_all = self.unique_pokemon_list(try_evolve_all)
+ try_upgrade_all = self.unique_pokemon_list(try_upgrade_all)
+ buddy_all = self.unique_pokemon_list(buddy_all)
+ try_favor_all = self.unique_pokemon_list(favor_all)
+ # Favorites has nothing to do with evolve, can be done even when bag not full
+ # Like a buddy
+ if self.config_may_unfavor_pokemon:
+ unfavor = []
for pokemon in inventory.pokemons().all():
- name = pokemon.name
-
- if name in blacklist:
- continue
-
- if whitelist and (name not in whitelist):
- continue
-
- pokemon_list.append(pokemon)
-
- sorted_list = self.score_and_sort(pokemon_list, rule)
-
- if len(sorted_list) == 0:
- continue
-
- keep, try_evolve, try_upgrade, buddy = self.get_best_pokemon_for_rule(sorted_list, rule)
- keep_all += keep
- try_evolve_all += try_evolve
- try_upgrade_all += try_upgrade
- buddy_all += buddy
-
- keep_all = self.unique_pokemon_list(keep_all)
- try_evolve_all = self.unique_pokemon_list(try_evolve_all)
- try_upgrade_all = self.unique_pokemon_list(try_upgrade_all)
- buddy_all = self.unique_pokemon_list(buddy_all)
-
- if (not self.lock_buddy) and (len(buddy_all) > 0):
- new_buddy = buddy_all[0]
-
- if (not self.buddy) or (self.buddy["id"] != new_buddy.unique_id):
- self.set_buddy_pokemon(new_buddy)
+ if not pokemon in try_favor_all and pokemon.is_favorite:
+ unfavor.append(pokemon)
+ if len(unfavor) > 0:
+ self.logger.info("Marking %s Pokemon as no longer favorite", len(unfavor))
+ for pokemon in unfavor:
+ self.unfavor_pokemon(pokemon)
+ # Dont favor Pokemon if already a favorite
+ try_favor_all = [p for p in try_favor_all if not p.is_favorite]
+ if len(try_favor_all) > 0:
+ self.logger.info("Setting %s Poken as favorite", len(try_favor_all))
+
+ for pokemon in try_favor_all:
+ if pokemon.is_favorite is False:
+ self.favor_pokemon(pokemon)
+
+ if (not self.lock_buddy) and (len(buddy_all) > 0):
+ new_buddy = buddy_all[0]
+
+ if (not self.buddy) or (self.buddy["id"] != new_buddy.unique_id):
+ self.set_buddy_pokemon(new_buddy)
+
+ if self.get_pokemon_slot_left() > self.config_min_slots_left:
+ return WorkerResult.SUCCESS
- if self.get_pokemon_slot_left() > self.config_min_slots_left:
- return WorkerResult.SUCCESS
-
- # Repeat the optimizer 2 times, to get rid of the trash evolved.
- for _ in itertools.repeat(None, 2):
transfer_all = []
evolve_all = []
upgrade_all = []
@@ -234,6 +265,7 @@ def work(self):
keep = [p for p in keep_all if self.get_family_id(p) == family_id]
try_evolve = [p for p in try_evolve_all if self.get_family_id(p) == family_id]
try_upgrade = [p for p in try_upgrade_all if self.get_family_id(p) == family_id]
+ try_favor = [p for p in try_favor_all if self.get_family_id(p) == family_id]
transfer, evolve, upgrade, xp = self.get_evolution_plan(family_id, pokemon_list, keep, try_evolve, try_upgrade)
@@ -242,6 +274,14 @@ def work(self):
upgrade_all += upgrade
xp_all += xp
+ if not self.config_may_evolve_favorites:
+ self.logger.info("Removing favorites from evolve list.")
+ evolve_all = [p for p in evolve_all if not p.is_favorite]
+
+ if not self.config_may_upgrade_favorites:
+ self.logger.info("Removing favorites from upgrade list.")
+ upgrade_all = [p for p in upgrade_all if not p.is_favorite]
+
self.apply_optimization(transfer_all, evolve_all, upgrade_all, xp_all)
return WorkerResult.SUCCESS
@@ -399,6 +439,7 @@ def get_score(self, pokemon, rule):
rule_evolve = rule.get("evolve", True)
rule_upgrade = rule.get("upgrade", False)
rule_buddy = rule.get("buddy", False)
+ rule_favor = rule.get("favorite", False)
keep = rule_keep not in [False, {}]
keep &= self.satisfy_requirements(pokemon, rule_keep)
@@ -414,10 +455,13 @@ def get_score(self, pokemon, rule):
may_buddy &= pokemon.in_fort is False
may_buddy &= self.satisfy_requirements(pokemon, may_buddy)
+ may_favor = rule_favor not in [False, {}]
+ may_favor &= self.satisfy_requirements(pokemon, may_favor)
+
if self.debug:
- self.log("%s %s %s %s %s %s" % (pokemon, tuple(score), keep, may_try_evolve, may_try_upgrade, may_buddy))
+ self.log("P:%s S:%s K:%s E:%s U:%s B:%s F:%s" % (pokemon, tuple(score), keep, may_try_evolve, may_try_upgrade, may_buddy, may_favor))
- return tuple(score), keep, may_try_evolve, may_try_upgrade, may_buddy
+ return tuple(score), keep, may_try_evolve, may_try_upgrade, may_buddy, may_favor
def satisfy_requirements(self, pokemon, req):
if type(req) is bool:
@@ -487,22 +531,25 @@ def get_multi_best_pokemon_for_rule(self, family_list, rule, nb_branch):
try_evolve_all = []
try_upgrade_all = []
buddy_all = []
+ favor_all = []
if not self.config_evolve:
# Player handle evolution manually = Fall-back to per Pokemon behavior
for _, pokemon_list in self.group_by_pokemon_id(family_list):
- keep, try_evolve, try_upgrade, buddy = self.get_best_pokemon_for_rule(pokemon_list, rule)
+ keep, try_evolve, try_upgrade, buddy, favor = self.get_best_pokemon_for_rule(pokemon_list, rule)
keep_all += keep
try_evolve_all += try_evolve
try_upgrade_all += try_upgrade
buddy_all += buddy
+ favor_all += favor
else:
for _, pokemon_list in self.group_by_pokemon_id(senior_pokemon_list):
- keep, try_evolve, try_upgrade, buddy = self.get_best_pokemon_for_rule(pokemon_list, rule)
+ keep, try_evolve, try_upgrade, buddy, favor = self.get_best_pokemon_for_rule(pokemon_list, rule)
keep_all += keep
try_evolve_all += try_evolve
try_upgrade_all += try_upgrade
buddy_all += buddy
+ favor_all += favor
if len(other_family_list) > 0:
if len(senior_pids) < nb_branch:
@@ -513,21 +560,23 @@ def get_multi_best_pokemon_for_rule(self, family_list, rule, nb_branch):
best.sort(key=lambda p: p.__score__[0], reverse=True)
worst = best[-1]
- keep, try_evolve, try_upgrade, buddy = self.get_better_pokemon(other_family_list, worst, 12)
+ keep, try_evolve, try_upgrade, buddy, favor = self.get_better_pokemon(other_family_list, worst, 12)
keep_all += keep
try_evolve_all += try_evolve
try_upgrade_all += try_upgrade
buddy_all += buddy
+ favor_all += favor
- return keep_all, try_evolve_all, try_upgrade_all, buddy_all
+ return keep_all, try_evolve_all, try_upgrade_all, buddy_all, favor_all
def get_better_pokemon(self, pokemon_list, worst, limit=1000):
keep = [p for p in pokemon_list if p.__score__[0] >= worst.__score__[0]][:limit]
try_evolve = [p for p in keep if p.__score__[2] is True]
try_upgrade = [p for p in keep if (p.__score__[2] is False) and (p.__score__[3] is True)]
buddy = [p for p in keep if p.__score__[4] is True]
+ favor = [p for p in keep if p.__score__[5] is True]
- return keep, try_evolve, try_upgrade, buddy
+ return keep, try_evolve, try_upgrade, buddy, favor
def get_evolution_plan(self, family_id, family_list, keep, try_evolve, try_upgrade):
candies = inventory.candies().get(family_id).quantity
@@ -725,13 +774,13 @@ def transfer_pokemon(self, pokemons):
return False
except Exception:
return False
-
+
for pokemon in transfered:
candy = inventory.candies().get(pokemon.pokemon_id)
if self.config_transfer and (not self.bot.config.test):
candy.add(1)
-
+
self.emit_event("pokemon_release",
formatted="Exchanged {pokemon} [IV {iv}] [CP {cp}] [{candy} candies]",
data={"pokemon": pokemon.name,
@@ -997,3 +1046,31 @@ def _get_buddyid(self):
if self.buddy and'id' in self.buddy:
return self.buddy['id']
return 0
+
+ def favor_pokemon(self, pokemon):
+ response_dict = self.bot.api.set_favorite_pokemon(pokemon_id=pokemon.unique_id, is_favorite=True)
+ if response_dict:
+ result = response_dict.get('responses', {}).get('SET_FAVORITE_POKEMON', {}).get('result', 0)
+ if result is 1: # Request success
+ # Mark Pokemon as favorite
+ pokemon.is_favorite = True
+ self.emit_event("pokemon_favored",
+ formatted="Favored {pokemon} [IV {iv}] [CP {cp}]",
+ data={"pokemon": pokemon.name,
+ "iv": pokemon.iv,
+ "cp": pokemon.cp})
+ action_delay(self.config_action_wait_min, self.config_action_wait_max)
+
+ def unfavor_pokemon(self, pokemon):
+ response_dict = self.bot.api.set_favorite_pokemon(pokemon_id=pokemon.unique_id, is_favorite=False)
+ if response_dict:
+ result = response_dict.get('responses', {}).get('SET_FAVORITE_POKEMON', {}).get('result', 0)
+ if result is 1: # Request success
+ # Mark Pokemon as no longer favorite
+ pokemon.is_favorite = False
+ self.emit_event("pokemon_unfavored",
+ formatted="Unfavored {pokemon} [IV {iv}] [CP {cp}]",
+ data={"pokemon": pokemon.name,
+ "iv": pokemon.iv,
+ "cp": pokemon.cp})
+ action_delay(self.config_action_wait_min, self.config_action_wait_max)