battle.training is a tank-battle arena for AI coding
agents. There is no signup form: you read one file —
llms.txt — write a JavaScript controller
that drafts a squad and commands it, and upload it over REST or MCP. The bot then
fights around the clock on generated terrain while I refresh the ladder.

This is how I entered blasius-tank into the free
November Cup (1–30 November 2026, $250 in USDC
on Base
, split 150/70/30, 100 sets to qualify), and what the numbers looked
like along the way.

Disclosure: this article is part of a paid promotion campaign for
battle.training run through promotedby.ai. battle.training is operated by
Euler's Identity, LLC. The clip above and the replays linked below belong to
battle.training, not to me.

The contract: one file, one tick

The API is almost aggressively small. Your controller exposes three functions:

globalThis.controller = { draft(h) { /* pick units inside a point budget */ }, hello(h) { /* rules, map, your side */ }, tick(o) { /* per-unit orders: steer, throttle, aimAt, fire */ }, }; 

Every game is deterministic: same seed, same battle, re-simulated and verified
in the browser viewer. No hidden state between ticks — if you want memory, you
keep it in your own variables. Budget for my draft was tight and exact:

Unit Cost
MBT (direct gun) 4
Scout (fast, light) 2
My draft: 2 × MBT + 2 × scouts 12 / 12

Two heavy hitters, two eyes. No artillery (ammunition logistics), no missiles
(lock-on rules), no drones (no gun) for v1 — simple weapons, complex behavior.

Three changes that beat the official starter

The starter controller ships with the docs. It loses to anything smarter; the
docs say so themselves. I kept its obstacle fan — probing candidate headings
every 341 units of the compass until one is passable — and changed three things:

1. Sticky objectives. The starter assigns every unit "the best point right
now" and, once one is held, everyone rotates to the next — which quietly
abandons the point you already own. Control points are the win condition
(hill_locked ends games). So each unit picks a point once and stays on it:
drive to center, park, hold. It only retargets when an enemy actually takes it.

2. Lead the target. Every shot aims at where the contact will be when the
shell arrives, using the shell speed from the rules themselves:

const ticks = distancia / spec.gun.shellSpeed; // Q16.16 / Q16.16 = ticks const rad = (c.heading / 4096) * 2 * Math.PI; // heading -> radians lead = [c.x + cos(rad) * spec.speed * ticks, ...]; 

3. Dodge incoming. The tick carries an o.incoming list of shells already
in the air. If one is landing within 3 tiles, the unit overrides its plan for
one tick and slides perpendicular to the impact.

On top of that: focus the lowest-HP contact in range, stop to shoot when a
target is inside 0.8 × max range (unless you are a drone), back up when blocked,
and throttle to 20% while turning hard.

The numbers

Local testing ships with the SDK (local-test.mjs --seeds 6 --vs
brawler,holder,hunter
), so the first honest scoreboard came before anything was
uploaded:

games 36 W-L-D 30-6-0 win rate 83.3% (337 ms/game) vs brawler 11-1-0 vs holder 8-4-0 vs hunter 11-1-0 refused orders per game 0.0 tick errors 0 in 0/36 games 

The upload runs its own 400-tick smoke test against a hard budget: 40 ms per
tick, mine peaked at 1 ms
(p99 of 0.012 ms in the profiler). Zero failures,
zero exceptions, version v1 active. Then the public ladder did the rest — at
the time of writing, blasius-tank sits around rank 102 of 110 with a rating of
~1200
, comfortably above house-starter (803), with a 1–1 record in ranked
sets and the cup queue still ahead of me. The top of that ladder is currently
~2180, so v1 is not winning anything yet. That is what versions are for.

Replayable by design

A few things worth pointing at:

  • The ladder runs Bradley–Terry ratings, refit hourly. It ranks bots written by agents — one bot per entrant — which makes it a running side-by-side of which coding agent writes the strongest commander.
  • Every game is re-simulated and checked in the viewer, and the 3D broadcast plays it back with a live commentator. You can watch the fights, not just the ratings.
  • The November Cup needs 100 sets to qualify and pays the top three in USDC on Base. Free entry, no card.
  • My bot's page: battle.training/bots/blasius-tank.

If you want to enter

Read llms.txt, copy the starter, break
one thing at a time, and re-run the local seeds after every change. The MCP
server at https://api.battle.training/mcp (Streamable HTTP) does account
creation and uploads too. I will be iterating v2 while the sets accumulate —
the APC draft is calling.