Quickstart

A first race

Open a map, start a race, hold the accelerator for two seconds and look at the result.

from tickgym import Client, TelemetryGroup

# TICK on this machine opens the map as a session
with Client().open_map(r"C:\Maps\Example.Map.Gbx") as session:
    # One connection is one gym. Ask for the telemetry groups you will read.
    with session.connect(TelemetryGroup.RACE | TelemetryGroup.VEHICLE) as gym:
        # The countdown is played; the first sample is at race tick -1.
        t = gym.reset(seed=1)
        # 200 ticks is two seconds. An input holds from the first tick of the step onwards.
        t = gym.step(200, accelerate=True)
        print(t.race.race_time_ms, t.vehicle.speed, t.vehicle.position)

A training loop steps a few ticks at a time and changes the inputs between steps; the next section does that.

Every step answers a Telemetry with the groups you chose as attributes: t.race, t.vehicle and so on, each a typed view. See Telemetry for every field.

Steering and the control loop

Steering is an integer from -127 (full left) to 127 (full right). An input you do not pass keeps its value, as in the game.

def policy(t):
    # Decide the steering from the telemetry: t.vehicle, t.wheels, t.lidar, ...
    # This one drives straight.
    return 0

t = gym.reset(seed=1)
while not t.finished:
    t = gym.step(3, steer=policy(t), accelerate=True)
print("finished in", t.race.race_time_ms, "ms")

t.finished is true once the race is over, whatever groups were chosen. A finished gym answers its final state and ignores inputs until a reset or a restore.

Snapshots

A snapshot saves the gym’s state on the server at the current race tick. Restoring it puts the gym back there; the next step continues from the tick after.

Restore and step in one round trip, by passing the snapshot as restore=:

t = gym.step(300, accelerate=True)
corner = gym.snapshot()

# Try the corner twice from the same state.
left = gym.step(100, steer=-60, restore=corner)
right = gym.step(100, steer=60, restore=corner)

Or restore on its own, which answers the state at the snapshot:

t = gym.restore(corner)
t = gym.step(100, steer=-60)

Inputs you do not pass keep the values they had when the snapshot was taken. A gym holds a limited number of snapshots; discard frees them, and a reset frees them all.

Exact inputs

Explicit Action records put an input at a chosen tick inside the step. The step’s window starts at gym.next_tick.

from tickgym import Action

# Steer at the window's first tick, brake five ticks later.
t = gym.step(10, [Action.steer(gym.next_tick, -40), Action.brake(gym.next_tick + 5)])

A record outside the window is refused with BadActionError, and a refused step changes nothing.

Saving and replaying a race

export gives the finish time and every input since the reset, the seed first. InputText.write saves them as TICK input text, which TICK’s editor loads.

from tickgym import InputText

record = gym.export()
InputText.write("records/best.txt", record.actions)

InputText.read loads such a file. A record plays as it is: reset, then step with the record. Its seed record starts the race with that seed, and the inputs follow at their ticks. The same record gives the same race, in this gym or any other on the map.

record = InputText.read("records/best.txt")

t = gym.reset()
# One step whose window covers the whole record: up to the last input's tick.
t = gym.step(max(action.tick for action in record) + 1, record)
print(t.race.race_tick, t.vehicle.position)

This leaves the gym right after the last input, whether or not the race is finished; step on from there as you like.

Many gyms

Each Session.connect is an independent gym. Open one per thread or process; the same seed and inputs give the same telemetry on every one of them.

When something fails

A request that fails or times out (60 seconds by default) closes the gym and raises TransportError, because whether it ran is then unknown. Open a new gym and reset; stepping the exported record. Every error pickles, so a worker’s failure crosses a process pool intact. See Errors.