# Play-testing with simulation

> Run gameplay without a renderer using game.simulate, with scheduled inputs, and read the report to verify behaviour.

Source: https://feather-engine.com/docs/agents/simulation/

`game.simulate` plays a fresh copy of the committed project without a window or renderer. It uses
the same scripts, Blueprints, Rapier physics and player controller as Play and the exported player,
runs for a fixed number of 1/60-second ticks with scheduled key presses, and returns a report. The
project, selection, revision and undo history are never changed.

Use it to check that a mechanic works after an edit: that a door opens, a body falls, a script runs
without errors, or the player can reach a platform.

## Run a simulation

1. Decide how many frames to run. At 60 ticks per second, 240 frames is four seconds.
2. Schedule inputs as key transitions. A key stays held until you release it.
3. Run the command and read the report.

```bash
# Hold D for the first second of a four-second run.
feather-cli --workspace ./my-game --project project.feather.json command game.simulate --json '{
  "frames": 240,
  "inputs": [
    {"frame": 0, "key": "D", "pressed": true},
    {"frame": 60, "key": "D", "pressed": false}
  ]
}'
```

Headless, the command returns `{report}` directly. In a live editor it starts a background job and
returns a `job_id`; poll `job.inspect` until `status` is `completed`, and read the report from its
`output`.

## Arguments

| Field | Description |
| --- | --- |
| `frames` | Ticks to run, 1 to 600. Default 240 |
| `inputs` | Up to 256 `{frame, key, pressed}` transitions. `frame` is zero-based and must be less than `frames` |
| `blasts` | Up to 64 explosions `{frame, position, radius, damage}`; needs destruction enabled |
| `weather` | Weather events `{frame, action, weather, seconds, at}` with actions `set`, `strike`, `tornado` and `stop_tornado`; needs weather enabled |

### Keys

| Keys | Effect |
| --- | --- |
| `W`, `A`, `S`, `D` | Move the player |
| `Space` | Jump |
| `Shift`, `ShiftLeft`, `ShiftRight` | Change movement speed |
| `E`, arrow keys (`Up`, `Down`, `Left`, `Right` or `ArrowUp`…) | Reach script and Blueprint input such as `key_down` |
| `Q`, `F`, `C`, `G`, `R`, `V`, `Ctrl`, `ControlLeft` | Further gameplay actions |
| `Pad:South`, `Pad:East`, … `Pad:DPadRight` | Gamepad buttons |
| `Pad:LeftStickAxis`, `Pad:RightStickAxis` | Gamepad sticks; pass `axis: [x, y]` in −1..1, where `y = 1` pushes forward |
| `Look` | Turns the view like mouse look; pass `look: [yaw, pitch]` in degrees. Yaw 0 faces −Z; pitch is −89 to 89, negative looks down. **E** then picks up what the centre of the view is on |

The player spawns during the first tick. Schedule jump presses after the player exists and is
standing on the ground.

## Report

| Field | Description |
| --- | --- |
| `success` | `true` if the run finished without runtime errors and Stop restored the authored state |
| `frames`, `timestep_seconds`, `prewarm_frames` | The ticks run, the tick length, and the warm-up ticks used to load terrain before the run |
| `objects` | For each object: `initial` and `final` state (position, rotation, scale, velocity, visibility, physics suspension), `min_height`, `max_height` and `max_upward_velocity` |
| `player` | The player's initial and final state and its height range |
| `score` | Collectible score at the end |
| `logs` | The most recent runtime messages, up to 128 |
| `error_count`, `errors` | Total runtime errors, and up to 128 distinct messages |
| `restored` | Whether Stop restored every authored object |
| `limitations` | What this run could not verify |

Further sections appear when the project uses the matching system: `blueprints` (per-node execution
counts), `destruction`, `boat`, `vehicle`, `traversal`, `weather`, `feel` (stamina, climbing,
footsteps and the surface underfoot), `fluids`, `smoke`, `interaction` (doors, switches, pickups,
carrying and throwing) and `ai` (agent paths, blackboards, perception and events).

The interactive water surface (`water.interactive` in `project.environment`) is part of the run
too.

:::caution
A run can complete with `ok: true` while its report has `success: false`, for example when a script
throws an error. Check both.
:::

## Live editor

- Pending editor edits must be committed or discarded first; see `editor.commit`.
- A simulation can run during Play; it uses an isolated copy of the authored project.
- Only one background simulation can run at a time in an editor session.

## Limits

| Limit | Value |
| --- | --- |
| Frames per run | 1 to 600, at 1/60 second each |
| Input transitions | 256 |
| Authored objects | 2,048 |
| Work budget | 1,000,000 object × frame steps |

The runner does not render pixels, load model visuals, or play animation or audio. Imported models
use their authored colliders. Dynamic bodies far from the player may stay suspended while their
terrain is not loaded; check `physics_suspended` in the report. Fixed ticks make runs repeatable on
one machine, but results are not guaranteed to be bit-identical across platforms.

## Automate it

Use simulation as a regression check after every change. For example, confirm that a cube set to
**Dynamic** falls:

```bash
feather-cli --workspace ./my-game --project project.feather.json \
  command game.simulate --json '{"frames":120}' \
  | python3 -c 'import json,sys; o=json.load(sys.stdin)["data"]["report"]["objects"][0]; print(o["initial"]["position"][1], o["final"]["position"][1])'
```

The War City example shows a run with scheduled blasts; see [Example projects](/docs/examples/).
