Flow Fields Before They Had a Name: How Pizza Tycoon Routed Traffic on a 25 MHz CPU
Source: hackernews
The pizzalegacy.nl retrospective on Pizza Tycoon’s traffic engine is the kind of engineering write-up that deserves careful reading. A 1994 DOS business simulation, running on a 25 MHz Intel 486, managed to keep a city full of discrete individual vehicles moving convincingly without saturating the CPU. The technique it used would not acquire a widely-used name in game development until around 2010.
The Hardware Context
The constraints are worth setting up properly, because they are what make the solution interesting.
A 25 MHz 486 delivers roughly 25 million integer operations per second. Memory bandwidth to main RAM runs around 20 MB/s. The L1 cache is 8 KB, write-through. A cache miss costs 8 to 15 wait cycles, which on a machine this slow is a meaningful fraction of a frame budget. Integer division takes roughly 40 cycles; multiplication on early 486DX chips takes 13 to 42 cycles depending on operand size. The entire game, rendering pipeline included, runs in this budget.
Pizza Tycoon targeted 4 MB of RAM, accessed through a DOS extender, almost certainly DOS/4GW, the same extender used by Doom and a generation of DOS games. The extender provided a flat 32-bit address space and freed the game from the 640 KB conventional memory constraint. This matters because it made large flat arrays practical without segment register gymnastics.
Why Per-Vehicle Pathfinding Fails
The naive approach to traffic simulation is to give each vehicle a destination and run A* or Dijkstra’s algorithm on the road graph. For a few dozen vehicles on a city-scale network, that means hundreds to thousands of nodes explored per vehicle per navigation event. Even if you only re-path vehicles when they reach a waypoint rather than every frame, the cost accumulates quickly as the city fills.
On a 25 MHz 486, A* on a moderately complex graph with 50 vehicles navigating simultaneously could consume the entire frame budget. Add rendering, game logic, competing business AI, and sound, and the simulation visibly struggles. SimCity 2000 (1994) sidestepped this by abstracting traffic entirely, tracking density values on road tiles rather than simulating individual vehicles. Transport Tycoon (1994) worked because vehicles were sparse and traveled predictable long-distance routes with few simultaneous navigation decisions. Pizza Tycoon was doing something harder: dense urban traffic, many vehicles, discrete visible movement through city streets.
The Flow Field Solution
What the Pizza Tycoon developers built is now called a flow field, or vector field pathfinding. The city map is divided into a tile grid, and rather than computing paths on demand per vehicle, the system pre-computes a single direction value for every road tile. That direction value points toward the nearest destination or navigation target.
Construction works through reverse BFS. Mark destination tiles as the wavefront origin. Expand outward through connected road tiles in breadth-first order. At each tile reached, record which direction points back toward the nearest origin. When the BFS completes, every road tile holds a direction byte. The total construction cost is O(tiles), computed once, or recomputed incrementally when the road network changes due to player construction.
With this structure in place, a vehicle’s navigation decision costs one array lookup. It reads the direction byte from its current tile and moves that way. No graph search, no priority queue, no open and closed set management. The per-vehicle-per-tick navigation cost is O(1).
The hardware alignment here is direct. The 486’s address calculation for a 2D array lookup, base pointer plus row times width plus column, executes in a single cycle using register-plus-displacement-plus-index addressing modes. The city map, stored as a flat byte array with bit-packed fields per tile, fits in a few tens of kilobytes. A 128x128 tile grid is 16,384 bytes, comfortably inside what stays warm in the 8 KB L1 cache during a vehicle update loop. The algorithm was shaped to match the machine’s strengths.
Emergent Congestion
Flow fields solve the routing problem but do not inherently produce realistic traffic density behavior. If all vehicles blindly follow the same gradient toward the same destination, they pile onto the shortest-path roads and leave alternate routes empty.
The solution was a density counter on each road tile. When a vehicle occupies a tile, it increments a small counter. The direction-selection logic checks adjacent tile density before committing to movement; if the preferred direction leads to a congested tile, the vehicle considers alternates. Vehicles modify shared tile state, and other vehicles react to it. There is no vehicle-to-vehicle communication, no explicit congestion model. The behavior emerges from local interactions with shared state.
This is structurally identical to stigmergy in ant colony systems, where ants deposit pheromones and other ants follow shared pheromone gradients, and it closely resembles the Nagel-Schreckenberg cellular automaton model published in physics in 1992, two years before Pizza Tycoon shipped. In the Nagel-Schreckenberg model, a road is a one-dimensional grid of cells; vehicles advance if the cell ahead is empty and brake if it is occupied; traffic jams emerge from these simple local rules without any jam-specific logic encoded anywhere. Pizza Tycoon’s approach extends this principle to a 2D grid with multi-directional flow and gradient-based routing layered on top.
Intersection Arbitration
Intersections require special handling because multiple vehicles approach from different directions simultaneously. The game avoided collision detection entirely through a different mechanism: each intersection tile held a state byte encoding which approach direction currently had priority. Vehicles checking the intersection tile read this byte; if another vehicle held priority, they waited in their current tile. The priority token rotated on a timer. No swept-area geometry, no broad-phase collision test, no spatial hashing. Collisions were structurally impossible because tile occupancy was enforced as mutually exclusive by the density counter serving as a lightweight mutex.
The Rendering Connection
The direction byte had a secondary use. Vehicle sprites were pre-rotated for each of four or eight compass directions and stored at fixed memory offsets. At render time, the direction byte already determined which sprite frame to use. No trigonometry, no angle-to-index table lookup at draw time. The same value driving navigation also drove rendering, with no additional computation.
Rediscovery
The broader game development community did not widely discuss or document flow field pathfinding until around 2010, when it became prominent through Supreme Commander’s unit navigation and was described in detail at industry conferences. StarCraft II and other large-scale RTS titles with hundreds of simultaneous units adopted similar approaches. Brian Walker wrote an accessible treatment of the technique for roguelike developers as Dijkstra maps in 2010, which brought it to a wide indie audience. Academic formalization in game AI literature followed from around 2012.
Pizza Tycoon shipped in 1994.
Hardware constraints in 1994 forced developers to reason carefully about algorithmic complexity in ways that later generations, working with hardware orders of magnitude more powerful, did not have to. The technique was not secret and was almost certainly in use elsewhere in unpublished form. But it was also not documented, named, or widely taught. It existed as practical engineering knowledge held by teams who needed it, and it evaporated as hardware made the need less urgent.
What This Demonstrates
The lasting lesson from the Pizza Tycoon traffic system is a principle about algorithm selection and update frequency.
Per-vehicle pathfinding is expensive, executed many times per second, for every vehicle in the city. Pre-computed flow fields amortize the search cost across all vehicles for the lifetime of the road network. The expensive BFS computation happens once; the cheap array lookup happens thousands of times per frame. This is the same reasoning behind precomputed lightmaps, mip-map chains, and lookup tables throughout game development: pay the cost at preparation time, not at runtime.
On constrained hardware, you cannot paper over a mismatched algorithm with faster code. The 25 MHz 486 forced the developers to get the complexity class right from the start. The result ran well within its hardware budget. The same algorithm, chosen deliberately on modern hardware, handles tens of thousands of simultaneous units in real-time strategy games where per-vehicle A* would stall at a few hundred.
The Pizza Tycoon write-up is worth reading not because it reveals a secret, but because it shows the reasoning process that produced a correct solution under genuine constraint. That reasoning process does not become less relevant when hardware gets faster; it becomes easier to skip, which is a different thing.