robo_sim (faber_tweann v2.4.0)
View SourceRobo Rumble: a deterministic 2D tank-arena engine (pure, process-free).
The instrument for the Robo Rumble front (faber-ecosystem PLAN_ROBO_RUMBLE.md, phase 0). Successor substrate to flatland_sim, chosen because it differs from the exhausted pursuit-evasion grid on three axes: PARTIAL OBSERVABILITY (a radar that sweeps, so you see only what you scan), an ENERGY ECONOMY WITH A REAL DECISION (firing costs energy, hitting returns more than it cost), and a high-dimensional action space (body, gun and radar steer independently).
DETERMINISM IS THE POINT, and it is a day-one design decision rather than a property to retrofit. Every quantity here is an INTEGER. No float, no libm, no wall clock, no process, no randomness. A match is therefore a pure function of its inputs and replays bit-identically on any machine, which is what makes a result verifiable by recomputation. See the sine table note below.
UNITS Distance/velocity/energy: fixed-point, scale 256 (8 fractional bits). Angles: BINARY ANGLE, 0..255 where 256 is a full turn (1 unit = 1.40625 deg). Wrap is a band, never a rem, so negative angles cost nothing. Bullet power: TENTHS, 1..30, meaning 0.1 .. 3.0. Sine: scaled by 32768.
TURN ORDER is fixed and load-bearing (a reordering is a rules change, so it changes the engine hash and starts a new season): 1 rotate 2 accelerate and move and resolve walls 3 fire 4 advance bullets and resolve hits 5 scan 6 cool guns
Summary
Functions
-spec alive(#arena{turn :: non_neg_integer(), tanks :: list(), bullets :: list(), scans :: list()}) -> [#tank{id :: term(), x :: integer(), y :: integer(), heading :: 0..255, gun :: 0..255, radar :: 0..255, vel :: integer(), energy :: integer(), gun_heat :: integer(), dead :: boolean(), damage_dealt :: integer()}].
-spec delta(#tank{id :: term(), x :: integer(), y :: integer(), heading :: 0..255, gun :: 0..255, radar :: 0..255, vel :: integer(), energy :: integer(), gun_heat :: integer(), dead :: boolean(), damage_dealt :: integer()}, #tank{id :: term(), x :: integer(), y :: integer(), heading :: 0..255, gun :: 0..255, radar :: 0..255, vel :: integer(), energy :: integer(), gun_heat :: integer(), dead :: boolean(), damage_dealt :: integer()}) -> {integer(), integer()}.
-spec finished(#arena{turn :: non_neg_integer(), tanks :: list(), bullets :: list(), scans :: list()}) -> boolean().
-spec step(#arena{turn :: non_neg_integer(), tanks :: list(), bullets :: list(), scans :: list()}, [{term(), #intent{turn_body :: integer(), turn_gun :: integer(), turn_radar :: integer(), accel :: integer(), fire :: integer()}}]) -> #arena{turn :: non_neg_integer(), tanks :: list(), bullets :: list(), scans :: list()}.
-spec trace_hash(#arena{turn :: non_neg_integer(), tanks :: list(), bullets :: list(), scans :: list()}) -> binary().
-spec wrap(integer()) -> 0..255.