# Vibe Duels: enter a tank

Your bot runs on your machine, in any language. The arena runs the physics and the house bots and talks to your
bot over a WebSocket, one tick at a time. Nobody sees anybody's code. Every match is replayable from a link.

## Five minutes to a first fight

You need Node 22 or newer for the reference client (your bot can be anything; see "Other languages").

```bash
mkdir my-tank && cd my-tank
curl -O https://vibeduels.com/client/vibeduels.mjs
curl -O https://vibeduels.com/client/bot.js
node vibeduels.mjs register your_handle
node vibeduels.mjs practice --vs volley
```

That plays the starter bot (it only turns its turret and shoots) for eight rounds against Volley, the first
tier, and prints a replay link. Open it. Then make the bot better: edit `bot.js`, or hand `PROMPT.md` to your AI
of choice and let it do the work. The rules, the state your bot sees and the commands it returns are in
[ARENA.md](https://vibeduels.com/kit/ARENA.md).

`node vibeduels.mjs sandbox` downloads the offline sandbox (the exact engine plus two sparring bots) so you can
iterate without the network: `node arena/cli.mjs tank --vs hunter`.

## Commands

| Command | What |
|---|---|
| `register <handle>` | Create an account. The token is saved in `vibeduels.json` next to your bot. Keep it private. |
| `controls` | The six house bots, weakest tier first, with their stat builds. |
| `practice [--vs id] [--rounds n] [--seed s] [--parallel k]` | Unranked rounds against a control. `--parallel 4` runs four matches at once. |
| `ranked --vs id` | A ranked run: 32 rounds on seeds you have never seen. This is what the ladder scores. |
| `lobby [--room code] [--rounds n] [--seed s]` | Play another entrant, unranked. Without a room you are paired with the next person in the queue; with one, with whoever uses the same code. Never touches the ladder. |
| `me` | Your recent matches with replay links. |
| `sandbox` | Download the offline sandbox into `./arena`. |

`--bot path/to/bot.js` plays a different file; `--server http://localhost:8787` targets a local arena.

## How scoring works

A ranked run's score is its win rate (a draw is half a win) and its tie-break is the margin: your HP share minus
the control's, averaged over the run. A control is **cleared** at a 90% win rate. The ladder ranks entrants by
controls cleared, then by total margin, so once you clear a tier the fight is about winning bigger and faster.

Practice is unlimited within a CPU budget (400 rounds an hour); ranked runs are two an hour per control.

## Fair play, in one paragraph

Your tank sees exactly what a local bot sees, no more. You have 250 ms per tick instead of the sandbox's 50 ms,
because the budget covers the network; the server enforces it and a late answer repeats your previous command.
Every ranked round starts from a seeded spawn and against a seeded variant of the control, so a recorded command
tape cannot win twice. Learning from the public replays, including the controls' habits, is allowed and expected.

## Other languages

The protocol is small and documented in [PROTOCOL.md](PROTOCOL.md): open a WebSocket, send a `hello`, answer
every `tick` with an `action`. `examples/python/vibeduels.py` is a complete Python client in under a hundred
lines.
