openspiel_env / README.md
sergiopaniego's picture
Upload folder using huggingface_hub
c65b2a4 verified
---
title: OpenSpiel Environment Server
emoji: ๐ŸŽฎ
colorFrom: blue
colorTo: purple
sdk: docker
pinned: false
app_port: 8000
base_path: /web
tags:
- openenv
---
# OpenSpiel Environment
Integration of OpenSpiel games with the OpenEnv framework. [OpenSpiel](https://github.com/google-deepmind/open_spiel) is DeepMind's collection of 70+ game environments for RL research.
## Supported Games
This environment supports 6 games across different categories:
### Single-Player Games (No Opponent)
1. **Catch** - Move horizontally to catch a falling ball
2. **Cliff Walking** - Navigate grid without falling off cliff (Sutton & Barto benchmark)
3. **2048** - Classic tile-merging puzzle game
4. **Blackjack** - Simplified blackjack (HIT/STAND only)
### Multi-Player Games (with Bot Opponent)
5. **Tic-Tac-Toe** - Classic 3x3 game
6. **Kuhn Poker** - 2-player simplified poker (game theory benchmark)
## Quick Start
The simplest way to use the OpenSpiel environment is through the `OpenSpielEnv` class:
```python
from openspiel_env import OpenSpielEnv, OpenSpielAction
try:
# Create environment from Docker image
env = OpenSpielEnv.from_docker_image("openspiel-env:latest")
# Reset to start a new episode
result = env.reset()
print(f"Initial state: {result.observation.info_state}")
print(f"Legal actions: {result.observation.legal_actions}")
# Play until done
while not result.done:
action_id = result.observation.legal_actions[0]
result = env.step(OpenSpielAction(action_id=action_id))
print(f"Reward: {result.reward}, Done: {result.done}")
finally:
# Always clean up
env.close()
```
That's it! The `OpenSpielEnv.from_docker_image()` method handles:
- Starting the Docker container
- Waiting for the server to be ready
- Connecting to the environment
- Container cleanup when you call `close()`
## Building the Docker Image
OpenSpiel requires compilation from C++ source. The Docker build uses a **pre-built base image** by default to avoid long build times.
### Default Build (Recommended)
From the **environment directory** (`envs/openspiel_env/`):
```bash
# Uses pre-built base image from GHCR (fast, ~1-2 min)
docker build -t openspiel-env:latest -f server/Dockerfile .
```
This uses the pre-built `ghcr.io/meta-pytorch/openenv-openspiel-base` image which already contains compiled OpenSpiel.
### Building Your Own Base Image (Optional)
If you need to customize OpenSpiel or can't access the pre-built image:
```bash
# Step 1: Build the base image (compiles OpenSpiel, ~30-60 min)
docker build -t openspiel-base:latest -f server/Dockerfile.openspiel-base .
# Step 2: Build the environment using your local base image
docker build -t openspiel-env:latest \
--build-arg OPENSPIEL_BASE_IMAGE=openspiel-base:latest \
-f server/Dockerfile .
```
## Deploying to Hugging Face Spaces
You can easily deploy your OpenEnv environment to Hugging Face Spaces using the `openenv push` command:
```bash
# From the environment directory (envs/openspiel_env/)
openenv push
# Or specify options
openenv push --namespace my-org --private
```
The `openenv push` command will:
1. Validate that the directory is an OpenEnv environment (checks for `openenv.yaml`)
2. Prepare a custom build for Hugging Face Docker space (enables web interface)
3. Upload to Hugging Face (ensuring you're logged in)
### Prerequisites
- Authenticate with Hugging Face: The command will prompt for login if not already authenticated
### Options
- `--directory`, `-d`: Directory containing the OpenEnv environment (defaults to current directory)
- `--repo-id`, `-r`: Repository ID in format 'username/repo-name' (defaults to 'username/env-name' from openenv.yaml)
- `--base-image`, `-b`: Base Docker image to use (overrides Dockerfile FROM)
- `--private`: Deploy the space as private (default: public)
### Examples
```bash
# Push to your personal namespace (defaults to username/env-name from openenv.yaml)
openenv push
# Push to a specific repository
openenv push --repo-id my-org/openspiel-env
# Push as a private space
openenv push --private
# Combine options
openenv push --repo-id my-org/openspiel-env --private
```
After deployment, your space will be available at:
`https://huggingface.co/spaces/<repo-id>`
The deployed space includes:
- **Web Interface** at `/web` - Interactive UI for exploring the environment
- **API Documentation** at `/docs` - Full OpenAPI/Swagger interface
- **Health Check** at `/health` - Container health monitoring
> **Note**: The default Dockerfile uses a pre-built base image with OpenSpiel already compiled, so deployment is fast and works with standard CPU hardware. If you build your own base image, compilation requires more resources and time.
## Running Specific Games
```bash
# Catch (default)
docker run -p 8000:8000 openspiel-env:latest
# Tic-Tac-Toe with random opponent
docker run -p 8000:8000 -e OPENSPIEL_GAME=tic_tac_toe openspiel-env:latest
# Kuhn Poker
docker run -p 8000:8000 -e OPENSPIEL_GAME=kuhn_poker openspiel-env:latest
# 2048
docker run -p 8000:8000 -e OPENSPIEL_GAME=2048 openspiel-env:latest
# Blackjack
docker run -p 8000:8000 -e OPENSPIEL_GAME=blackjack openspiel-env:latest
# Cliff Walking
docker run -p 8000:8000 -e OPENSPIEL_GAME=cliff_walking openspiel-env:latest
```
## Environment Details
### Action
**OpenSpielAction**: Contains the action to take
- `action_id` (int) - Action ID to execute
- `game_name` (str) - Game name (default: "catch")
- `game_params` (Dict) - Optional game parameters
### Observation
**OpenSpielObservation**: Contains the game state
- `info_state` (List[float]) - Agent's information state vector
- `legal_actions` (List[int]) - Legal action IDs
- `game_phase` (str) - "initial", "playing", or "terminal"
- `current_player_id` (int) - Current player (-1 for simultaneous)
- `opponent_last_action` (Optional[int]) - Last opponent action
- `done` (bool) - Whether the episode has ended
- `reward` (Optional[float]) - Reward for the last action
### State
**OpenSpielState**: Server-side state snapshot
- `episode_id` (str) - Unique identifier for the current episode
- `step_count` (int) - Number of steps taken
- `game_name` (str) - Game name
- `agent_player` (int) - Agent's player ID
- `opponent_policy` (str) - Opponent policy name
- `num_players` (int) - Total players
## Configuration
### Environment Variables
- `OPENSPIEL_GAME`: Game name (default: "catch")
- `OPENSPIEL_AGENT_PLAYER`: Player ID for agent (default: 0)
- `OPENSPIEL_OPPONENT_POLICY`: Opponent policy for multi-player games
- `random`: Uniform random (default)
- `first`: Always picks first legal action
- `last`: Always picks last legal action
### Example: Tic-Tac-Toe with Fixed Opponent
```bash
docker run -p 8000:8000 \
-e OPENSPIEL_GAME=tic_tac_toe \
-e OPENSPIEL_OPPONENT_POLICY=first \
openspiel-env:latest
```
## Advanced Usage
### Connecting to an Existing Server
If you already have an OpenSpiel environment server running:
```python
from openspiel_env import OpenSpielEnv, OpenSpielAction
# Connect to existing server
env = OpenSpielEnv(base_url="http://localhost:8000")
# Use as normal
result = env.reset()
result = env.step(OpenSpielAction(action_id=result.observation.legal_actions[0]))
# Close connection (does NOT stop the server)
env.close()
```
### Connecting to HuggingFace Space
```python
from openspiel_env import OpenSpielEnv, OpenSpielAction
# Connect to remote Space
env = OpenSpielEnv(base_url="https://your-username-openspiel.hf.space")
result = env.reset()
print(f"Game: {result.observation.game_phase}")
print(f"Legal actions: {result.observation.legal_actions}")
result = env.step(OpenSpielAction(action_id=result.observation.legal_actions[0]))
env.close()
```
## Game-Specific Information
### 1. Catch
- **Type**: Single-player
- **Action Space**: 3 actions (left, stay, right)
- **Observation**: 5x5 grid flattened (25 dimensions)
- **Reward**: +1 for catching ball, 0 otherwise
- **Episode Length**: ~10 steps
### 2. Tic-Tac-Toe
- **Type**: 2-player turn-based, perfect information
- **Players**: Agent (X) vs Random Bot (O)
- **Action Space**: 9 positions
- **Observation**: 27 dimensions (3x3 board + game state)
- **Reward**: +1 win, -1 loss, 0 draw/mid-game
### 3. Kuhn Poker
- **Type**: 2-player turn-based, imperfect information
- **Players**: Agent vs Random Bot
- **Action Space**: 2 actions (pass/fold, bet/call)
- **Observation**: 6 dimensions (card + betting history)
- **Reward**: Pot winnings (typically -1, 0, +1, +2)
- **Notes**: THE benchmark for imperfect-information RL
### 4. Cliff Walking
- **Type**: Single-player grid world
- **Action Space**: 4 actions (up, down, left, right)
- **Observation**: Position encoding
- **Reward**: -1 per step, -100 for falling off cliff
- **Notes**: Classic RL benchmark from Sutton & Barto
### 5. 2048
- **Type**: Single-player puzzle
- **Action Space**: 4 actions (up, down, left, right)
- **Observation**: 4x4 grid with tile values
- **Reward**: Points from merging tiles
- **Notes**: Stochastic tile spawning
### 6. Blackjack
- **Type**: Single-player vs dealer
- **Action Space**: 2 actions (HIT, STAND)
- **Observation**: Player hand + dealer's visible card
- **Reward**: +1 win, -1 loss, 0 draw
- **Notes**: Simplified version, no double/split
## Development & Testing
### Direct Environment Testing
Test the environment logic directly without starting the HTTP server (requires OpenSpiel installed locally):
```python
from openspiel_env.server.openspiel_environment import OpenSpielEnvironment
from openspiel_env.models import OpenSpielAction
# Create environment directly
env = OpenSpielEnvironment(game_name="catch")
# Test reset
obs = env.reset()
print(f"Info state: {obs.info_state}")
# Test step
obs = env.step(OpenSpielAction(action_id=0))
print(f"Done: {obs.done}, Reward: {obs.reward}")
```
### Running Locally
Run the server locally for development (requires OpenSpiel installed):
```bash
# From the environment directory
cd envs/openspiel_env
# Install dependencies
uv venv && source .venv/bin/activate
uv pip install -e .
# Start the server
python -m uvicorn server.app:app --reload
```
Or using the CLI entry point:
```bash
uv run --project . server --port 8000
```
### Automated Testing (All 6 Games)
```bash
./test_docker_all_games.sh
```
This script will build and test all 6 supported games in Docker.
## Project Structure
```
openspiel_env/
โ”œโ”€โ”€ __init__.py # Module exports
โ”œโ”€โ”€ README.md # This file
โ”œโ”€โ”€ openenv.yaml # OpenEnv manifest
โ”œโ”€โ”€ pyproject.toml # Project metadata and dependencies
โ”œโ”€โ”€ client.py # OpenSpielEnv client implementation
โ”œโ”€โ”€ models.py # Action, Observation, and State models
โ”œโ”€โ”€ test_docker_all_games.sh # Automated test script
โ””โ”€โ”€ server/
โ”œโ”€โ”€ __init__.py # Server module exports
โ”œโ”€โ”€ openspiel_environment.py # Core OpenSpielEnvironment implementation
โ”œโ”€โ”€ opponent_policies.py # Opponent policies (random, fixed)
โ”œโ”€โ”€ app.py # FastAPI application
โ”œโ”€โ”€ Dockerfile # Environment container (uses pre-built base)
โ””โ”€โ”€ Dockerfile.openspiel-base # Base image with compiled OpenSpiel
```
## Limitations
- **Simultaneous-move games**: Only agent_player=0 supported
- **Multi-agent training**: Single agent only (no self-play yet)
- **Opponent policies**: Random and fixed only (no MCTS yet)
- **Build time**: Building your own base image takes ~30-60 min (compiles OpenSpiel C++). Using the pre-built image is fast (~1-2 min) and works with standard hardware.
## References
- [OpenSpiel Paper (2019)](https://arxiv.org/abs/1908.09453)
- [OpenSpiel GitHub](https://github.com/google-deepmind/open_spiel)
- [OpenSpiel Documentation](https://openspiel.readthedocs.io/)