Factorio’s quality mechanic rolls for higher item tiers, and Wube’s framing is that it all comes down to statistics: craft enough and the distribution does the work. Gegell, a CS grad student working in 3D computer vision, spent two years proving there is another route — read the game’s random number generator out of crafting observations, predict thousands of rolls ahead, and skip the RNG forward until a legendary item is guaranteed.

Most of the post is about what determinism actually buys you.

  • Factorio uses taus88 from Boost: three linear feedback shift registers, XORed. A former Wube developer confirmed the choice on the forums in 2014 — fastest of Boost’s generators, and randomness was never a bottleneck.
  • Wube ships a .pdb with the binary, so a decompiler yields readable RandomGenerator::getInt(). Three seed words, three independent updates, XOR on return.
  • LFSRs are linear over GF(2). Each step is a matrix multiply, and the matrix is invertible — so observed outputs can be run backwards to recover internal state.
  • Three consecutive full-width outputs are enough to solve for the state. After that, every future roll is computable.

The observation channel is the interesting constraint. You never see a full 32-bit roll from inside the game, only implied facts like “did this craft upgrade quality”:

  • Each recipe with random outputs leaks some top bits. Uranium processing has a 0.7% surprise that reveals 7 bits; repair pack recycling yields exactly one bit per item at a clean 50/50 split.
  • That makes recipe choice an information-theory problem: E[bits] = p⌊−log₂p⌋ + (1−p)⌊−log₂(1−p)⌋ — a discretised Shannon entropy, best near even odds.
  • Repair pack recycling wins at 2 bits per craft, cheap and unlocked early. 44 recycler measurement units give the 88 bits needed to reconstruct the 96-bit state.
  • The matrix algebra is precomputed in Python. Only a GF(2) matrix-vector product runs in-game, built from decider combinators and implicit wire summation.

Then the manipulation. You cannot force the generator to a value — but you can burn the calls in front of a good one. Scrap recycling eats 12 calls per craft at 0.2s base speed, gears pad the remainder, a buffer of pre-filtered good offsets drives the skipper, and a divergence detector auto-restarts the machine when predictions drift from what the assemblers actually produce.

The limitations are as useful as the technique:

  • One global generalRandomGenerator handles item creation, so any other random craft anywhere in the factory desynchronises everything.
  • The hidden callers are numerous: tile variant changes, entity name randomisation, dust particles when you walk, mining drills, spidertron legs, lightning on Fulgora.
  • Scale kills it — a factory making thousands of items per second outruns the ability to precompute the sequence.
  • Factorio 2.1 changed the usage pattern and broke the machine; at one point in the experimental branch, fish movement drew from the same generator, which would have meant deleting every fish on the map before a dedicated fishRandomGenerator appeared.

Worth reading as a case study in the gap between “random enough” and “unpredictable” — and as a reminder that a deterministic simulation with an observable side channel is a linear system waiting to be solved.