Your first program
A program is one function that the game calls over and over. Here is how that works, and a first program that finds its opponent, chases it and shoots, explained a part at a time.
How a program runs
Physics runs 30 ticks / s; your program runs 10 updates / s. Each time, the game calls update(state, memory):
stateis what your vehicle knows about itself right now: where it is, which way it's facing, its health, energy and more. See the state record.memoryis yours: anything you set on it (memory.target_x = 10) is still there next update. Local variables are forgotten whenupdateends.
Your program doesn't move the vehicle itself. It gives orders, through the libraries it imports: drive.set() to move, radar.scan() to look, missiles.fire() to shoot. The game carries them out until the next update.
drive.set(), missiles.aim(), shield.set_level(). Others happen once: radar.scan() and missiles.fire(). Give a one-off order again every update you want it.The smallest program
Every program starts by importing the libraries it uses, then defines update. This one drives in a circle and nothing else:
from vehicle import drive
def update(state, memory):
drive.set(throttle=0.6, turn=0.3)throttle runs from -1 (full reverse) to 1 (full ahead), and turn from -1 (left) to 1 (right). A library can be imported only if the vehicle carries the equipment for it: drive needs a drive unit. The equipment page says which is which.
Looking: the radar
The radar sees along one bearing at a time. radar.scan(bearing) asks for a scan; the answer comes on the next update, from radar.contact(). Bearings are degrees clockwise from north (up): 0 is up, 90 is right.
To find an opponent you haven't seen yet, sweep: scan a little further round each update. The built-in wrap() keeps an angle between 0 and 360.
if memory.sweep == None:
memory.sweep = 0
memory.sweep = wrap(memory.sweep + 15)
radar.scan(memory.sweep)A field of memory that has never been set reads as None, which is how the program knows it's the first update.
Following what you've found
When a scan finds the opponent, radar.contact() returns a contact: its bearing, range, position, and seen_at, the time it was seen. A contact stays the latest answer until the next scan replaces it, so check how fresh it is. Scanning straight back at it every update keeps it in view:
contact = radar.contact()
if contact and state.time - contact.seen_at <= 0.2:
radar.scan(contact.bearing)Turning towards it
To steer onto a bearing, turn by how far off it is. angle_diff(target, current) gives the shortest turn, from -180 to 180; dividing by 45 and clamping to -1..1 turns hard when far off and gently when nearly there.
turn = clamp(angle_diff(contact.bearing, state.heading) / 45, -1, 1)
drive.set(throttle=0.8, turn=turn)Shooting
The launcher sits on a turret. missiles.aim(bearing) turns it, which takes time; missiles.fire() launches along wherever it points now. So fire once the turret has come round, and when the launcher is ready: missiles.ready() says it's loaded, has missiles, and there's the energy for a shot.
missiles.aim(contact.bearing)
on_target = abs(angle_diff(contact.bearing, missiles.turret())) <= 3
if on_target and missiles.ready():
missiles.fire()All together
from vehicle import drive, radar, missiles, debug
def update(state, memory):
if memory.sweep == None:
memory.sweep = 0
contact = radar.contact()
if contact and state.time - contact.seen_at <= 0.2:
# Found: keep the radar on it, close in, aim and fire.
radar.scan(contact.bearing)
turn = clamp(angle_diff(contact.bearing, state.heading) / 45, -1, 1)
drive.set(throttle=0.8, turn=turn)
missiles.aim(contact.bearing)
on_target = abs(angle_diff(contact.bearing, missiles.turret())) <= 3
if on_target and missiles.ready():
missiles.fire()
debug.log("Fired")
else:
# Lost, or not found yet: sweep the radar round and keep moving.
memory.sweep = wrap(memory.sweep + 15)
radar.scan(memory.sweep)
drive.set(throttle=0.5, turn=0.2)debug.log() writes to the battle's timeline, so in the replay you can see when and why your program did something. A program may log up to 4 messages an update.
Try it
- Open Battle Code Arena and go to PROGRAM.
- Paste the program into the editor. It checks as you type: the bar under the editor says OK · READY TO FIGHT or what's wrong and where.
- Press ▶ RUN to fight a training opponent, and watch the replay.
It will miss a lot against anything that moves. The next tutorial fixes that.