Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions examples/project-pikachu/Poke-Env.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
### Poke-Env Environment:

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.

#### Installation:
```bash
pip install poke-env
```

#### Basic Usage:
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.

```json
{"action_type": "move" or "switch"}
```

Each battle outputs the observation of what the team or player has access to

team_1 = """
Goodra (M) @ Assault Vest
Ability: Sap Sipper
EVs: 248 HP / 252 SpA / 8 Spe
Modest Nature
IVs: 0 Atk
Dragon Pulse
Flamethrower
Sludge Wave
Thunderbolt

Sylveon (M) @ Leftovers
Ability: Pixilate
EVs: 248 HP / 244 Def / 16 SpD
Calm Nature
IVs: 0 Atk
Hyper Voice
Mystical Fire
Protect
Wish

Toxtricity (M) @ Throat Spray
Ability: Punk Rock
EVs: 4 Atk / 252 SpA / 252 Spe
Rash Nature
Overdrive
Boomburst
Shift Gear
Fire Punch

Seismitoad (M) @ Leftovers
Ability: Water Absorb
EVs: 252 HP / 252 Def / 4 SpD
Relaxed Nature
Stealth Rock
Scald
Earthquake
Toxic

Corviknight (M) @ Leftovers
Ability: Pressure
EVs: 248 HP / 80 SpD / 180 Spe
Impish Nature
Defog
Brave Bird
Roost
U-turn

Galvantula @ Focus Sash
Ability: Compound Eyes
EVs: 252 SpA / 4 SpD / 252 Spe
Timid Nature
IVs: 0 Atk
Sticky Web
Thunder Wave
Thunder
Energy Ball
"""

plus what player or team 2 has

team_2 = """
Togekiss @ Leftovers
Ability: Serene Grace
EVs: 248 HP / 8 SpA / 252 Spe
Timid Nature
IVs: 0 Atk
Air Slash
Nasty Plot
Substitute
Thunder Wave

Galvantula @ Focus Sash
Ability: Compound Eyes
EVs: 252 SpA / 4 SpD / 252 Spe
Timid Nature
IVs: 0 Atk
Sticky Web
Thunder Wave
Thunder
Energy Ball

Cloyster @ Leftovers
Ability: Skill Link
EVs: 252 Atk / 4 SpD / 252 Spe
Adamant Nature
Icicle Spear
Rock Blast
Ice Shard
Shell Smash

Sandaconda @ Focus Sash
Ability: Sand Spit
EVs: 252 Atk / 4 SpD / 252 Spe
Jolly Nature
Stealth Rock
Glare
Earthquake
Rock Tomb

Excadrill @ Focus Sash
Ability: Sand Rush
EVs: 252 Atk / 4 SpD / 252 Spe
Adamant Nature
Iron Head
Rock Slide
Earthquake
Rapid Spin

Cinccino @ Leftovers
Ability: Skill Link
EVs: 252 Atk / 4 Def / 252 Spe
Jolly Nature
Bullet Seed
Knock Off
Rock Blast
Tail Slap
"""
24 changes: 24 additions & 0 deletions examples/project-pikachu/poke_env/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
Pokemon Battle Environment for OpenEnv.

This module provides OpenEnv integration for Pokemon battles via poke-env.

Example:
>>> from envs.pokemon_env import PokemonEnv, PokemonAction
>>>
>>> # Connect to a running Pokemon Showdown server
>>> env = PokemonEnv(battle_format="gen8randombattle")
>>>
>>> # Reset and interact
>>> result = env.reset()
>>> result = env.step(PokemonAction(action_type="move", action_index=0))
>>> print(result.reward, result.done)
>>>
>>> # Cleanup
>>> env.close()
"""

from .client import PokemonEnv
from .models import PokemonAction, PokemonObservation, PokemonState, PokemonData

__all__ = ["PokemonEnv", "PokemonAction", "PokemonObservation", "PokemonState", "PokemonData"]
157 changes: 157 additions & 0 deletions examples/project-pikachu/poke_env/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""
Pokemon Battle Environment HTTP Client.

This module provides the client for connecting to a Pokemon Battle Environment server
over HTTP.
"""

from __future__ import annotations

from typing import Any, Dict, TYPE_CHECKING

from core.client_types import StepResult
from core.http_env_client import HTTPEnvClient

from .models import PokemonAction, PokemonObservation, PokemonState, PokemonData

if TYPE_CHECKING:
from core.containers.runtime import ContainerProvider


class PokemonEnv(HTTPEnvClient[PokemonAction, PokemonObservation]):
"""
HTTP client for Pokemon Battle Environment.

This client connects to a Pokemon Battle Environment HTTP server and provides
methods to interact with it: reset(), step(), and state access.

Example:
>>> # Connect to a running server
>>> client = PokemonEnv(base_url="http://localhost:8000")
>>> result = client.reset()
>>> print(result.observation.active_pokemon.species)
>>>
>>> # Take an action
>>> result = client.step(PokemonAction(action_type="move", action_index=0))
>>> print(result.reward, result.done)

Example with Docker:
>>> # Automatically start container and connect
>>> client = PokemonEnv.from_docker_image("pokemon-env:latest")
>>> result = client.reset()
>>> result = client.step(PokemonAction(action_type="switch", action_index=1))
"""

def _step_payload(self, action: PokemonAction) -> Dict[str, Any]:
"""
Convert PokemonAction to JSON payload for step request.

Args:
action: PokemonAction instance.

Returns:
Dictionary representation suitable for JSON encoding.
"""
return {
"action_type": action.action_type,
"action_index": action.action_index,
"move_id": action.move_id,
"switch_pokemon": action.switch_pokemon,
"mega_evolve": action.mega_evolve,
"dynamax": action.dynamax,
"terastallize": action.terastallize,
}

def _parse_pokemon_data(self, data: Dict[str, Any]) -> PokemonData:
"""Parse Pokemon data from JSON."""
return PokemonData(
species=data.get("species", "unknown"),
hp_percent=data.get("hp_percent", 0.0),
max_hp=data.get("max_hp", 100),
current_hp=data.get("current_hp", 0),
level=data.get("level", 50),
status=data.get("status"),
types=data.get("types", []),
ability=data.get("ability"),
item=data.get("item"),
attack=data.get("attack", 0),
defense=data.get("defense", 0),
special_attack=data.get("special_attack", 0),
special_defense=data.get("special_defense", 0),
speed=data.get("speed", 0),
boosts=data.get("boosts", {}),
moves=data.get("moves", []),
fainted=data.get("fainted", False),
active=data.get("active", False),
)

def _parse_result(self, payload: Dict[str, Any]) -> StepResult[PokemonObservation]:
"""
Parse server response into StepResult[PokemonObservation].

Args:
payload: JSON response from server.

Returns:
StepResult with PokemonObservation.
"""
obs_data = payload.get("observation", {})

active_pokemon = None
if obs_data.get("active_pokemon"):
active_pokemon = self._parse_pokemon_data(obs_data["active_pokemon"])

opponent_active = None
if obs_data.get("opponent_active_pokemon"):
opponent_active = self._parse_pokemon_data(obs_data["opponent_active_pokemon"])

team = [self._parse_pokemon_data(p) for p in obs_data.get("team", [])]
opponent_team = [self._parse_pokemon_data(p) for p in obs_data.get("opponent_team", [])]

observation = PokemonObservation(
active_pokemon=active_pokemon,
opponent_active_pokemon=opponent_active,
team=team,
opponent_team=opponent_team,
available_moves=obs_data.get("available_moves", []),
available_switches=obs_data.get("available_switches", []),
legal_actions=obs_data.get("legal_actions", []),
field_conditions=obs_data.get("field_conditions", {}),
turn=obs_data.get("turn", 0),
forced_switch=obs_data.get("forced_switch", False),
can_mega_evolve=obs_data.get("can_mega_evolve", False),
can_dynamax=obs_data.get("can_dynamax", False),
can_terastallize=obs_data.get("can_terastallize", False),
battle_format=obs_data.get("battle_format", "gen8randombattle"),
battle_id=obs_data.get("battle_id"),
done=payload.get("done", False),
reward=payload.get("reward"),
metadata=obs_data.get("metadata", {}),
)

return StepResult(
observation=observation,
reward=payload.get("reward"),
done=payload.get("done", False),
)

def _parse_state(self, payload: Dict[str, Any]) -> PokemonState:
"""
Parse server response into PokemonState object.

Args:
payload: JSON response from /state endpoint.

Returns:
PokemonState object with environment state information.
"""
return PokemonState(
episode_id=payload.get("episode_id"),
step_count=payload.get("step_count", 0),
battle_format=payload.get("battle_format", "gen8randombattle"),
player_username=payload.get("player_username", "player"),
server_url=payload.get("server_url", "localhost:8000"),
battle_id=payload.get("battle_id"),
is_battle_finished=payload.get("is_battle_finished", False),
battle_winner=payload.get("battle_winner"),
)
Loading