▚ BATTLE CODE ARENA // BCAS REFERENCE PLAY ▶

Example programs

The programs that come with the game: the STARTER, a few lessons, and the training opponents. Open one to read it; copy it into the editor to try it.

The league's house vehicles aren't here: they're there to be beaten by a better program, not by reading theirs. Each one's description, in the game's opponent list, says how it fights.

Lessons

StarterSearches in 15-degree steps, keeps moving, brings its turret round to a fresh contact and fires, and gets out of the way of incoming missiles. Needs drive, radar, missiles and a missile warning sensor.
Starter
from vehicle import drive, radar, missiles, warning, debug, hull

def update(state, memory):
    if memory.scan_angle == None:
        memory.scan_angle = 0

    # With nothing to chase, carry straight on.
    course = state.heading
    throttle = 0.6

    contact = radar.contact()

    if contact and state.time - contact.seen_at <= 0.2:
        radar.scan(contact.bearing)
        missiles.aim(contact.bearing)

        # Close in to one side of the contact, not straight at it.
        course = contact.bearing + 35
        throttle = 0.9

        # Near enough: go round it instead.
        if contact.range < 90:
            course = contact.bearing + 90

        # Fire only once the turret has come round to the contact.
        on_target = abs(angle_diff(contact.bearing, missiles.turret())) <= 3

        if on_target and missiles.ready():
            missiles.fire()
            debug.log("Shot requested")
    else:
        memory.scan_angle = (memory.scan_angle + 15) % 360
        radar.scan(memory.scan_angle)

    # A missile coming for us: get out of its way. Go across its path,
    # not away along it, turning whichever way is nearer.
    threat = warning.threat()

    if threat and threat.eta > 0.2:
        course = threat.heading + 90

        if abs(angle_diff(course, state.heading)) > 90:
            course = threat.heading - 90

        throttle = 1

    # Near a wall, turn back towards the middle of the arena.
    margin = 40
    near_x = state.x < margin or state.x > state.arena_width - margin
    near_y = state.y < margin or state.y > state.arena_height - margin

    if near_x or near_y:
        course = bearing_to(state.x, state.y, state.arena_width / 2, state.arena_height / 2)

    # Up against something: turn off to one side until it is clear.
    if state.blocked:
        course = state.heading + 90

    # Steer onto the course: the further off it is, the harder the turn.
    turn = clamp(angle_diff(course, state.heading) / 45, -1, 1)
    drive.set(throttle=throttle, turn=turn)

    # Resupply. A scan's echo can find a supply crate: keep the nearest
    # one found. Out of missiles, go to it, stop on it and reload.
    echo = radar.echo()

    if echo and echo.supply:
        nearer = memory.crate_x == None

        if not nearer:
            kx = abs(memory.crate_x - state.x)
            ky = abs(memory.crate_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.crate_x = echo.x
            memory.crate_y = echo.y

    if missiles.ammo() == 0:
        if memory.crate_x == None:
            # None found yet: sweep the radar round for one.
            radar.scan(wrap(state.time * 250))
        else:
            kx = memory.crate_x - state.x
            ky = memory.crate_y - state.y
            near = kx * kx + ky * ky

            if near < 6 * 6:
                # There: stop, and once stopped, reload.
                drive.set(throttle=0, turn=0)

                if memory.arrived == None:
                    memory.arrived = state.time

                if abs(state.speed) < 1:
                    missiles.reload()

                # Still empty after a while: someone else emptied this
                # crate. Forget it, and look for another.
                if state.time - memory.arrived > 4:
                    memory.crate_x = None
                    memory.arrived = None
            else:
                memory.arrived = None
                course = bearing_to(state.x, state.y, memory.crate_x, memory.crate_y)
                turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
                throttle = 0.7

                if near < 30 * 30:
                    throttle = 0.35

                drive.set(throttle=throttle, turn=turn)

    # Repair. A scan's echo can find a repair depot: keep the nearest
    # found. Badly hurt, go to it, stop on it and repair; then leave it
    # alone for a while, since it is empty once used.
    if echo and echo.repair:
        nearer = memory.depot_x == None

        if not nearer:
            kx = abs(memory.depot_x - state.x)
            ky = abs(memory.depot_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.depot_x = echo.x
            memory.depot_y = echo.y

    hurt = state.health < 50 and memory.depot_x != None

    if memory.skip_until != None and state.time < memory.skip_until:
        hurt = False

    # Mid-repair: stay put until it is done.
    if memory.mend_until != None and state.time < memory.mend_until:
        hurt = True

    if hurt:
        kx = memory.depot_x - state.x
        ky = memory.depot_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                hull.repair()

                if memory.mend_until == None or state.time > memory.mend_until:
                    memory.mend_until = state.time + 4.5
                    memory.skip_until = state.time + 25
        else:
            course = bearing_to(state.x, state.y, memory.depot_x, memory.depot_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)

    # Recharge. Energy comes back only at a charging point, and a scan's
    # echo can find one: keep the nearest found. Running low, go to it,
    # stop on it and charge until the tank is full.
    if echo and echo.charger:
        nearer = memory.charger_x == None

        if not nearer:
            kx = abs(memory.charger_x - state.x)
            ky = abs(memory.charger_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.charger_x = echo.x
            memory.charger_y = echo.y

    if state.energy < state.energy_max * 0.3:
        memory.charging = True
    elif state.energy > state.energy_max * 0.99:
        memory.charging = False

    # Getting low and none found yet: sweep the radar round for one.
    if memory.charger_x == None and state.energy < state.energy_max * 0.6:
        radar.scan(wrap(state.time * 250))

    if memory.charging and memory.charger_x != None:
        kx = memory.charger_x - state.x
        ky = memory.charger_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            # There (it reaches 6 m): stop, and once stopped, charge.
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                drive.charge()
        else:
            course = bearing_to(state.x, state.y, memory.charger_x, memory.charger_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)
Energy-aware hunterTurns towards contacts and lowers its shield when energy is short. Needs a shield generator too.
Energy-aware hunter
from vehicle import drive, radar, missiles, shield

def update(state, memory):
    if memory.scan_angle == None:
        memory.scan_angle = state.heading

    if state.energy > 60 and state.heat < 70:
        shield.set_level(0.5)
    else:
        shield.set_level(0)

    contact = radar.contact()

    if contact and state.time - contact.seen_at <= 0.2:
        error = angle_diff(contact.bearing, state.heading)
        turn = clamp(error / 45, -1, 1)
        throttle = 0.7

        if contact.range < 80:
            throttle = 0.2

        drive.set(throttle=throttle, turn=turn)
        radar.scan(contact.bearing)
        missiles.aim(contact.bearing)
        memory.scan_angle = contact.bearing

        on_target = abs(angle_diff(contact.bearing, missiles.turret())) <= 3

        if on_target and missiles.ready() and state.heat < 80:
            missiles.fire()
    else:
        memory.scan_angle = wrap(memory.scan_angle + 20)
        radar.scan(memory.scan_angle)
        drive.set(throttle=0.4, turn=0.35)
Scan schedulingKeeps a timetable in memory. It only drives and scans: add your own aiming and firing.
Scan scheduling
from vehicle import drive, radar

def update(state, memory):
    if memory.next_scan == None:
        memory.next_scan = 0
        memory.scan_angle = 0

    drive.set(throttle=0.5, turn=0.2)

    if state.time >= memory.next_scan:
        radar.scan(memory.scan_angle)
        memory.scan_angle = wrap(memory.scan_angle + 30)
        memory.next_scan = state.time + 0.3
Taking coverThe starter's program, plus cover: it remembers the nearest obstacle its radar has echoed off, and gets behind it when hit. Needs a missile warning sensor too, as the starter does.
Taking cover
# Taking cover: the starter's program, plus a memory of the nearest
# obstacle the radar has found. When hit, it gets behind it for a while.
from vehicle import drive, radar, missiles, warning, hull

def update(state, memory):
    if memory.scan_angle == None:
        memory.scan_angle = 0
        memory.cover = False
        memory.hide_until = -1
        memory.health = state.health

    # Every scan also says what it ran into. Keep the nearest obstacle (not
    # a wall) as cover.
    echo = radar.echo()

    if echo and not echo.wall and state.time - echo.seen_at < 0.2:
        far = 1000

        if memory.cover:
            cx = abs(memory.cover_x - state.x)
            cy = abs(memory.cover_y - state.y)
            far = max(cx, cy) + 0.4 * min(cx, cy)

        if echo.range < far:
            memory.cover = True
            memory.cover_x = echo.x
            memory.cover_y = echo.y

    # Hit: time to hide.
    if state.health < memory.health:
        memory.hide_until = state.time + 2.5

    memory.health = state.health

    # With nothing to chase, carry straight on.
    course = state.heading
    throttle = 0.6

    contact = radar.contact()

    if contact and state.time - contact.seen_at <= 0.2:
        memory.enemy_x = contact.x
        memory.enemy_y = contact.y
        radar.scan(contact.bearing)
        missiles.aim(contact.bearing)

        # Close in to one side of the contact, not straight at it.
        course = contact.bearing + 35
        throttle = 0.9

        # Near enough: go round it instead.
        if contact.range < 90:
            course = contact.bearing + 90

        # Fire only once the turret has come round to the contact.
        on_target = abs(angle_diff(contact.bearing, missiles.turret())) <= 3

        if on_target and missiles.ready():
            missiles.fire()
    else:
        memory.scan_angle = (memory.scan_angle + 15) % 360
        radar.scan(memory.scan_angle)

    # Hiding: make for the far side of the cover from the opponent.
    if memory.cover and memory.enemy_x != None and state.time < memory.hide_until:
        dx = memory.cover_x - memory.enemy_x
        dy = memory.cover_y - memory.enemy_y
        length = max(abs(dx), abs(dy)) + 0.4 * min(abs(dx), abs(dy))

        if length > 1:
            course = bearing_to(state.x, state.y, memory.cover_x + dx / length * 15, memory.cover_y + dy / length * 15)
            throttle = 1

    # A missile coming for us: get out of its way. Go across its path,
    # not away along it, turning whichever way is nearer.
    threat = warning.threat()

    if threat and threat.eta > 0.2:
        course = threat.heading + 90

        if abs(angle_diff(course, state.heading)) > 90:
            course = threat.heading - 90

        throttle = 1

    # Near a wall, turn back towards the middle of the arena.
    margin = 40
    near_x = state.x < margin or state.x > state.arena_width - margin
    near_y = state.y < margin or state.y > state.arena_height - margin

    if near_x or near_y:
        course = bearing_to(state.x, state.y, state.arena_width / 2, state.arena_height / 2)

    # Up against something: turn off to one side until it is clear.
    if state.blocked:
        course = state.heading + 90

    # Steer onto the course: the further off it is, the harder the turn.
    turn = clamp(angle_diff(course, state.heading) / 45, -1, 1)
    drive.set(throttle=throttle, turn=turn)

    # Resupply. A scan's echo can find a supply crate: keep the nearest
    # one found. Out of missiles, go to it, stop on it and reload.
    echo = radar.echo()

    if echo and echo.supply:
        nearer = memory.crate_x == None

        if not nearer:
            kx = abs(memory.crate_x - state.x)
            ky = abs(memory.crate_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.crate_x = echo.x
            memory.crate_y = echo.y

    if missiles.ammo() == 0:
        if memory.crate_x == None:
            # None found yet: sweep the radar round for one.
            radar.scan(wrap(state.time * 250))
        else:
            kx = memory.crate_x - state.x
            ky = memory.crate_y - state.y
            near = kx * kx + ky * ky

            if near < 6 * 6:
                # There: stop, and once stopped, reload.
                drive.set(throttle=0, turn=0)

                if memory.arrived == None:
                    memory.arrived = state.time

                if abs(state.speed) < 1:
                    missiles.reload()

                # Still empty after a while: someone else emptied this
                # crate. Forget it, and look for another.
                if state.time - memory.arrived > 4:
                    memory.crate_x = None
                    memory.arrived = None
            else:
                memory.arrived = None
                course = bearing_to(state.x, state.y, memory.crate_x, memory.crate_y)
                turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
                throttle = 0.7

                if near < 30 * 30:
                    throttle = 0.35

                drive.set(throttle=throttle, turn=turn)

    # Repair. A scan's echo can find a repair depot: keep the nearest
    # found. Badly hurt, go to it, stop on it and repair; then leave it
    # alone for a while, since it is empty once used.
    if echo and echo.repair:
        nearer = memory.depot_x == None

        if not nearer:
            kx = abs(memory.depot_x - state.x)
            ky = abs(memory.depot_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.depot_x = echo.x
            memory.depot_y = echo.y

    hurt = state.health < 50 and memory.depot_x != None

    if memory.skip_until != None and state.time < memory.skip_until:
        hurt = False

    # Mid-repair: stay put until it is done.
    if memory.mend_until != None and state.time < memory.mend_until:
        hurt = True

    if hurt:
        kx = memory.depot_x - state.x
        ky = memory.depot_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                hull.repair()

                if memory.mend_until == None or state.time > memory.mend_until:
                    memory.mend_until = state.time + 4.5
                    memory.skip_until = state.time + 25
        else:
            course = bearing_to(state.x, state.y, memory.depot_x, memory.depot_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)

    # Recharge. Energy comes back only at a charging point, and a scan's
    # echo can find one: keep the nearest found. Running low, go to it,
    # stop on it and charge until the tank is full.
    if echo and echo.charger:
        nearer = memory.charger_x == None

        if not nearer:
            kx = abs(memory.charger_x - state.x)
            ky = abs(memory.charger_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.charger_x = echo.x
            memory.charger_y = echo.y

    if state.energy < state.energy_max * 0.3:
        memory.charging = True
    elif state.energy > state.energy_max * 0.99:
        memory.charging = False

    # Getting low and none found yet: sweep the radar round for one.
    if memory.charger_x == None and state.energy < state.energy_max * 0.6:
        radar.scan(wrap(state.time * 250))

    if memory.charging and memory.charger_x != None:
        kx = memory.charger_x - state.x
        ky = memory.charger_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            # There (it reaches 6 m): stop, and once stopped, charge.
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                drive.charge()
        else:
            course = bearing_to(state.x, state.y, memory.charger_x, memory.charger_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)

Training opponents

SentryThe SENTRY's own program.
Sentry
# SENTRY: holds its ground, sweeps its radar and fires at what it finds.
from vehicle import drive, radar, missiles, shield, hull

def update(state, memory):
    if memory.angle == None:
        memory.angle = state.heading

    drive.set(throttle=0, turn=0)

    if state.energy > 55 and state.heat < 60:
        shield.set_level(0.5)
    else:
        shield.set_level(0)

    contact = radar.contact()

    if contact and state.time - contact.seen_at <= 0.25:
        memory.angle = contact.bearing
        radar.scan(contact.bearing)
        missiles.aim(contact.bearing)

        on_target = abs(angle_diff(contact.bearing, missiles.turret())) <= 3

        # Keep a reserve: an empty tank cuts the power to everything.
        if on_target and missiles.ready() and state.heat < 85 and state.energy > 20:
            missiles.fire()
    else:
        memory.angle = wrap(memory.angle + 28)
        radar.scan(memory.angle)

    # Resupply. A scan's echo can find a supply crate: keep the nearest
    # one found. Out of missiles, go to it, stop on it and reload.
    echo = radar.echo()

    if echo and echo.supply:
        nearer = memory.crate_x == None

        if not nearer:
            kx = abs(memory.crate_x - state.x)
            ky = abs(memory.crate_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.crate_x = echo.x
            memory.crate_y = echo.y

    if missiles.ammo() == 0:
        if memory.crate_x == None:
            # None found yet: sweep the radar round for one.
            radar.scan(wrap(state.time * 250))
        else:
            kx = memory.crate_x - state.x
            ky = memory.crate_y - state.y
            near = kx * kx + ky * ky

            if near < 6 * 6:
                # There: stop, and once stopped, reload.
                drive.set(throttle=0, turn=0)

                if memory.arrived == None:
                    memory.arrived = state.time

                if abs(state.speed) < 1:
                    missiles.reload()

                # Still empty after a while: someone else emptied this
                # crate. Forget it, and look for another.
                if state.time - memory.arrived > 4:
                    memory.crate_x = None
                    memory.arrived = None
            else:
                memory.arrived = None
                course = bearing_to(state.x, state.y, memory.crate_x, memory.crate_y)
                turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
                throttle = 0.7

                if near < 30 * 30:
                    throttle = 0.35

                drive.set(throttle=throttle, turn=turn)

    # Repair. A scan's echo can find a repair depot: keep the nearest
    # found. Badly hurt, go to it, stop on it and repair; then leave it
    # alone for a while, since it is empty once used.
    if echo and echo.repair:
        nearer = memory.depot_x == None

        if not nearer:
            kx = abs(memory.depot_x - state.x)
            ky = abs(memory.depot_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.depot_x = echo.x
            memory.depot_y = echo.y

    hurt = state.health < 50 and memory.depot_x != None

    if memory.skip_until != None and state.time < memory.skip_until:
        hurt = False

    # Mid-repair: stay put until it is done.
    if memory.mend_until != None and state.time < memory.mend_until:
        hurt = True

    if hurt:
        kx = memory.depot_x - state.x
        ky = memory.depot_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                hull.repair()

                if memory.mend_until == None or state.time > memory.mend_until:
                    memory.mend_until = state.time + 4.5
                    memory.skip_until = state.time + 25
        else:
            course = bearing_to(state.x, state.y, memory.depot_x, memory.depot_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)

    # Recharge. Energy comes back only at a charging point, and a scan's
    # echo can find one: keep the nearest found. Running low, go to it,
    # stop on it and charge until the tank is full.
    if echo and echo.charger:
        nearer = memory.charger_x == None

        if not nearer:
            kx = abs(memory.charger_x - state.x)
            ky = abs(memory.charger_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.charger_x = echo.x
            memory.charger_y = echo.y

    if state.energy < state.energy_max * 0.3:
        memory.charging = True
    elif state.energy > state.energy_max * 0.99:
        memory.charging = False

    # Getting low and none found yet: sweep the radar round for one.
    if memory.charger_x == None and state.energy < state.energy_max * 0.6:
        radar.scan(wrap(state.time * 250))

    if memory.charging and memory.charger_x != None:
        kx = memory.charger_x - state.x
        ky = memory.charger_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            # There (it reaches 6 m): stop, and once stopped, charge.
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                drive.charge()
        else:
            course = bearing_to(state.x, state.y, memory.charger_x, memory.charger_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)
HunterThe HUNTER's own program.
Hunter
# HUNTER: searches, closes in on a contact and keeps firing.
from vehicle import drive, radar, missiles, hull

def update(state, memory):
    if memory.scan == None:
        memory.scan = state.heading
        memory.unstick_until = 0
        memory.mark_t = -10

    contact = radar.contact()

    if contact and state.time - contact.seen_at <= 0.25:
        memory.scan = contact.bearing
        radar.scan(contact.bearing)

        # Lead the target: work out its speed from where it was a moment
        # ago, and aim at where it will be when a missile gets there.
        if state.time - memory.mark_t > 1.5:
            memory.mark_x = contact.x
            memory.mark_y = contact.y
            memory.mark_t = state.time
            memory.vx = 0
            memory.vy = 0
        elif state.time - memory.mark_t >= 0.5:
            passed = state.time - memory.mark_t
            memory.vx = (contact.x - memory.mark_x) / passed
            memory.vy = (contact.y - memory.mark_y) / passed
            memory.mark_x = contact.x
            memory.mark_y = contact.y
            memory.mark_t = state.time

        flight = contact.range / 140
        aim = bearing_to(state.x, state.y, contact.x + memory.vx * flight, contact.y + memory.vy * flight)
        missiles.aim(aim)

        # Spiral in rather than driving straight down a missile's path.
        course = contact.bearing + 30

        # Close enough: circle the target, always the same way round.
        if contact.range < 110:
            course = contact.bearing + 80

        # The turbo drive, the radar and the launcher together use more
        # energy than the chassis makes: ease off when it runs low. It is
        # not enough. HUNTER spends what it has, and in a long fight it
        # runs dry and loses power: that is the moment to hit it.
        throttle = 1

        if state.energy < 45:
            throttle = 0.4

        error = angle_diff(course, state.heading)
        drive.set(throttle=throttle, turn=clamp(error / 40, -1, 1))

        on_target = abs(angle_diff(aim, missiles.turret())) <= 4

        if on_target and missiles.ready() and state.heat < 80 and state.energy > 20:
            missiles.fire()
    else:
        memory.scan = wrap(memory.scan + 28)
        radar.scan(memory.scan)

        # Turn hard for a moment after bumping into something.
        if state.blocked:
            memory.unstick_until = state.time + 0.8

        if state.time < memory.unstick_until:
            drive.set(throttle=0.6, turn=1)
        else:
            drive.set(throttle=0.6, turn=0.2)

    # Resupply. A scan's echo can find a supply crate: keep the nearest
    # one found. Out of missiles, go to it, stop on it and reload.
    echo = radar.echo()

    if echo and echo.supply:
        nearer = memory.crate_x == None

        if not nearer:
            kx = abs(memory.crate_x - state.x)
            ky = abs(memory.crate_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.crate_x = echo.x
            memory.crate_y = echo.y

    if missiles.ammo() == 0:
        if memory.crate_x == None:
            # None found yet: sweep the radar round for one.
            radar.scan(wrap(state.time * 250))
        else:
            kx = memory.crate_x - state.x
            ky = memory.crate_y - state.y
            near = kx * kx + ky * ky

            if near < 6 * 6:
                # There: stop, and once stopped, reload.
                drive.set(throttle=0, turn=0)

                if memory.arrived == None:
                    memory.arrived = state.time

                if abs(state.speed) < 1:
                    missiles.reload()

                # Still empty after a while: someone else emptied this
                # crate. Forget it, and look for another.
                if state.time - memory.arrived > 4:
                    memory.crate_x = None
                    memory.arrived = None
            else:
                memory.arrived = None
                course = bearing_to(state.x, state.y, memory.crate_x, memory.crate_y)
                turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
                throttle = 0.7

                if near < 30 * 30:
                    throttle = 0.35

                drive.set(throttle=throttle, turn=turn)

    # Repair. A scan's echo can find a repair depot: keep the nearest
    # found. Badly hurt, go to it, stop on it and repair; then leave it
    # alone for a while, since it is empty once used.
    if echo and echo.repair:
        nearer = memory.depot_x == None

        if not nearer:
            kx = abs(memory.depot_x - state.x)
            ky = abs(memory.depot_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.depot_x = echo.x
            memory.depot_y = echo.y

    hurt = state.health < 50 and memory.depot_x != None

    if memory.skip_until != None and state.time < memory.skip_until:
        hurt = False

    # Mid-repair: stay put until it is done.
    if memory.mend_until != None and state.time < memory.mend_until:
        hurt = True

    if hurt:
        kx = memory.depot_x - state.x
        ky = memory.depot_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                hull.repair()

                if memory.mend_until == None or state.time > memory.mend_until:
                    memory.mend_until = state.time + 4.5
                    memory.skip_until = state.time + 25
        else:
            course = bearing_to(state.x, state.y, memory.depot_x, memory.depot_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)

    # Recharge. Energy comes back only at a charging point, and a scan's
    # echo can find one: keep the nearest found. Running low, go to it,
    # stop on it and charge until the tank is full.
    if echo and echo.charger:
        nearer = memory.charger_x == None

        if not nearer:
            kx = abs(memory.charger_x - state.x)
            ky = abs(memory.charger_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.charger_x = echo.x
            memory.charger_y = echo.y

    if state.energy < state.energy_max * 0.3:
        memory.charging = True
    elif state.energy > state.energy_max * 0.99:
        memory.charging = False

    # Getting low and none found yet: sweep the radar round for one.
    if memory.charger_x == None and state.energy < state.energy_max * 0.6:
        radar.scan(wrap(state.time * 250))

    if memory.charging and memory.charger_x != None:
        kx = memory.charger_x - state.x
        ky = memory.charger_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            # There (it reaches 6 m): stop, and once stopped, charge.
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                drive.charge()
        else:
            course = bearing_to(state.x, state.y, memory.charger_x, memory.charger_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)
EvaderThe EVADER's own program.
Evader
# EVADER: never stops moving, weaves, and turns away when it is hit.
from vehicle import drive, radar, missiles, shield, hull

def update(state, memory):
    if memory.scan == None:
        memory.scan = state.heading
        memory.weave = 1
        memory.flip_at = 1.2
        memory.health = state.health
        memory.alarm_until = 0
        memory.mark_t = -10

    # Damage taken: weave the other way and raise the shield for a while.
    if state.health < memory.health:
        memory.weave = 0 - memory.weave
        memory.alarm_until = state.time + 2

    memory.health = state.health

    if state.time >= memory.flip_at:
        memory.weave = 0 - memory.weave
        memory.flip_at = state.time + 1.2

    if state.time < memory.alarm_until and state.energy > 30 and state.heat < 75:
        shield.set_level(1)
    else:
        shield.set_level(0)

    # Near a wall, steer for the middle of the arena.
    margin = 45
    near_x = state.x < margin or state.x > state.arena_width - margin
    near_y = state.y < margin or state.y > state.arena_height - margin
    turn = memory.weave * 0.55

    if near_x or near_y or state.blocked:
        home = bearing_to(state.x, state.y, state.arena_width / 2, state.arena_height / 2)
        turn = clamp(angle_diff(home, state.heading) / 30, -1, 1)

    # Flat out, firing and shielded, it spends more than the chassis makes,
    # and an empty tank would stop it dead: ease off when it runs low.
    throttle = 1

    if state.energy < 25:
        throttle = 0.5

    drive.set(throttle=throttle, turn=turn)

    contact = radar.contact()

    if contact and state.time - contact.seen_at <= 0.25:
        memory.scan = contact.bearing
        radar.scan(contact.bearing)

        # Lead the target: work out its speed from where it was a moment
        # ago, and aim at where it will be when a missile gets there.
        if state.time - memory.mark_t > 1.5:
            memory.mark_x = contact.x
            memory.mark_y = contact.y
            memory.mark_t = state.time
            memory.vx = 0
            memory.vy = 0
        elif state.time - memory.mark_t >= 0.5:
            passed = state.time - memory.mark_t
            memory.vx = (contact.x - memory.mark_x) / passed
            memory.vy = (contact.y - memory.mark_y) / passed
            memory.mark_x = contact.x
            memory.mark_y = contact.y
            memory.mark_t = state.time

        flight = contact.range / 140
        aim = bearing_to(state.x, state.y, contact.x + memory.vx * flight, contact.y + memory.vy * flight)
        missiles.aim(aim)

        on_target = abs(angle_diff(aim, missiles.turret())) <= 3

        if on_target and missiles.ready() and state.heat < 70 and state.energy > 20:
            missiles.fire()
    else:
        memory.scan = wrap(memory.scan + 18)
        radar.scan(memory.scan)

    # Resupply. A scan's echo can find a supply crate: keep the nearest
    # one found. Out of missiles, go to it, stop on it and reload.
    echo = radar.echo()

    if echo and echo.supply:
        nearer = memory.crate_x == None

        if not nearer:
            kx = abs(memory.crate_x - state.x)
            ky = abs(memory.crate_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.crate_x = echo.x
            memory.crate_y = echo.y

    if missiles.ammo() == 0:
        if memory.crate_x == None:
            # None found yet: sweep the radar round for one.
            radar.scan(wrap(state.time * 250))
        else:
            kx = memory.crate_x - state.x
            ky = memory.crate_y - state.y
            near = kx * kx + ky * ky

            if near < 6 * 6:
                # There: stop, and once stopped, reload.
                drive.set(throttle=0, turn=0)

                if memory.arrived == None:
                    memory.arrived = state.time

                if abs(state.speed) < 1:
                    missiles.reload()

                # Still empty after a while: someone else emptied this
                # crate. Forget it, and look for another.
                if state.time - memory.arrived > 4:
                    memory.crate_x = None
                    memory.arrived = None
            else:
                memory.arrived = None
                course = bearing_to(state.x, state.y, memory.crate_x, memory.crate_y)
                turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
                throttle = 0.7

                if near < 30 * 30:
                    throttle = 0.35

                drive.set(throttle=throttle, turn=turn)

    # Repair. A scan's echo can find a repair depot: keep the nearest
    # found. Badly hurt, go to it, stop on it and repair; then leave it
    # alone for a while, since it is empty once used.
    if echo and echo.repair:
        nearer = memory.depot_x == None

        if not nearer:
            kx = abs(memory.depot_x - state.x)
            ky = abs(memory.depot_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.depot_x = echo.x
            memory.depot_y = echo.y

    hurt = state.health < 50 and memory.depot_x != None

    if memory.skip_until != None and state.time < memory.skip_until:
        hurt = False

    # Mid-repair: stay put until it is done.
    if memory.mend_until != None and state.time < memory.mend_until:
        hurt = True

    if hurt:
        kx = memory.depot_x - state.x
        ky = memory.depot_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                hull.repair()

                if memory.mend_until == None or state.time > memory.mend_until:
                    memory.mend_until = state.time + 4.5
                    memory.skip_until = state.time + 25
        else:
            course = bearing_to(state.x, state.y, memory.depot_x, memory.depot_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)

    # Recharge. Energy comes back only at a charging point, and a scan's
    # echo can find one: keep the nearest found. Running low, go to it,
    # stop on it and charge until the tank is full.
    if echo and echo.charger:
        nearer = memory.charger_x == None

        if not nearer:
            kx = abs(memory.charger_x - state.x)
            ky = abs(memory.charger_y - state.y)
            nearer = echo.range < max(kx, ky) + 0.4 * min(kx, ky)

        if nearer:
            memory.charger_x = echo.x
            memory.charger_y = echo.y

    if state.energy < state.energy_max * 0.3:
        memory.charging = True
    elif state.energy > state.energy_max * 0.99:
        memory.charging = False

    # Getting low and none found yet: sweep the radar round for one.
    if memory.charger_x == None and state.energy < state.energy_max * 0.6:
        radar.scan(wrap(state.time * 250))

    if memory.charging and memory.charger_x != None:
        kx = memory.charger_x - state.x
        ky = memory.charger_y - state.y
        near = kx * kx + ky * ky

        if near < 5 * 5:
            # There (it reaches 6 m): stop, and once stopped, charge.
            drive.set(throttle=0, turn=0)

            if abs(state.speed) < 1:
                drive.charge()
        else:
            course = bearing_to(state.x, state.y, memory.charger_x, memory.charger_y)
            turn = clamp(angle_diff(course, state.heading) / 30, -1, 1)
            throttle = 0.7

            if near < 30 * 30:
                throttle = 0.35

            drive.set(throttle=throttle, turn=turn)