How a battle works
The rules every program plays by: time, the arena, energy, ammunition, missiles and how a battle is won. The numbers come from ruleset v0.10.
The numbers
| Ruleset | v0.10 |
|---|---|
| Physics rate | 30 ticks / s |
| Program rate | 10 updates / s |
| Battle length | up to 120 s |
| Arena | 400 x 300 m |
| Missile radius | 1 m |
| Supply crates | 2 in each arena |
| Reloading | stopped within 8 m of a stocked crate, for 2 s |
| Crate restocks | after 20 s |
| Charging points | 4 in each arena |
| Charging | 80 energy / s, stopped within 6 m of one |
| Repair depots | 2 in each arena |
| Repairing | 25 % of full health over 4 s, stopped within 6 m of a stocked depot |
| Depot restocks | after 30 s |
Timing
Physics runs 30 times a second and your program 10 times a second. At each update both vehicles observe the same instant, both programs run, and then their commands are resolved together. Neither sees what the other did in the same update.
Radar is one update behind
radar.scan() asks for a scan and returns nothing. Read radar.contact() on the next update. contact.seen_at says when the target was observed; check it before acting on an old contact.
Commands
drive.set(), shield.set_level() and missiles.aim() last until you change them. radar.scan() and missiles.fire() are single requests. Repeat a command in one update and only the last counts. Scans are resolved before shots, from the same energy.
Units
Metres, seconds and degrees. Bearings are absolute and clockwise: 0 is north (up), 90 east. x grows to the right and y downwards. A positive turn is clockwise.
The turret
missiles.aim() turns the turret towards a bearing, and it takes time to get there. missiles.fire() launches along the turret as it points at that moment (missiles.turret()), so wait until it is on target. The radar is separate: it can look one way while the turret points another.
Missiles
Unguided. They leave along the turret, can miss, and stop at obstacles and walls. A vehicle's own missiles cannot hit it.
Cover
Obstacles stop missiles and radar alike. You can't see them in state, but every scan reports what it ran into: radar.echo() says how far along its bearing the scan went before an obstacle or a wall stopped it, and where. Sweep the arena, remember where the obstacles are, and you can put one between you and your opponent.
Missile warning
With a missile warning sensor fitted, warning.threat() says which of the opponent's missiles will reach you soonest: where it is, which way it is flying and how long until it arrives. It reports only missiles set to pass close by, and gives about a second's notice at most: enough to get across a missile's path at range, not close in. Move across its path, not away along it.
Energy and charging
Energy does not come back by itself. Everything spends it: driving, scanning, firing, shielding. Charging points in the arena give it back: a scan's echo reports one it passes over (echo.charger). Drive to it, stop within reach and call drive.charge(): energy flows in until the tank is full or you move off. Only one vehicle fits on a point at a time. Run dry away from one and the vehicle loses power where it stands, for good: keep a reserve to get back.
Running out of energy
Run the tank dry and the vehicle loses power: drive, shield, radar and launcher all stop until it has a reserve again, which only charging gives it (see the chassis). state.power_out says when. Leave yourself a margin: missiles.ready() only says there is enough for the shot, not what is left after it.
Ammunition and supply
A launcher carries a few missiles: missiles.ammo() says how many are left. Supply crates in the arena hold more. A scan's echo reports a stocked crate it passes over (echo.supply). Drive to it, stop within reach and call missiles.reload(): it takes a couple of seconds, you can't fire meanwhile, and moving off cancels it. Then the crate is empty until it restocks, for you and your opponent alike.
Repair
Repair depots in the arena patch up a damaged hull: a scan's echo reports a stocked one (echo.repair). Drive to it, stop within reach and call hull.repair(): a share of your full health comes back over a few seconds, and moving off stops it. Then the depot is empty for a while, for you and your opponent alike. Every vehicle has a hull, so the hull library needs nothing fitted.
Winning
Destroy the opponent to win. When time runs out the higher health wins; equal health, or both destroyed on the same tick, is a draw.
Classes
A vehicle's program has a section for each class it has reached, bronze first. Each section is a program of its own, with its own imports and its own update. At every update each runs in turn, from bronze up, and a later one's commands replace an earlier one's. Each has its own variables; they share memory. A copy of a vehicle starts in bronze: the sections above are kept, and not run.
A program's limits
Each update runs within these limits. Going over one is a fault: the update's work is thrown away and the program carries on at the next update.
| Program size | 64 KiB |
|---|---|
| Operations per update | 2000 |
| Cost of a call | 10 operations extra |
| Memory fields | 64 |
| Memory size | 16 KiB |
| Local variables | 128 |
| String length | 256 characters |
| Debug messages per update | 4 |
| Nesting | 32 levels |
Faults
If an update fails, everything it did is thrown away (memory, commands and logs), the fault goes on the timeline, and the program runs again at the next update. Settings already committed carry on.