File size: 4,587 Bytes
690d660
7aca0bc
 
 
 
690d660
 
7aca0bc
 
 
 
 
690d660
 
7aca0bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
---
title: Chess Environment Server
emoji: ♟️
colorFrom: blue
colorTo: green
sdk: docker
pinned: false
app_port: 8000
base_path: /web
tags:
  - openenv-0.10.0
  - openenv
---

# Chess Environment

A chess reinforcement learning environment for OpenEnv, powered by the [moonfish](https://github.com/luccabb/moonfish) chess engine.

![Chess web playground](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/openenv/chess.png)

*The `/web` playground after a few moves: the board and one button per legal move.*

## Features

- **Full chess rules** via python-chess library
- **Configurable opponent**: moonfish engine, random moves, or self-play
- **Position evaluation**: Uses moonfish's PSQT-based evaluation
- **Standard OpenEnv interface**: reset(), step(), state

## Quick Start

### Using Docker

```bash
# Build the image
docker build -t chess-env:latest -f envs/chess_env/server/Dockerfile .

# Run the server
docker run -p 8000:8000 chess-env:latest
```

### Using the Client

The client is **async by default**:

```python
import asyncio
from chess_env import ChessEnv, ChessAction

async def main():
    async with ChessEnv(base_url="http://localhost:8000") as env:
        # Reset for a new game
        result = await env.reset()
        print(f"Starting position: {result.observation.fen}")
        print(f"Legal moves: {result.observation.legal_moves}")

        # Make a move
        result = await env.step(ChessAction(move="e2e4"))
        print(f"Reward: {result.reward}, Done: {result.done}")

        # Play until game ends
        while not result.done:
            move = result.observation.legal_moves[0]
            result = await env.step(ChessAction(move=move))

        print(f"Game result: {result.observation.result}")

asyncio.run(main())
```

For **synchronous usage**, use the `.sync()` wrapper:

```python
from chess_env import ChessEnv, ChessAction

with ChessEnv(base_url="http://localhost:8000").sync() as env:
    result = env.reset()
    result = env.step(ChessAction(move="e2e4"))
    print(f"Reward: {result.reward}")
```

## Observation Space

| Field | Type | Description |
|-------|------|-------------|
| `fen` | str | Board position in FEN notation |
| `legal_moves` | List[str] | Legal moves in UCI format |
| `is_check` | bool | Whether current player is in check |
| `done` | bool | Whether game has ended |
| `reward` | float | Reward for last action |
| `result` | str | Game result ("1-0", "0-1", "1/2-1/2") |

## Action Space

| Field | Type | Description |
|-------|------|-------------|
| `move` | str | UCI format move (e.g., "e2e4", "e7e8q") |

## Rewards

| Outcome | Reward |
|---------|--------|
| Win | +1.0 |
| Loss | -1.0 |
| Draw | 0.0 |
| Illegal move | -0.1 |

## Configuration

The environment supports these configuration options:

| Parameter | Default | Description |
|-----------|---------|-------------|
| `opponent` | "moonfish" | Opponent type: "moonfish", "random", or None |
| `opponent_depth` | 2 | Search depth for moonfish opponent |
| `max_moves` | 500 | Maximum half-moves before draw |
| `agent_color` | None | Agent color: "white", "black", or None (alternate each episode) |
| `gamma` | 0.99 | Discount factor for temporal credit assignment |

## Temporal Discounting

For RL training, the environment computes temporally discounted rewards at episode end. This helps with credit assignment in long games where only the final outcome is known.

When an episode ends, the terminal observation's `metadata` includes:
- `discounted_rewards`: List of discounted rewards for each agent move
- `gamma`: The discount factor used

The formula is `r_t = γ^(T-1-t) × R_final` where:
- `T` = total agent moves
- `t` = move index (0-indexed)
- `R_final` = terminal reward (+1, -1, or 0)

Example for a 5-move win with γ=0.99:
```
Move 0: 0.99^4 × 1.0 = 0.961
Move 1: 0.99^3 × 1.0 = 0.970
Move 2: 0.99^2 × 1.0 = 0.980
Move 3: 0.99^1 × 1.0 = 0.990
Move 4: 0.99^0 × 1.0 = 1.000
```

## Links

- [moonfish GitHub](https://github.com/luccabb/moonfish)
- [Play online](https://huggingface.co/spaces/luccabb/moonfish_chess)

## Hugging Face Space Deployment

This Space is built from OpenEnv environment `chess_env`.

- Space URL: `https://huggingface.co/spaces/openenv/chess_env`
- OpenEnv pinned ref: `0.10.0`
- Hub tag: `openenv`

### Connecting from Code

```bash
pip install git+https://huggingface.co/spaces/openenv/chess_env
```

```python
from chess_env import ChessEnv

with ChessEnv(base_url="https://openenv-chess-env.hf.space").sync() as env:
    result = env.reset()
```