Game inputs and float observation vector

This document describes what data we take from the game (TMInterface / SimStateData), how it maps to our float observation vector, and what the game provides but we do not use. It is the single reference for “are we using everything?” and for extending the observation space.

Sources

  • TMInterface 2 (socket API): we use get_simulation_state() → returns SimStateData (from package tminterface).

  • Float vector is built in trackmania_rl.float_inputs: build_float_vector() and state_dict_from_sim_state().

  • RL rollout: game_env_backend.py / game_instance_manager.py call get_simulation_state(), then state_dict_from_sim_state(), then build_float_vector().

Float vector layout (dimension and indices)

Formula (see config_loader.py):

float_input_dim = 27 + 3 * n_zone_centers_in_inputs
                 + 4 * n_prev_actions_in_inputs
                 + 4 * n_contact_material_physics_behavior_types
                 + 1

With defaults (n_zone=40, n_prev=5, n_contact=4): 184.

Order (same as in float_inputs.build_float_vector()):

Index

Count

Description

Source

0

1

Temporal: time left in mini-race (overwritten in buffer collate)

Config / rollout

1–20

20

Previous actions: [accel, brake, left, right] × 5 steps

Our action buffer (not from SimStateData)

21–52

32

gear_and_wheels: 4×is_sliding, 4×has_ground_contact, 4×damper_absorb, gearbox_state, gear, actual_rpm, counter_gearbox_state, 4×4 contact_material one-hot per wheel

sim_state.scene_mobil, sim_state.simulation_wheels

53–55

3

Angular velocity (car frame)

dyna.current_state.angular_speed × orientation

56–58

3

Velocity (car frame); 56=lateral, 58=forward

dyna.current_state.linear_speed × orientation

59–61

3

y_map (up in world, in car frame)

orientation @ [0,1,0]

62–181

120

Zone centers in car frame (40 × 3)

Map VCP + position/orientation (not raw from game)

182

1

Margin to finish (meters)

Zone math (not raw from game)

183

1

is_freewheeling

scene_mobil.is_freewheeling

Indices used elsewhere: buffer_management.py and reward shaping use e.g. 25:29 (ground contact), 56:59 (velocity), 62:65 and 65:68 (first two zone centers). If you add/remove/reorder features, update those indices and float_input_dim.

What we use from SimStateData

  • dyna.current_state: position, rotation (orientation), linear_speed, angular_speed.

  • scene_mobil: engine.gear, engine.actual_rpm, gearbox_state, is_freewheeling.

  • simulation_wheels (×4): real_time_state.is_sliding, has_ground_contact, damper_absorb, contact_material_id (mapped via contact_materials.py to physics behavior categories).

  • sim_state.race_time: used for progress and finish detection (not in the float vector).

We do not read previous_state / temp_state from dyna; we only use current_state.

What the game provides but we do NOT use

Below is what is available on SimStateData (and related structs) but is not fed into the float vector or used in rollout logic. Adding any of these would require extending FloatStateDict, state_dict_from_sim_state(), build_float_vector(), config (e.g. float_input_dim, normalization), and any code that indexes by position (e.g. buffer_management reward logic).

dyna (HmsDynaStruct)

  • previous_state, temp_state (full previous/temp physics state).

  • add_linear_speed, force, torque, inverse_inertia_tensor.

  • quat (we use rotation matrix only).

  • unknown, not_tweaked_linear_speed, owner.

scene_mobil (SceneVehicleCar)

  • input_gas, input_brake, input_steer (current inputs; we use our own prev_actions from the action buffer).

  • max_linear_speed, quality, block_flags.

  • prev_sync_vehicle_state, sync_vehicle_state, async_vehicle_state, prev_async_vehicle_state (speed_forward, speed_sideward, rpm, input_steer, input_gas, input_brake, is_turbo, gearbox_state — game-internal view; we use dyna velocity and engine instead).

  • has_any_lateral_contact, last_has_any_lateral_contact_time.

  • water_forces_applied, turning_rate, turbo_boost_factor, last_turbo_type_change_time, last_turbo_time, turbo_type, roulette_value.

  • is_sliding (car-level; we use per-wheel is_sliding), wheel_contact_absorb_counter, burnout_state.

  • current_local_speed, total_central_force_added, is_rubber_ball, saved_state.

simulation_wheels (per wheel)

  • Everything except real_time_state: steerable, surface_handler (position, rotation, etc.), offset_from_vehicle, prev_sync_wheel_state, sync_wheel_state, async_wheel_state, contact_relative_local_distance, etc.

  • real_time_state: we use is_sliding, has_ground_contact, damper_absorb, contact_material_id only; we do not use field_4, field_8, field_12, field_48, field_84, field_108, relative_rotz_axis, nb_ground_contacts, field_144, rest.

SimStateData top-level

  • version, context_mode, flags, timers.

  • cp_data (checkpoint data): we use our own zone/VCP math and do not feed raw checkpoint state into the float vector.

TMInterface API

  • get_inputs(): returns replay input script as a string (used in validation/capture scripts), not the current simulation state. We do not use it for the RL observation.

Outputs: commands we send to the car (binary vs analog)

What we use today: binary only.

We send commands via TMInterface.set_input_state(left, right, accelerate, brake) in tminterface2.py. All four arguments are booleans (packed as uint8). So in RL we only send one of the 12 discrete actions from config.inputs.actions (e.g. left+accel, right+brake, coast) — no analog steering or throttle.

What TMInterface supports in script format:

  • steeranalog. Integer in [-65536, 65536] (negative = left, positive = right, 0 = no steer). Extended range with extended_steer. Used in replay/script files (e.g. "8.43 steer 13292").

  • gas — analog in script, but TMNF does not use the value: “TMNF/TMUF do not support analog acceleration.” So gas is effectively binary in TMNF.

  • press / rel — binary (up, down, left, right).

When we convert replays to TMInterface scripts we convert analog steer to binary left/right (deadzone); we do not preserve analog in our RL action space.

Socket API we have: Our client only implements C_SET_INPUT_STATE with 4 bytes (left, right, accelerate, brake). There is no set_steer_value(int) in tminterface2.py. So for real-time control we are limited to binary. To use analog steer we would need a new socket message type (if the game plugin supports it) or per-step execute_command("steer ...") (brittle).

Summary

  • Dimension: 184 with default config; layout is fixed in float_inputs.build_float_vector() and must match float_input_dim and state normalization.

  • Used from game: current dyna state (position, orientation, linear/angular speed), mobil engine/gearbox/freewheeling, and per-wheel sliding/contact/damper/contact_material.

  • Not used: previous/temp dyna state, forces/torques, car-level lateral contact/turbo/burnout, game’s internal vehicle states (sync/async), wheel geometry/sync state, replay input string, and raw checkpoint data. Adding any of these would require extending the float vector and all dependent code (config, normalization, reward indices).