Metadata-Version: 2.4
Name: gym-bullet-chess
Version: 0.1.0
Summary: A Gymnasium chess environment for quality-latency research with language agents
Author: gym-bullet-chess contributors
License-Expression: GPL-3.0-or-later
Project-URL: Repository, https://github.com/ChoiCube84/gym-bullet-chess
Keywords: chess,gymnasium,reinforcement-learning,llm,vlm
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: THIRD_PARTY_LICENSES.md
License-File: src/gym_bullet_chess/assets/ATTRIBUTION.md
Requires-Dist: chess>=1.11.2
Requires-Dist: gymnasium>=1.0.0
Requires-Dist: numpy>=1.24.0
Requires-Dist: Pillow>=10.0.0
Provides-Extra: dev
Requires-Dist: build>=1.2.2; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: ruff>=0.8; extra == "dev"
Dynamic: license-file

# gym-bullet-chess

`gym-bullet-chess` is a Gymnasium environment for studying whether an agent can
trade move quality against the time consumed by its own reasoning.

The package does not contain a chess engine. `python-chess` is used only for
rules, legal moves, and outcomes. A language agent may reason, call tools, or
inspect a VLM-ready board image, but all work performed before it submits a move
can consume its clock.

## Install

```bash
python -m pip install -e ".[dev]"
```

Python 3.10 or newer is required.

## Real-time evaluation

`RealTimeClock` measures from the instant an observation is returned until the
next action reaches `step()`. Model inference, network latency, and tool calls
inside that interval are charged to the side to move. Environment execution is
excluded from the next decision's budget.

```python
import gymnasium as gym
import gym_bullet_chess
from gym_bullet_chess import RealTimeClock

env = gym.make(
    "BulletChess-v0",
    initial_seconds=60.0,
    increment_seconds=0.0,
    include_board_image=True,
)
env = RealTimeClock(env)

observation, info = env.reset(seed=7)
action = int(observation["action_mask"].nonzero()[0][0])
observation, reward, terminated, truncated, info = env.step(action)
```

## Reproducible latency experiments

Wall time is appropriate for evaluation but unsuitable for deterministic
training. `SimulatedClock` injects a constant or scheduled latency while
preserving the exact `Discrete` action contract.

```python
from gym_bullet_chess import BulletChessEnv, SimulatedClock

env = SimulatedClock(
    BulletChessEnv(initial_seconds=10.0, increment_seconds=0.1),
    think_time=lambda decision_index, action: 0.05 + decision_index * 0.01,
)
```

The unwrapped environment also exposes
`step_timed(action, elapsed_seconds)` for explicit experiment orchestration.
Invalid, negative, NaN, and infinite durations are rejected.

## Language and vision agents

Set `include_board_image=True` to include a headless RGB array:

| Key | Shape | Type | Meaning |
| --- | --- | --- | --- |
| `board` | `(8, 8, 18)` | `float32` | 12 piece planes, turn, castling rights, and en-passant target |
| `state` | `(10,)` | `float32` | Rule state including repetition and claimable-draw features |
| `clocks` | `(2,)` | `float32` | Raw white and black seconds |
| `action_mask` | `(20481,)` | `int8` | Legal move and draw-claim actions |
| `board_image` | `(image_size, image_size, 3)` | `uint8` | Optional Cburnett RGB board image |

`info` contains the FEN, legal UCI moves, both clocks, current view, last move,
outcome, and timing fields. For language-model tools, use `UCIActionWrapper`
and `format_position_for_llm`:

```python
from gym_bullet_chess import (
    BulletChessEnv,
    RealTimeClock,
    UCIActionWrapper,
    format_position_for_llm,
)

env = RealTimeClock(UCIActionWrapper(BulletChessEnv(include_board_image=True)))
observation, info = env.reset()
prompt_context = format_position_for_llm(info)
observation, reward, terminated, truncated, info = env.step("e2e4")
```

The string `claim_draw` is accepted when a draw is legally claimable.

## Actions and perspectives

The discrete action space covers all standard legal moves:

```text
((from_square * 64) + to_square) * 5 + promotion_slot
```

Promotion slots are none, queen, rook, bishop, and knight. Action `20480` is a
draw claim. This supports underpromotions instead of silently forcing a queen.

With `perspective="side_to_move"` (the default), board coordinates, images,
action encoding, and action masks all rotate together for Black. Fixed
`white`, `black`, and `agent` perspectives are also available. The helpers
`action_for_uci()` and `uci_for_action()` avoid manual encoding.

## Play modes

- `opponent=None`: self-play. Each step advances one ply and terminal reward is
  relative to the side that submitted the action.
- `opponent="random"`: a seeded legal-move baseline responds automatically.
  Reward is always relative to `agent_color`.
- Custom opponents implement `select_move(board, rng)` and return
  `OpponentDecision(move, elapsed_seconds)`. Their latency is charged to their
  own clock.

The random opponent is a baseline for integration tests, not a substitute for
an LLM agent or an evaluation opponent.

## Rewards and endings

A win returns `+1.0`, a loss returns `-1.0`, and a draw returns `0.0`. In
self-play the result is relative to the actor that submitted the terminal action;
against an opponent it is relative to `agent_color`. Illegal actions terminate
the episode with `illegal_move_reward` (default `-1.0`). Clock expiration is a
draw when the non-flagging side lacks mating material.

## Experiment trace

`env.unwrapped.episode_trace` stores one record per decision with actor, source,
integer action, UCI/SAN move, elapsed time, remaining time, and event. Terminal
`info["outcome"]` records winner, result, and termination cause.

## License and assets

The project is GPL-3.0-or-later because `python-chess` is GPL-3.0-or-later.
Cburnett piece images are freshly downloaded Wikimedia rasterizations. The
upstream multi-license, selected BSD-3-Clause terms, source URL for every file,
and SHA-256 hashes are recorded in
[`src/gym_bullet_chess/assets/ATTRIBUTION.md`](src/gym_bullet_chess/assets/ATTRIBUTION.md).
Other dependency licenses are listed in
[`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md).
