# Tank Arena — rules for contestants

You are building a tank for a one-on-one tournament. Your opponent is another contestant's tank; you do not know its code. The battle is shown on video: what matters is both the result and how your tank looks and behaves.

Work only inside this folder. Your tank is the `tank/` folder: when results are tallied, `bot.js`, `body.svg` and `turret.svg` are taken from it; organize everything else however you like. Do not modify the `arena/` folder: the judge uses its own copy of the engine, and edits to your copy have no effect on the battle.

## What to submit

`tank/bot.js` — an ES module:

```js
export default {
  name: 'Tank name',            // up to 20 characters, shown large on screen
  motto: 'Motto',               // up to 60 characters, for the intro screen
  stats: { armor: 3, engine: 3, gun: 2, reload: 2 },
  init(info) {},                // optional, at the start of every round
  tick(state) { return { throttle: 0, turn: 0, turretTurn: 0, fire: false }; },
};
```

`tank/body.svg` and `tank/turret.svg` — the appearance, top-down view, `viewBox="0 0 64 64"`, nose pointing right (+x). The point (32, 32) is the rotation centre of both hull and turret; the turret is drawn on top of the hull and rotates independently. On screen the tank occupies 64×64 pixels; its collision body is a circle of radius 24 around the centre. Colours and style are up to you.

The bot runs in the browser inside a Web Worker, and in the sandbox — in Node. You may not use the DOM, Node modules, the network or external packages. You may import your own files from `tank/` via relative paths, as well as `../arena/engine.js`. The module is loaded once per match, so you can keep state between rounds in module variables. The limit for one `tick` is 50 ms; if you run over, the tank repeats its previous command. If `tick` throws an error, the tank stands still that turn.

## Stats

10 points across four stats, each an integer from 0 to 5. An invalid allocation means the bot is not admitted.

| Points | armor: HP | engine: speed, turning, acceleration | gun: damage, bullet speed | reload: reload time |
|---|---|---|---|---|
| formula | 120 + 28·armor | 120 + 24·engine px/s; 1.8 + 0.3·engine rad/s; 420 + 40·engine px/s² | 24 + 3·gun; 480 + 32·gun px/s | 0.9 − 0.07·reload s |
| 0 | 120 | 120; 1.80; 420 | 24; 480 | 0.90 |
| 5 | 260 | 240; 3.30; 620 | 39; 640 | 0.55 |

Every tank's turret turns at 2.8 rad/s. Acceleration is the engine stat's (420 + 40·engine px/s², also when braking); in reverse a tank goes no faster than 60% of its top speed.

## Battle rules

- The field is 1600×900, x to the right, y down. Angles are in radians: 0 is right, π/2 is down (clockwise on screen).
- The simulation runs at 30 ticks per second (`dt = 1/30`). A round lasts up to 120 seconds.
- The turret angle is absolute: turning the hull does not rotate it.
- A bullet leaves from a point 34 px from the centre in the direction of the turret. If that point is inside a wall, the shot is lost.
- A bullet ricochets off a wall or the field edge once and disappears on the next contact. It lives for 4 seconds. After a ricochet it can also hit its own shooter.
- Bullets that collide with each other destroy each other: you can shoot down an enemy bullet.
- Repair kits: they appear at the 15th second at two symmetric points, heal 50 HP (not above the maximum), and come back 20 seconds after being picked up.
- Zone: from the 60th to the 100th second the safe circle in the centre shrinks to a radius of 170. Outside the circle you take 20 damage per second.
- A round is won by whoever destroys the opponent. If both are alive when the 120 seconds run out, the one with the higher share of remaining HP wins. Simultaneous destruction or a tie is a draw.
- Four maps (Proving Ground, Labyrinth, Fortresses, Canyon), all point-symmetric around the centre. The maps rotate in order and sides swap: every map is played from both sides. A battle is a series of rounds up to a number of wins, followed by a long series at high speed.

## What the bot sees

Every tick `tick(state)` receives complete information about the field; nothing is hidden:

```js
{
  tick, time, timeLeft, dt, side,            // side: 0 — starts on the left, 1 — on the right
  arena: { width, height, mapName, walls: [{ x, y, w, h }] },
  me:    { x, y, heading, turret, speed, vx, vy, hp, maxHp, reloadLeft, alive, stats },
  enemy: { ...the same for the opponent },
  bullets: [{ id, x, y, vx, vy, mine, bouncesLeft, damage, canHitOwner }],
  repairKits: [{ x, y, active, respawnIn }],
  zone: { x, y, radius, finalRadius, shrinkStart, shrinkEnd, damagePerSecond },
}
```

`stats` in `me` and `enemy` are already the derived values: `maxHp, maxSpeed, turnRate, accel, turretRate, damage, bulletSpeed, reloadTime`, plus the original points. `init(info)` receives `{ round, side, mapName, view }`, where `view` is the same state at the start of the round.

## Commands

`tick` returns `{ throttle, turn, turretTurn, fire }`:

- `throttle` from −1 to 1 — throttle forward/backward;
- `turn` from −1 to 1 — hull rotation (positive is clockwise on screen);
- `turretTurn` from −1 to 1 — turret rotation;
- `fire` — fire, if reloading has finished.

Out-of-range values are clamped; garbage counts as zero.

## Sandbox

```
node arena/cli.mjs                     # tank/ vs the Hunter, 8 rounds: every map from both sides
node arena/cli.mjs --vs dummy          # vs the Dummy
node arena/cli.mjs --vs tank           # mirror match against yourself
node arena/cli.mjs --rounds 16 --map Canyon --verbose
```

The sparring bots are in `arena/sparring/`. The engine is `arena/engine.js`; it is the exact rules, and you can read it and import it for your own calculations. Node.js 18+ is required.
