Every few tenths of a second each idle courier and every unassigned order form a bipartite candidate set. For a courier at position C and an order with restaurant R and customer drop D, the dispatcher scores the pairing by a two-leg cost:
cost(courier, order) = dist(C, R) + dist(R, D)
The dispatcher is a greedy minimum-cost matcher: each tick it repeatedly picks the (courier, order) pair with the lowest cost across the whole remaining candidate set, assigns it, removes both from the pool, and repeats until either couriers or orders run out. This is the same greedy heuristic real dispatch systems (DoorDash, Uber Eats) fall back to for small batches, since the exact optimum (the Hungarian algorithm) is too slow to re-run every few seconds at city scale.
Order batching: if a courier is already en route to pick up from restaurant R and a new order also from R arrives with a drop point within the batching radius of that courier's current drop point, it is appended to the courier's existing trip instead of waiting for a fresh dispatch — cutting total courier-km at the cost of a slightly longer wait for the first customer.
- Order rate — how many new orders spawn per simulated minute across the city's restaurants.
- Couriers on shift — fleet size; too few and the queue backs up, too many and utilization drops.
- Batching radius — max drop-point distance for two same-restaurant orders to share a courier trip; 0 disables batching entirely.
- Courier utilization — the fraction of couriers currently busy (assigned) rather than idle at the depot.
Watch how raising the order rate without adding couriers grows the queue and average delivery time, while a wider batching radius trims courier-km per order at the cost of a few minutes of extra wait for early customers in a batch.
This 2D top-down view renders the exact same city, restaurants, couriers and matcher as the 3D version — drag to pan and scroll to zoom around the map instead of orbiting a camera.