ELK GAMEVERSE
Get started

Why we replay every game on our servers

A leaderboard is only worth appearing on if nobody can write to it directly. Here is what that costs and what it buys.

The obvious way to build a game is to let the phone decide. The player finishes, the app works out the score, and it tells the server what happened. It is fast, it is simple, and the server has no opinion at all.

It also means the score is whatever the app says. And an app is a program on somebody else's computer, which they may change.

What we do instead

Every game here records the moves you made — not the outcome, the moves. When you finish, that list goes to our servers, and the server plays the same game again from the same starting position, applying the same rules to the same moves.

If it reaches your result, the session is accepted and any reward is calculated from the server's own copy. If it does not, the session is refused and nothing is credited.

The engine is the same code in both places, and it is deterministic: given the same seed and the same moves, it must produce the same board. That last requirement shapes a surprising amount of the design. Anything random has to come from a seed the server also holds. Anything that reads the clock has to read a time both sides agree on. A rule that behaves differently on a fast device than on a slow one is not a rule we can have.

What it costs

A few milliseconds per finished game, and a real constraint on how the games can be written. It also finds our own bugs, which is less comfortable than it sounds.

One example, because it is the honest kind. A harvest was being distributed by iterating over a JSON object's keys. Two copies of the same content — one loaded from a file, one from the database — came back with those keys in a different order, and the store filled up partway through. Same rules, same moves, different result. Honest players had sessions refused with a message about a settlement holding 590 loads instead of 591. Nothing in the game looked wrong; the replay simply disagreed with itself.

That bug was invisible to every unit test, because both halves passed on their own. It took the two sides being fed by different routes to expose it.

What it buys

A leaderboard nobody can write to directly, and a reward that cannot be invented. Those are the same property stated twice: what you earn is what the replay says you earned.

It also means we can be specific when something goes wrong. A refused session is not a vague accusation — it is a mismatch at a known point, and almost always a connection that dropped mid-game rather than anything the player did.