← Academy

Room 02 / Simulation state / recovered engine notes

Why simulations need two futures.

Reading and writing the same world at the same time quietly changes the rules. The fix is two buffers and a name swap.

IntuitiveVisualExperimentFrom scratch
01 Experiment

Read A. Write B. Swap.

Run a tick. The bytes do not move back; the roles do.

A / PRESENT / READ
B / FUTURE / WRITE
02 Intuitive

Do not erase the evidence while you still need it.

You are calculating tomorrow’s city from today’s city. Halfway through, you overwrite one neighbourhood with tomorrow. The next neighbourhood now sees a mixture of today and tomorrow. Your update order has become part of the model by accident.

Keep today immutable for the whole tick. Every cell reads from A and writes its result to B. After all cells finish, B becomes the new present. On the next tick, read B and write A.

St+1=F(St)all outputs see the same St
03 Technical

The stale-readback trap.

A renderer or test that always reads buffer B works on odd ticks and shows stale state on even ticks. Fixed buffer names leak an implementation detail that changes every dispatch.

Expose a role-aware method such as getCurrentState(). The caller asks for the state most recently produced; the simulation resolves whether that is A or B.

const input = readRole === "A" ? stateA : stateB;
const output = readRole === "A" ? stateB : stateA;

dispatch(input, output);
readRole = readRole === "A" ? "B" : "A";

// The current output is now the same buffer as readRole.
return readRole === "A" ? stateA : stateB;

Reconstruction check: After an A→B tick, the role flips to B. Which buffer must the renderer display, and why?

Evidence boundary

Recovered from the simulation engine’s ping-pong-buffer implementation notes and its documented stale-readback failure mode. The visual uses a small illustrative grid, not research output.

Limits: Double buffering preserves synchronous update semantics; it does not by itself make a simulation deterministic, race-free, or numerically stable.