Why the 2048 board is a Python list

Published

I built an AlphaZero agent for 2048 that is never told what a good board looks like. The record-holding agents lean on hand-tuned features such as monotonicity, smoothness and empty cells. Mine learns from self-play alone: a residual network scores boards and suggests moves, and a Monte Carlo Tree Search plans around the random 2 or 4 that drops in after every move.

Self-play is engine-bound, so time saved in the game code is training time saved. The board is a flat 16-element Python list with a row-shift cache. I measured it against a 4×4 NumPy array, and at 16 cells NumPy’s per-call overhead dominates the actual work. A full clone, move, spawn and legal-moves cycle runs at about 480k a second on the list and 21k on NumPy. Every engine primitive came out between 20 and 51 times faster.

The GPU needed batching. Running many games at once and sending their search leaves to the network together took utilisation from a few percent to over 80%. It was the single biggest throughput win.

After 20 self-play iterations, which took about 20 hours on one workstation with a single GPU/MPS device, roughly 28% of late-stage self-play games reach 2048. Around 1 in 17 reach 4096. In the first five iterations it rarely cleared 512.

That’s nowhere near the record. TD learning with Expectimax search reaches 32,768 in over 30% of games.

Training curves and the full setup are in the case study, the technical report (PDF) and the code on GitHub.

← Blog