# simviewer — watch a fleet of headless sims render

`simviewer` is a **developer tool for people building swarm bots on `simcore`**. When
you train or evaluate a bot over a fleet of headless `simcore` instances (see
[`../simcore/README.md`](../simcore/README.md)), the sims are invisible — they run with
no window and no renderer. `simviewer` gives you eyes: a standalone GPU window that
renders your instances with the real game's art, driven **entirely by your harness** over
a tiny text socket. You decide what it shows and when, so you can tailor the view to
whatever you're trying to see (a whole evolving population, a single champion, a diverse
sample), and push your own status text onto the screen.

It is **not** a game and not shipped to players — it's an offline visualization tool that
sits next to your training program.

## Quick start

```sh
make simviewer-demo
```

builds the viewer + the `simcore` harness library, launches the viewer, and runs the
genetic-programming example (`../simcore/examples/evolve.c`) pointed at it — so you watch a
seaquest controller *evolve live* as a "strategy cloud." Pick a port with
`make simviewer-demo SIMVIEWER_PORT=9988`. Needs a Vulkan GPU + display, a C compiler with
OpenMP (for the demo harness), and `python3`.

## Run it yourself and point your own harness at it

```sh
cargo build --release -p simviewer -p simcore
simviewer --socket 9977 --assets <staged-assets-dir>
```

- `--socket <port>` — the loopback control port the viewer listens on (default **9977**).
- `--assets <dir>` — the runtime asset root (compiled shaders + `assets/themes`). Use a
  staged build tree, e.g. `build/<platform>/release/<game>` (what `make client` /
  `make simviewer-demo` produce). Without staged assets the renderer falls back to
  checkerboard placeholders — the viewer needs the same shaders/themes the game uses.
  **In a release bundle** the viewer ships in `swarm-trainer/` next to the playable
  `game/` folder, so point it there: `./simviewer --assets ../game --socket 9977`.

Your harness then connects out to `127.0.0.1:9977` and speaks one command per line (below).
Any language works — it's plain text over TCP (`nc`, Python `socket`, etc.). See
[`../simcore/examples/simviewer_ctl.py`](../simcore/examples/simviewer_ctl.py) for a
one-shot CLI driver and `evolve.c`'s `viewer_*` helpers for a streaming harness.

## Control protocol (harness → viewer)

One command per line, `\n`-terminated, over the loopback socket. Unknown/malformed lines
are ignored (never fatal). The socket is loopback-only and single-harness (last connection
wins).

| Command | Meaning |
|---|---|
| `overlay <n>` | **Mode 2:** show `n` instances, seeded `0..n` (seed drives each run) |
| `overlay seeds <csv>` | Mode 2: show exactly these seeds (your harness picks them) |
| `overlay off` / `off` | hide the overlay |
| `reset` | begin assembling a fresh tape batch (clears the pending set) |
| `tape <seed> <csv-masks>` | add one instance to the pending batch: a seed + the per-tick button masks it played (requires ≥1 mask) |
| `show` | commit the pending tape batch → the shown overlay (and loop it) |
| `hud <text>` | set the on-screen status text (literal `\n` → line break; empty clears) |

**Overlay vs tape.** `overlay` runs each instance from a seed-driven *random walk* (a quick
"is anything alive" view). `tape` is the real tool: you stream the *actual* inputs your bot
played (`seed` + per-tick button masks), and the viewer reconstructs the **exact** run
deterministically (fresh sim + that seed + those inputs → identical to what your `simcore`
instance did). A GP/GA harness typically does, once per generation: `reset` → a `tape` per
top individual → `show`, plus a `hud` with the generation/score. Playback is
time-normalized so each generation plays over ~10s regardless of run length; instance 0 is
drawn opaque ("champion"), the rest translucent; runs that died vanish as their tape ends.

## Reverse channel (viewer → harness)

The socket is bidirectional. When you press a key in the viewer window it sends
`input <name>\n` back to the harness, where `<name>` ∈ `space | left | right | up | down |
enter | tab | 0..9`. Your harness can react — e.g. pause, or switch what it streams. The
demo harness uses `space` to pause and the number keys to change how many instances show.
Locally the viewer also handles Up/Down (nudge playback speed), Space (pause), and Enter
(reset speed) for you.

The viewer shows a *live population* of your swarm — that's its niche. To scrub a single
recorded run frame-by-frame, use the replay/TAS editor instead (it's purpose-built for that
and has far more features); the viewer deliberately doesn't duplicate it.

## Notes

- Native-only (needs a window + Vulkan); a workspace member with its own `[[bin]]`.
- The control socket binds loopback only and is optional — if the port is busy the viewer
  still runs, just without harness control.
- Design rationale and the original (richer, partially-unimplemented) control-API sketch are
  in `docs/plans/sim-harness.md` §3.2; this README documents what actually ships.
