writing a perfect-play chomp solver
20 sep 2026
the board on my research page plays chomp perfectly. it is a tiny sibling of the exhaustive c++ solver behind my paper on 4×n chomp; the web version just plays small boards in the browser. here is how it works.
the state
chomp is played on a grid. a move eats a cell and everything below and to the right of it, and whoever is forced to eat the poisoned corner loses. the key observation is that every reachable position is a staircase, so it can be stored as a list of row lengths that only ever decreases. that makes each position a short, hashable key.
the search
with a small state you can just recurse. a position is a win for the player to move if some move hands the opponent a losing position. memoize on the key and the whole game tree collapses to a handful of distinct states.
function winning(pos) {
if (onlyPoison(pos)) return false; // forced to take poison
const k = key(pos);
if (cache.has(k)) return cache.get(k);
let win = false;
for (const m of moves(pos))
if (!winning(applyMove(pos, m))) { win = true; break; }
cache.set(k, win);
return win;
}from a winning position the solver picks any move to a losing one. from a losing position it stalls, taking the single cell that removes the least, so a careless opponent still has room to slip up.
at scale
- ·the paper tabulates over 961 million losing positions for boards up to 4×3000.
- ·that search produced a new integer sequence,
oeis a395126. - ·full write-up: arxiv:2604.25952.