hsahovic / poke-env

A python interface for training Reinforcement Learning bots to battle on pokemon showdown
https://poke-env.readthedocs.io/
MIT License
294 stars 99 forks source link

Feature: Add unique integers for pokemon, moves, items, and natures #204

Open Benjamin-Etheredge opened 3 years ago

Benjamin-Etheredge commented 3 years ago

I'm still going through the code base, so this might already exist in some form. I noticed the existing JSON data in the project, so it got me thinking better access would be awesome.

My objective is to learn embeddings in my models. That requires translating values to integers (like an OrdinalEncoder/LabelEncoder in sklearn). So I need a way of encoding a Pokemon into a unique integer for its species. I would repeat this for items, moves, nature, and types.

Currently, I don't see an easy way of doing this. I'm thinking my best bet is to modify the underlying code to give that functionality. This would be achieved by doing some extra stuff when reading in the data. Or maybe just adding information to the underlying JSON files. I'm not sure yet.

Thoughts?

hsahovic commented 3 years ago

Hey @Benjamin-Etheredge,

Thanks for opening this issue. This is a common use case, but I don't think it should be a feature of poke-env, as it's more of a modelling decision. That being said, you could use something along those lines to get started:

# -*- coding: utf-8 -*-
import asyncio

from poke_env.player.random_player import RandomPlayer
from poke_env.data import POKEDEX, MOVES, NATURES
from functools import lru_cache

import requests

@lru_cache(None)
def pokemon_to_int(mon):
    return POKEDEX[mon.species]["num"]

ITEMS = requests.get(
    "https://raw.githubusercontent.com/itsjavi/showdown-data/main/dist/data/items.json"
).json()["Items"]

ITEM_IDX_MAP = {item: i for i, item in enumerate(sorted(ITEMS))}
ITEM_IDX_MAP[None] = len(ITEM_IDX_MAP)
ITEM_IDX_MAP[""] = len(ITEM_IDX_MAP)

MOVE_IDX_MAP = {move: i for i, move in enumerate(sorted(MOVES))}

# Natures are not exposed in pokemon objects, but you could use it for teambuilding
# for instance
NATURE_IDX_MAP = {nature: i for i, nature in enumerate(sorted(NATURES))}

class CustomPlayer(RandomPlayer):
    def choose_move(self, battle):
        mon = battle.active_pokemon
        print(
            pokemon_to_int(battle.active_pokemon),
            [MOVE_IDX_MAP[m] for m in mon.moves],
            ITEM_IDX_MAP[mon.item],
        )
        return self.choose_random_move(battle)

async def main():
    random_player = RandomPlayer()
    custom_player = CustomPlayer()

    await random_player.battle_against(custom_player, n_battles=1)

if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(main())
Benjamin-Etheredge commented 3 years ago

Awesome! Thanks for the code! I'll make use of it.

I'm still not entirely convinced it shouldn't be part of the code base. There is already some starter code in there for building RL agents, so it might still be worth having. I'll have to think about it more and test it out.

hsahovic commented 3 years ago

Maybe as part of a set of helper functions? What other tools would you like to see in there?

Benjamin-Etheredge commented 3 years ago

I'm not sure. Helper functions or enums or something like that would be cool to add. The ability to quickly encode pokemon to unique integers can be used by a lot of AI applications. Learned embeddings is getting super popular. I'm planning to see if I can get a learner working with only learned encodings like those.