Skip to content

Commit 62bede1

Browse files
authored
Merge pull request #124 from cpich3g/project-pikachu
Project pikachu - First Draft
2 parents b962714 + f1685c1 commit 62bede1

File tree

13 files changed

+1119
-0
lines changed

13 files changed

+1119
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
### Poke-Env Environment:
2+
3+
The Poke-Env is a Python library that provides an environment for training reinforcement learning agents to play Pokémon battles. It simulates the mechanics of Pokémon games, allowing agents to learn strategies and make decisions in battles. Gen8OU is the game format used in this example.
4+
5+
#### Installation:
6+
```bash
7+
pip install poke-env
8+
```
9+
10+
#### Basic Usage:
11+
Poke-Env allows battles against a random player or bots. Each battle consists of turns where the agent selects actions based on the current state of the game.
12+
13+
```json
14+
{"action_type": "move" or "switch"}
15+
```
16+
17+
Each battle outputs the observation of what the team or player has access to
18+
19+
team_1 = """
20+
Goodra (M) @ Assault Vest
21+
Ability: Sap Sipper
22+
EVs: 248 HP / 252 SpA / 8 Spe
23+
Modest Nature
24+
IVs: 0 Atk
25+
Dragon Pulse
26+
Flamethrower
27+
Sludge Wave
28+
Thunderbolt
29+
30+
Sylveon (M) @ Leftovers
31+
Ability: Pixilate
32+
EVs: 248 HP / 244 Def / 16 SpD
33+
Calm Nature
34+
IVs: 0 Atk
35+
Hyper Voice
36+
Mystical Fire
37+
Protect
38+
Wish
39+
40+
Toxtricity (M) @ Throat Spray
41+
Ability: Punk Rock
42+
EVs: 4 Atk / 252 SpA / 252 Spe
43+
Rash Nature
44+
Overdrive
45+
Boomburst
46+
Shift Gear
47+
Fire Punch
48+
49+
Seismitoad (M) @ Leftovers
50+
Ability: Water Absorb
51+
EVs: 252 HP / 252 Def / 4 SpD
52+
Relaxed Nature
53+
Stealth Rock
54+
Scald
55+
Earthquake
56+
Toxic
57+
58+
Corviknight (M) @ Leftovers
59+
Ability: Pressure
60+
EVs: 248 HP / 80 SpD / 180 Spe
61+
Impish Nature
62+
Defog
63+
Brave Bird
64+
Roost
65+
U-turn
66+
67+
Galvantula @ Focus Sash
68+
Ability: Compound Eyes
69+
EVs: 252 SpA / 4 SpD / 252 Spe
70+
Timid Nature
71+
IVs: 0 Atk
72+
Sticky Web
73+
Thunder Wave
74+
Thunder
75+
Energy Ball
76+
"""
77+
78+
plus what player or team 2 has
79+
80+
team_2 = """
81+
Togekiss @ Leftovers
82+
Ability: Serene Grace
83+
EVs: 248 HP / 8 SpA / 252 Spe
84+
Timid Nature
85+
IVs: 0 Atk
86+
Air Slash
87+
Nasty Plot
88+
Substitute
89+
Thunder Wave
90+
91+
Galvantula @ Focus Sash
92+
Ability: Compound Eyes
93+
EVs: 252 SpA / 4 SpD / 252 Spe
94+
Timid Nature
95+
IVs: 0 Atk
96+
Sticky Web
97+
Thunder Wave
98+
Thunder
99+
Energy Ball
100+
101+
Cloyster @ Leftovers
102+
Ability: Skill Link
103+
EVs: 252 Atk / 4 SpD / 252 Spe
104+
Adamant Nature
105+
Icicle Spear
106+
Rock Blast
107+
Ice Shard
108+
Shell Smash
109+
110+
Sandaconda @ Focus Sash
111+
Ability: Sand Spit
112+
EVs: 252 Atk / 4 SpD / 252 Spe
113+
Jolly Nature
114+
Stealth Rock
115+
Glare
116+
Earthquake
117+
Rock Tomb
118+
119+
Excadrill @ Focus Sash
120+
Ability: Sand Rush
121+
EVs: 252 Atk / 4 SpD / 252 Spe
122+
Adamant Nature
123+
Iron Head
124+
Rock Slide
125+
Earthquake
126+
Rapid Spin
127+
128+
Cinccino @ Leftovers
129+
Ability: Skill Link
130+
EVs: 252 Atk / 4 Def / 252 Spe
131+
Jolly Nature
132+
Bullet Seed
133+
Knock Off
134+
Rock Blast
135+
Tail Slap
136+
"""
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
Pokemon Battle Environment for OpenEnv.
3+
4+
This module provides OpenEnv integration for Pokemon battles via poke-env.
5+
6+
Example:
7+
>>> from envs.pokemon_env import PokemonEnv, PokemonAction
8+
>>>
9+
>>> # Connect to a running Pokemon Showdown server
10+
>>> env = PokemonEnv(battle_format="gen8randombattle")
11+
>>>
12+
>>> # Reset and interact
13+
>>> result = env.reset()
14+
>>> result = env.step(PokemonAction(action_type="move", action_index=0))
15+
>>> print(result.reward, result.done)
16+
>>>
17+
>>> # Cleanup
18+
>>> env.close()
19+
"""
20+
21+
from .client import PokemonEnv
22+
from .models import PokemonAction, PokemonObservation, PokemonState, PokemonData
23+
24+
__all__ = ["PokemonEnv", "PokemonAction", "PokemonObservation", "PokemonState", "PokemonData"]
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
"""
2+
Pokemon Battle Environment HTTP Client.
3+
4+
This module provides the client for connecting to a Pokemon Battle Environment server
5+
over HTTP.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
from typing import Any, Dict, TYPE_CHECKING
11+
12+
from core.client_types import StepResult
13+
from core.http_env_client import HTTPEnvClient
14+
15+
from .models import PokemonAction, PokemonObservation, PokemonState, PokemonData
16+
17+
if TYPE_CHECKING:
18+
from core.containers.runtime import ContainerProvider
19+
20+
21+
class PokemonEnv(HTTPEnvClient[PokemonAction, PokemonObservation]):
22+
"""
23+
HTTP client for Pokemon Battle Environment.
24+
25+
This client connects to a Pokemon Battle Environment HTTP server and provides
26+
methods to interact with it: reset(), step(), and state access.
27+
28+
Example:
29+
>>> # Connect to a running server
30+
>>> client = PokemonEnv(base_url="http://localhost:8000")
31+
>>> result = client.reset()
32+
>>> print(result.observation.active_pokemon.species)
33+
>>>
34+
>>> # Take an action
35+
>>> result = client.step(PokemonAction(action_type="move", action_index=0))
36+
>>> print(result.reward, result.done)
37+
38+
Example with Docker:
39+
>>> # Automatically start container and connect
40+
>>> client = PokemonEnv.from_docker_image("pokemon-env:latest")
41+
>>> result = client.reset()
42+
>>> result = client.step(PokemonAction(action_type="switch", action_index=1))
43+
"""
44+
45+
def _step_payload(self, action: PokemonAction) -> Dict[str, Any]:
46+
"""
47+
Convert PokemonAction to JSON payload for step request.
48+
49+
Args:
50+
action: PokemonAction instance.
51+
52+
Returns:
53+
Dictionary representation suitable for JSON encoding.
54+
"""
55+
return {
56+
"action_type": action.action_type,
57+
"action_index": action.action_index,
58+
"move_id": action.move_id,
59+
"switch_pokemon": action.switch_pokemon,
60+
"mega_evolve": action.mega_evolve,
61+
"dynamax": action.dynamax,
62+
"terastallize": action.terastallize,
63+
}
64+
65+
def _parse_pokemon_data(self, data: Dict[str, Any]) -> PokemonData:
66+
"""Parse Pokemon data from JSON."""
67+
return PokemonData(
68+
species=data.get("species", "unknown"),
69+
hp_percent=data.get("hp_percent", 0.0),
70+
max_hp=data.get("max_hp", 100),
71+
current_hp=data.get("current_hp", 0),
72+
level=data.get("level", 50),
73+
status=data.get("status"),
74+
types=data.get("types", []),
75+
ability=data.get("ability"),
76+
item=data.get("item"),
77+
attack=data.get("attack", 0),
78+
defense=data.get("defense", 0),
79+
special_attack=data.get("special_attack", 0),
80+
special_defense=data.get("special_defense", 0),
81+
speed=data.get("speed", 0),
82+
boosts=data.get("boosts", {}),
83+
moves=data.get("moves", []),
84+
fainted=data.get("fainted", False),
85+
active=data.get("active", False),
86+
)
87+
88+
def _parse_result(self, payload: Dict[str, Any]) -> StepResult[PokemonObservation]:
89+
"""
90+
Parse server response into StepResult[PokemonObservation].
91+
92+
Args:
93+
payload: JSON response from server.
94+
95+
Returns:
96+
StepResult with PokemonObservation.
97+
"""
98+
obs_data = payload.get("observation", {})
99+
100+
active_pokemon = None
101+
if obs_data.get("active_pokemon"):
102+
active_pokemon = self._parse_pokemon_data(obs_data["active_pokemon"])
103+
104+
opponent_active = None
105+
if obs_data.get("opponent_active_pokemon"):
106+
opponent_active = self._parse_pokemon_data(obs_data["opponent_active_pokemon"])
107+
108+
team = [self._parse_pokemon_data(p) for p in obs_data.get("team", [])]
109+
opponent_team = [self._parse_pokemon_data(p) for p in obs_data.get("opponent_team", [])]
110+
111+
observation = PokemonObservation(
112+
active_pokemon=active_pokemon,
113+
opponent_active_pokemon=opponent_active,
114+
team=team,
115+
opponent_team=opponent_team,
116+
available_moves=obs_data.get("available_moves", []),
117+
available_switches=obs_data.get("available_switches", []),
118+
legal_actions=obs_data.get("legal_actions", []),
119+
field_conditions=obs_data.get("field_conditions", {}),
120+
turn=obs_data.get("turn", 0),
121+
forced_switch=obs_data.get("forced_switch", False),
122+
can_mega_evolve=obs_data.get("can_mega_evolve", False),
123+
can_dynamax=obs_data.get("can_dynamax", False),
124+
can_terastallize=obs_data.get("can_terastallize", False),
125+
battle_format=obs_data.get("battle_format", "gen8randombattle"),
126+
battle_id=obs_data.get("battle_id"),
127+
done=payload.get("done", False),
128+
reward=payload.get("reward"),
129+
metadata=obs_data.get("metadata", {}),
130+
)
131+
132+
return StepResult(
133+
observation=observation,
134+
reward=payload.get("reward"),
135+
done=payload.get("done", False),
136+
)
137+
138+
def _parse_state(self, payload: Dict[str, Any]) -> PokemonState:
139+
"""
140+
Parse server response into PokemonState object.
141+
142+
Args:
143+
payload: JSON response from /state endpoint.
144+
145+
Returns:
146+
PokemonState object with environment state information.
147+
"""
148+
return PokemonState(
149+
episode_id=payload.get("episode_id"),
150+
step_count=payload.get("step_count", 0),
151+
battle_format=payload.get("battle_format", "gen8randombattle"),
152+
player_username=payload.get("player_username", "player"),
153+
server_url=payload.get("server_url", "localhost:8000"),
154+
battle_id=payload.get("battle_id"),
155+
is_battle_finished=payload.get("is_battle_finished", False),
156+
battle_winner=payload.get("battle_winner"),
157+
)

0 commit comments

Comments
 (0)