About the AIs
Three ways to teach a computer the world's oldest board game
There are three computer opponents on this site: one that searches, one that imitates, and one taught from a perfect solution of the game, which I only found out existed this July. This is how each of them works, and what building them taught me.
In the 1920s Leonard Woolley dug several game boards out of the Royal Cemetery of Ur, in what is now southern Iraq: twenty squares of shell inlay decorated with lapis lazuli and red limestone, about 4,500 years old. We would have no idea how to play them, except that Irving Finkel at the British Museum deciphered a clay tablet from 177 BCE that amounts to a rulebook, written down when the game was already ancient. His reconstruction is the game people play today. It is arguably the oldest game on Earth whose rules we actually know.
You can play it here, in your browser. I built two computer opponents for it in a burst of enthusiasm last summer, one that searches and one that imitates, and then the project went quiet for a year. This July I came back to freshen up the site, started reading around the game again, and found out it had been solved the whole time. The solve was published three months before my first commit; I read it sixteen months later. That discovery turned into a third opponent, and no, this is not the order I would have chosen.
The game, in one minute
Ur is a racing game, an ancestor of backgammon. Each player has seven pieces and wants to run them along a fixed track and bear them off the far end. Movement comes from four binary dice: tetrahedral dice with two of their four corners marked, each one effectively a coin flip. A roll is 0 to 4, and 2 comes up six times as often as 0 or 4.
The interesting part is the middle. Your track begins and ends on your own side of the board, but the central eight squares are shared with your opponent. Land on their piece there and it goes back to the start. Five squares carry a rosette: land on one and you roll again, and while you sit there you cannot be captured. Bearing off needs an exact roll.
That is the whole rule set, and it is enough to create real decisions: race or fight, spread out or stack up, take the contested central rosette or slip past it. Luck arrives every turn, but the choices compound, which is what makes it a good subject for game AI.
The first opponent: search
If you have read anything about chess engines you know minimax: I pick my best move, assuming you will pick your best reply, and so on down to some depth, where I score whatever positions I find. Dice break this. Between my move and your reply sits a roll neither of us controls, so there is no single best reply to plan against.
The fix is called expectiminimax, and if you take one idea away from this article it should be this one: wherever chance intervenes, insert a chance node whose value is the probability-weighted average of its children. My layers maximise, your layers minimise, and the dice layers just average.
Classic, the first opponent, does exactly this. It is written in Rust, compiled to WebAssembly, and searches three plies deep in your browser, with alpha-beta pruning and a transposition table (a cache of positions already scored, because the same position comes up constantly).
When the search runs out of depth it scores the position with a hand-designed evaluation function: eight weights covering advancement, captures, rosette control and so on. How much is a capture worth relative to a square of progress? I did not want to guess, so I let a genetic algorithm settle it. Fifty generations of fifty weight sets, a hundred games each, and a challenger only replaced the champion after winning more than 55% of a final thousand-game match. My favourite detail of the whole project is that evolution set the safety weight slightly negative. Given the choice, it would rather threaten than hide.
How deep is deep enough?
Depth 3 is a choice, and it is fair to ask what it costs. So I measured the ladder: each search depth against the depth-3 opponent, seat-swapped pairs on identical dice streams, using the native build on a desktop machine.
| Search depth | Games | Win rate v depth 3 | ms per move | Worst single move |
|---|---|---|---|---|
| 1 | 400 | 14.8% | 0.01 | 12 |
| 2 | 400 | 33.8% | 0.11 | 9 |
| 3 (the website) | baseline | 1.3 | ||
| 4 | 400 | 55.2% | 15 | 202 |
| 5 | 200 | 62.0% | 110 | 1,709 |
| 6 | 16 | too few games | 667 | 7,232 |
Two things stand out. Time explodes: every extra ply multiplies the work by roughly the number of legal moves times the five possible rolls, and pruning only claws some of that back. And strength creeps: each rung is worth real percentage points, but the returns shrink while the bill grows by an order of magnitude a rung.
The website runs depth 3 because of what those columns mean on a phone. Native desktop milliseconds turn into tens of browser milliseconds, the search shares a Web Worker with everything else, and a move that keeps you waiting changes how the game feels. Depth 3 answers faster than you can lift your finger off the screen. Depth 5 averages 110 ms a move natively, with the odd move near two seconds; through WebAssembly on a phone that is a visible pause on every turn and a multi-second stall on the bad ones.
The second opponent: imitation
The second opponent is a pair of small neural networks in the arrangement AlphaGo made famous: a value network (who is winning?) and a policy network (which piece would you move?). Together they are about 164,000 parameters. They were not trained on human games or with reinforcement learning. They were trained to imitate Classic. I simulated six thousand games, labelled nearly a million positions with a deeper, slower Classic search (depth 4, one rung above what the browser runs), and trained the networks to reproduce those judgements in about a millisecond.
The trick is called distillation: an expensive teacher labels the data, and a small, fast student learns to approximate it. It works, and it comes with a ceiling built in. Over the 400-game benchmark the student and the browser's depth-3 Classic are level, 51.8% to 48.2%. I would not turn that small gap into a claim that one is better. They play differently, which is the more interesting fact.
Notice what happened there, though: the student was taught by depth 4 but plays like depth 3. Distillation kept the speed and lost about a ply in translation. The obvious follow-up is to label with depth 5 or 6 instead and get a stronger opponent at the same fixed millisecond. The ladder above is why I have not rushed to do it. Depth-4 search beats depth 3 by about 5 points, and the student trained on its labels lands about 3 points below its teacher. Run the same arithmetic one rung up: a depth-5 teacher wins 62% against depth 3, labelling a million positions at 110 ms each is one overnight run, and the student would plausibly come out in the high fifties. A deeper-taught student is a real option, and it would stay just as fast to run, but it buys a rung or two on a ladder whose top had already been published. If I was going to pay for better labels, exact ones were sitting there for free.
The twist: it was already solved
In March 2025 Padraig Lamont announced that royalur.net, after seven years of on-and-off effort and a breakthrough contributed by Jeroen Olieslagers, had strongly solved the Royal Game of Ur.
Solved has a precise ladder of meanings in game theory. Ultra-weakly solved means we can prove who wins with perfect play, without knowing how. Weakly solved means perfect play is known from the starting position; that is what happened to checkers in 2007. Strongly solved is the top rung: the best move is known from every legal position, including ones perfect play would never reach. That is what royalur.net did, for the Finkel rules used here and two other common rule sets.
For the Finkel rules that means roughly 276 million reachable positions, or 138 million once you use the board's symmetry (an observation credited to a community member called Mr. Carrot, which halved the storage). You cannot crack a dice game the way Connect Four was cracked, by reasoning backwards from final positions, because captures send pieces back toward the start, so positions repeat and the game graph has cycles. The tool that works is value iteration, familiar from every reinforcement learning textbook: give every position a guessed win probability, then repeatedly recompute each guess from its successors, averaging over the dice and assuming the mover picks the best continuation, until 276 million numbers stop changing. The final numbers are not estimates. They are the answer.
After seven years of attempts, the final program solves the whole game in under five hours on a laptop.
The result is a table with the exact probability that the player to move goes on to win, for every reachable position, assuming perfect play from then on. It is 1.6 GB raw, 827 MB compressed, MIT licensed, and anyone can download it.
The announcement went up on 10 March 2025. My first commit is dated 21 June 2025. I built Classic and the ML opponent that summer, three months after the answer was published, and it never occurred to me to check whether one existed. Then the project slept for a year. When I came back this July, meaning only to tidy the site up, I finally read around the game properly, and there it was. I will admit the first ten minutes felt deflating: the cleverest parts of a hobby project, pre-empted before I had started. It took a little while to see it the other way round. Somebody had published the best training data I could possibly ask for, and it was just sitting there.
The third opponent: distilling perfection
Shipping the table and looking moves up was never an option. This site is a small offline-capable web app, and I am not asking anyone to download 827 MB for a board game. But I had already spent a summer on the alternative without realising it: distillation again, with a far better teacher.
I sampled two million positions with their exact win probabilities and trained a small value network to reproduce the number. Oracle, the third opponent, is that network: 29,057 parameters. Thirty-two inputs describe which squares each side occupies and how many pieces are waiting or finished, and the one output is the win probability.
On a hundred thousand positions held back from training, its average error is 0.34 percentage points of win probability, 95% of positions are within 0.9, and the worst error found was about six. The part I like is that it needs no policy network at all. The rules engine lists the legal moves and the network values each resulting position. The table stores the probability for the player about to move, so a successor is worth 1 − V if the turn passes to the opponent, V if you landed on a rosette and kept the turn, and 1 if the move wins on the spot. Highest number plays. There are no hand-tuned bonuses left anywhere.
Did it work?
| Plays as | Classic | ML | Oracle |
|---|---|---|---|
| Classic | n/a | 48.2% | 15.0% |
| ML | 51.8% | n/a | 20.2% |
| Oracle | 85.0% | 79.8% | n/a |
The main result is not subtle. Oracle beats Classic 85% of the time and the ML opponent 80%, while Classic against ML stays a coin flip.
A 29,057-parameter network beating a 164,000-parameter one is not about size. Both run in the same WebAssembly and see similar inputs; the difference is who the teacher was. Better labels beat a bigger network. The ladder earlier says deeper search buys single-digit points per rung. A better teacher bought thirty-five.
And Oracle still loses roughly one game in six to an opponent it dominates. That is Ur. When every turn starts with four coin flips, perfect play does not mean winning; it means never giving away win probability. Over one game the dice can do anything. Over four hundred they average out.
What I took away
- An AI is its teacher. Classic got its weights from evolution, ML got its judgement from Classic, Oracle got its values from the solved game, and each landed where its teacher could take it. Better labels are usually worth more than a bigger model.
- Distillation is the workhorse. Expensive teacher, small student, ship the student. It is the same pattern used to squeeze large language models onto phones. Here the teacher happened to be perfect, and the whole thing compressed 3,000 times over for a 0.34-point error.
- Check whether your problem is already solved. Somebody spent seven years so that I could spend a fortnight. Look for prior work before you start, and look again when you pick a project back up. In my case the answer was published three months before my first commit and I read it a year later.
- Solved did not ruin the game. You can play perfectly and still lose one game in six. And I have kept all three opponents, because the differences between them are more interesting than pretending they form a neat easy, medium and hard ladder. Watch mode lets you put any two of them against each other.
All three opponents are free to play here, in your browser, offline if you like. Oracle will probably beat you. Not always, though. After four and a half thousand years, the dice are still doing their job.
Further reading
- Lamont & Olieslagers, Solving the Royal Game of Ur: the announcement, and the technical report behind it.
- Tom Scott v Irving Finkel: the British Museum curator who reconstructed the rules teaches the game, delightfully.
- The engineering detail behind this article (architectures, training runs, promotion gates, reproducibility hashes) lives in the project docs: AI-SYSTEM.md and ORACLE-AI.md.