How Much Search Does 2048 Need? An Expectimax Depth Benchmark
By Lucian — builder & engineer, LK Forge
A 2048 AI that searches only one or two moves ahead never reaches the 2048 tile — not rarely, never. We took the exact expectimax engine the game ships, capped its search at each fixed depth from 1 to 6, and self-played hundreds of games at each. The result is a sharp threshold, a plateau, and a compute bill that grows 127× for the last few plies — which is why the shipped engine varies its depth instead of always digging deeper.
· 7 min read · every number from a re-runnable headless benchmark
How we measured it
The engine under test is the one the 2048 game actually ships — an expectimax search with a corner-snake heuristic, pulled out of the production file and driven from a script with no browser and no human. Normally its depth adapts to the board: three plies when there is lots of space, up to five when the board is nearly full. For this benchmark we added one optional switch that pins the search to a single fixed depth, then self-played the game to a game-over at each depth from 1 to 6.
Depths 1–4 and the adaptive default are 300 games each. Depths 5 and 6 are 100 and 30 games, because each move there is far slower and a full run would take hours — so their percentages carry wider error bars, and we report the exact sample size on every row rather than hide it. Tile spawns use a seeded generator so the run is re-runnable; the search itself samples empty cells with the same randomness the shipped engine uses, so reach-rate is a stable distribution, not a single fixed value. Timing is mean milliseconds per move on one laptop.
Share of games reaching each tile by fixed search depth. Reach 2048 and reach 4096. The dashed amber line is the shipped adaptive engine. Depth 1–2 sit flat on zero for 2048.
The cliff at depth 3
The most striking thing in the data is not a curve, it is a wall. At depth 1 and depth 2 the solver reached the 2048 tile in zero of 300 games each — mean scores stuck around 4,400 and 4,950, the low plateau of a player that stacks tiles without a plan. Then at depth 3 it clears 2048 in 57% of games and the mean score jumps to 26,274, a six-fold leap for one extra ply.
This is the opposite of what tic-tac-toe showed, where even a one-ply search wins most games. 2048 is stochastic and its rewards are deferred: a merge to a higher tile is several moves away, so a shallow search optimises the immediate board and never sets up the chain that a high tile requires. Three plies is the point where the search can finally see a merge forming and start playing toward it.
What each extra ply costs
The gains are not free. Expectimax branches on every empty cell and every possible spawn, so each extra ply multiplies the work. A move takes 0.088 ms at depth 3, 3.07 ms at depth 5, and 11.18 ms at depth 6 — about 127× more compute across those three plies, for a reach-rate that climbed from 57% to 87% and then stopped climbing.
Mean milliseconds per move, linear scale. The bars are invisible until depth 4 for the same reason the shipped engine can afford a shallow search early: on an open board, thinking hard is cheap and pointless.
Every number
| Depth | Games | 2048 | 4096 | Mean score | Median | ms/move |
|---|---|---|---|---|---|---|
| 1 | 300 | 0% | 0% | 4,366 | 3,636 | 0.003 |
| 2 | 300 | 0% | 0% | 4,950 | 4,112 | 0.01 |
| 3 | 300 | 57.3% | 11% | 26,274 | 26,472 | 0.088 |
| 4 | 300 | 57.7% | 14.3% | 26,841 | 26,492 | 0.306 |
| 5 | 100 | 87% | 52% | 50,600 | 46,624 | 3.069 |
| 6 | 30 | 86.7% | 66.7% | 53,464 | 60,240 | 11.184 |
| adaptive | 300 | 70.3% | 29% | 34,539 | 31,868 | 0.57 |
Measured 9 September 2026 on one laptop. Depth 5 (100 games) and depth 6 (30 games) use smaller samples than the 300-game rows, so their percentages are approximate. The adaptive row is the engine the game ships with; its 70.3% reach reproduces the 69.6% from our earlier 250-game run.
Why the shipped engine varies its depth
Put the reach-rate and the cost side by side and the design falls out on its own. A constant depth-5 search is the strongest here — 87% reach 2048 — but it pays 3.07 ms on every move, including the early moves on a wide-open board where deep thinking changes nothing. A constant depth-3 search is cheap but tops out at 57%.
The shipped engine spends its compute where it matters: shallow (depth 3) while the board is empty and moves are obvious, deep (depth 5) once the board fills up and a wrong move ends the game. That buys 70.3% reach at 0.57 ms/move — well above the depth-3 baseline for a fifth of constant-depth-5’s cost. It does give up the top reach-rate a constant deep search would reach; the point of the benchmark is that the tradeoff is a deliberate, measured choice rather than a guess.
Reproduce it yourself
The engine is public: solver-core.js is the exact file the game loads, and it runs unmodified in Node. Save it, then self-play at any fixed depth — selfPlayGame(rng, depth) is the whole harness.
import { readFileSync } from 'node:fs';
import vm from 'node:vm';
// solver-core.js downloaded from lkforge.com/games/2048/
vm.runInThisContext(readFileSync('solver-core.js', 'utf8'));
let reached = 0;
for (let i = 0; i < 300; i++) {
const g = globalThis.LK2048.selfPlayGame(Math.random, 3); // fixed depth 3
if (g.maxTile >= 2048) reached++;
}
console.log((100 * reached / 300).toFixed(1) + '% reached 2048'); Drop the third argument and you get the shipped adaptive engine instead. The solver’s heuristic and search live in the open-source 2048-ai-solver repository.
Watch it play
The 2048 solver runs live in your browser — ask it for a move, or let it autoplay and watch the depth-5 endgame it was built for.