▚ BATTLE CODE ARENA // BCAS REFERENCE PLAY ▶

Staying alive

A battle is won by the vehicle left standing, or the healthier one when time runs out. Shooting well is half of it. This tutorial is the other half: the shield, dodging, and not getting stuck.

The shield, and what it costs

A shield generator absorbs part of every hit while it's up. shield.set_level(level) sets it from 0 (off) to 1 (full), and it stays where you set it. But it draws energy all the time it's up, and energy doesn't come back by itself: only a charging point gives it back. A shield left at full drains the tank, and a vehicle with an empty tank loses power altogether.

So raise it when it's needed, and only when there's energy to spare:

A shield that pays its way
shield.set_level(0)

if contact and contact.range < 200 and state.energy > 30:
    shield.set_level(0.5)

Setting it to 0 first, then up when needed, means the last call wins: the shield is down unless something in this update raised it.

Seeing missiles coming

A missile warning sensor gives warning.threat(): the opponent's missile that will reach you soonest, if one is coming your way. Its heading is the way the missile is flying, and eta how many seconds until it arrives. See the threat record.

Two good answers to a missile:

Dodge, and brace
threat = warning.threat()

if threat and threat.eta < 1 and state.energy > 30:
    shield.set_level(1)

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

    # Whichever side is the smaller turn.
    if abs(angle_diff(course, state.heading)) > 90:
        course = threat.heading - 90

With less than a fifth of a second to go there's no time to get clear, so it doesn't try: it braces instead.

Walls and obstacles

A vehicle against a wall can't dodge. Turn back towards the middle before you get there; state.arena_width and state.arena_height say how big the arena is. When something stops you, state.blocked is True: turn away until you're clear.

Keeping clear
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)

if state.blocked:
    course = state.heading + 90

The whole program

The order matters: each part may change course, and the last one that does wins. So the most urgent (walls, then being stuck) come last.

survivor.bcas
from vehicle import drive, radar, missiles, shield, warning

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

    course = state.heading
    throttle = 0.5
    shield.set_level(0)
    contact = radar.contact()

    if contact and state.time - contact.seen_at <= 0.2:
        radar.scan(contact.bearing)
        missiles.aim(contact.bearing)
        course = contact.bearing + 35
        throttle = 1

        if contact.range < 200 and state.energy > 30:
            shield.set_level(0.5)

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

        if on_target and contact.range < 170 and missiles.ready():
            missiles.fire()
    else:
        memory.sweep = wrap(memory.sweep + 15)
        radar.scan(memory.sweep)

    # A missile coming: brace if it's about to land, else get across its path.
    threat = warning.threat()

    if threat and threat.eta < 1 and state.energy > 30:
        shield.set_level(1)

    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

    # Off the walls, and out of anything that has stopped us.
    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)

    if state.blocked:
        course = state.heading + 90

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

Going further