Skip to content

The car controllers ​

2026-10-24, the controllers session of Un-POD.

Seven computer cars racing on Beltane, read from the game's memory over sixty seconds, over the track's lanes in grey

An EASY race on Beltane, read from the running game every fifth of a second for a minute (run/vm/ctlsnap.py): the seven computer cars (colours) over the track's lanes (grey). The player's car (white) stands at the right. Each of these 2,100 controller steps, run again in the original under an emulator and in the port, gives the same result.

Before the physics moves a car, WINPOD.EXE calls the car's controller. For a computer car that is FUN_0042f5f0; for the player, FUN_00423b3c. Both write one word that the physics reads, with the steering in one half and the buttons in the other, and the front wheels' angle. This session traced both, ran them in the original under Unicorn, and ported them to a new crate, pod-ai.

How a computer car drives ​

Each step the car first looks around. Every other car within 25 units gets flags: ahead, to one side or the other, behind, just ahead in the same lane, or touching. Then the car moves along its lane, a run of points the track file gives per section. It always steers at the point after the one it last passed. Then it runs a behaviour, picks a steering index from 0 to 31 by the target's angle, and decides between throttle, coast and brake from its speed and the lane's speed limit.

The behaviours were the surprise. Each opponent in the track file carries a list of rules: "when a car is ahead, overtake, 40% of the time". There are sixteen rule types: overtaking on the free side, cutting in ahead of a car beside, holding a line away from a car, ramming, pushing, braking. The rules are per difficulty. On Beltane the EASY opponents have none at all, so they only follow their lanes and draft. The MEDIUM and HARD ones have two to ten each.

Luck without a random number generator ​

The rules need chance, and the game's rand() is never called from this code. The draw is a global that adds up every step's frame time; its remainder by 100 picks the rule. The loop that walks the rules does not stop when a rule takes the draw, so a later rule can take it too. The port does both the same way. A replay that is to give the same race must therefore replay the frame times. The physics already needed them for the step size.

Checked five ways ​

  1. Random states. A script lays out a whole race in emulated memory: eight cars, their drivers, Beltane's paths and lanes, and random rules. It then runs the original on 2,000 states per routine. The port matches every neighbour list, lane point, behaviour, target, input word and car field.

  2. Races. Five races of eight computer cars, 3,000 steps each, with a crude stand-in for the physics moving the cars between steps. The drivers' state carries over in both, and 880 lane choices at the "Troncon" gates come out the same.

  3. The game itself. While another session had a race running, a reader copied the cars and drivers from the game's memory without stopping it. The original and the port ran on 300 of those snapshots and agree on all 2,100 steps.

  4. The game's own answers. Later the physics session patched a step log into the game: it writes every car and driver at each simulation step, and again right after each car's controller returns. Fed to the port, all 17,402 controller calls of a 2,486-step race match what the game itself computed. No emulator stands between the two.

  5. The rules in the game. With the display free, a MEDIUM race on Beltane: the game reads Difficulty=Normal from Ubi.ini, and its seven opponents carry four to ten rules each. Over two and a half minutes, 5,250 controller steps were read from memory, with 68 rule draws. In 8 a rule took the draw: four overtakes, a brake, a cut-in and two side moves. The rest fell back to Settle. The port makes every one of those choices, with the same clock.

One pitfall cost a detour. The game builds a square-root table at start-up, and under the emulator 40% of it came out one too high. Unicorn starts the FPU in single precision, while the game's C library starts it in extended precision. Every emulated call now begins with the game's own fninit and control word (docs/knowhow/unicorn-fpu-starts-single-precision.md).

The player's side reads DirectInput's keyboard against the key map from Ubi.ini, or a joystick of three kinds. It also has a counter-steer the player never asked for: when the car slides against the steering, the game sets the wheels itself. It matches too, on 2,000 cases.

What is next ​

The function that puts a car back on the paths after a reset (FUN_004af830) is not ported yet; the port reports the reset and leaves it to the caller. And the physics session now has the controllers' word to read: steering, throttle and brake, as the original gives them.

The details are in the car controllers.

Prose CC0 1.0, code Apache 2.0. POD: Planet of Death is © Ubi Soft; none of its files are on this site.