Open mancho2000 opened 3 years ago
Hey @mancho2000,
This is a good idea. poke-env
currently contains a jsonified version of pokemon-showdown's learnset files, that could be used to do this. It is not used anywhere in the source code, apart from some scripts used for debugging.
Adding usage statistics on top of it would require a bit more effort, as it would come with a mechanism to keep the ground truth data updated with PS and cached locally.
Let me think about the problem a bit more - I will implement something along these lines in the next few weeks. In the meantime, don't hesitate to take a look at the script mentioned above if you want to implement a first version for your specific project. If you have some ideas regarding the API / related features, feel free to leave a comment!
Additionally, I saw that you closed #160 - did you find a solution? If not, I can take a deeper look at the problem if you want.
I believe that random battles don't use the full learnset of each mon, but rather a small pool of moves from which it selects randomly. If we could use that small pool it would be very useful, but I think that using the whole learnset would be too inefficient to be of any use.
About the usage statistics, I saw Pmariglia's bot that uses them. Maybe it could be useful in some way: https://github.com/pmariglia/showdown/blob/master/data/parse_smogon_stats.py
And about the self play issue, I really don't know what caused it or how I solved it, but now its working fine :)
edit: He also has a json with random battle sets from which the move pools could be extracted, although I am not sure how accurate they are. https://github.com/pmariglia/showdown/blob/master/data/random_battle_sets.json
Interesting - I'll take a deeper look at it this week. This might indeed be an interesting feature :)
I just noticed that sometimes a mon has only 1 possible ability, yet mon.ability returns None. I believe I fixed it like this:
def ability(self) -> Optional[str]:
"""
:return: The pokemon's ability. None if unknown.
:rtype: str, optional
"""
if self._ability:
return self._ability
else:
if len(self._possible_abilities) == 1:
for ability in self._possible_abilities.values():
return ability
else:
return self._ability
For sure there is a better way to do it, but that's all my programming skills allowed me to do haha :)
hey!
any updates on this? :)
Hey @mancho2000,
It's been a few busy weeks - I'll try to dig into it over the weekend :)
Hi Haris,
any estimate of when this will be done? :)
Hey @mancho2000,
Thanks for your patience - sorry it took me so long to get back to you.
Implementing something like this into poke-env
is part of the project, but doing so properly and robustly is hard. I don't know when it will be done.
That being said, here is an example script that should be easy to adapt to your specific use case. There are two main options:
# -*- coding: utf-8 -*-
import asyncio
from poke_env.data import GEN8_POKEDEX
from poke_env.player.random_player import RandomPlayer
from poke_env.utils import to_id_str
from functools import lru_cache
import requests
MOVESETS = requests.get(
"https://raw.githubusercontent.com/hsahovic/poke-env/master/src/poke_env/data/learnset.json"
).json()
# Replace file with the metagame you are interested in
USAGE_STATS = requests.get(
"https://www.smogon.com/stats/2021-08/chaos/gen8anythinggoes-0.json"
).json()
@lru_cache(None)
def possible_moves(species):
base_species = to_id_str(GEN8_POKEDEX[species]['baseSpecies'])
if base_species not in MOVESETS:
print("Missing species from movesets:", base_species)
# You could return something else, like all moves, instead
return []
if "learnset" not in MOVESETS[base_species]:
print("No learnset for", base_species)
return []
to_return = []
for move, ways in MOVESETS[base_species]["learnset"].items():
for way in ways:
# 8 just means 8th gen - you can look up the different subtypes if you are
# interested (eg. per level, eggs, tutors, etc)
if way.startswith("8"):
to_return.append(move)
break
print("All possible moves for", species, "are", to_return)
return to_return
@lru_cache(None)
def moves_with_usage_stats(species):
if species not in GEN8_POKEDEX:
print("Missing species from dex:", species)
return {}
base_species = to_id_str(GEN8_POKEDEX[species]['baseSpecies'])
name = GEN8_POKEDEX[base_species]['name']
if name not in USAGE_STATS["data"]:
# For some weird edge cases - this is slow, it might be better to init at startup
id_name = to_id_str(name)
for k in USAGE_STATS["data"]:
if to_id_str(k) == id_name:
name = k
break
else:
print("Missing usage stats for", name)
print([k for k in USAGE_STATS["data"] if k[0] == name[0]])
return {}
print("Moves with usage stats", species, "are", USAGE_STATS["data"][name]["Moves"])
return USAGE_STATS["data"][name]["Moves"]
class CustomPlayer(RandomPlayer):
def choose_move(self, battle):
possible_moves(battle.opponent_active_pokemon.species)
moves_with_usage_stats(battle.opponent_active_pokemon.species)
return self.choose_random_move(battle)
async def main():
random_player = RandomPlayer()
custom_player = CustomPlayer()
await random_player.battle_against(custom_player, 1)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
I resolved the issues I found when testing over a couple dozens battles - this is not thoroughly testes, but should work fine 99% of the time.
Hey,
I think adding the possible moves each mon can have would make agents much better, as they would know what to expect. Would it be possible to add them? As I understand, each mon has a pool of possible moves from which they get random moves. Also for OU and other similar tiers could get the moves from the monthly usage statistics, plus their use %.