How the Tic-Tac-Toe Solver Works: Minimax Perfect Play
The Tic-Tac-Toe solver never makes a mistake — not because it is clever, but because it cheats in the only honest way available: it looks at every possible continuation, right to the end of the game, and picks a move that cannot lose. That is minimax, and tic-tac-toe is small enough that it solves the game completely. There are only 5,478 reachable positions and 255,168 possible games — a tree a browser can walk in an instant. Here is how it works, and the exact numbers behind "you can't win, you can only draw."
Minimax: Assuming a Perfect Opponent
Tic-tac-toe is adversarial, so the solver uses minimax: it plays out the game tree assuming both sides play as well as possible. Each finished position is scored simply — +1 if X has won, -1 if O has won, 0 for a draw. On its own turn a side keeps the move with the best score for it (X maximises, O minimises); backed up to the root, that gives every candidate move an exact verdict of win, draw or loss. A small tweak-preference for faster wins makes it finish you off promptly rather than dawdling in a won position.
Why it can search everything. Chess or Go have trees too vast to ever fully explore, so their engines estimate. Tic-tac-toe's entire tree is a few thousand positions — so the solver never estimates. Its answer is not "probably best," it is provably best, because it has literally seen how every line ends.
The Whole Game, Counted
Because the tree is finite, we can enumerate it exactly. Walking every legal sequence of moves from the empty board gives 255,168 distinct games — and their outcomes are lopsided in the first player's favour, because going first is a real edge:
Those totals count all games, including the many where someone blunders. The moment you assume good play, the picture collapses to the famous result.
Why You Can Only Draw
Run minimax from the empty board and the value of the game is 0 — a draw. Neither side can force a win against correct defence. We can make the guarantee sharper by enumerating every line in which the first player plays optimally against all of the second player's replies:
| First player plays perfectly | Games | Share |
|---|---|---|
| Wins | 27,456 | 88.5% |
| Draws | 3,584 | 11.5% |
| Losses | 0 | 0.0% |
31,040 games in which X plays only minimax-optimal moves, against every possible O reply. A perfect player punishes mistakes (27,456 wins) and is held to a draw by perfect defence (3,584) — but is never, in any line, beaten.
That last row is the whole point of a solver: zero losses. Play the solver's moves and the worst that can ever happen is a draw. This is exactly what the tool reports — set up any position, and it labels each best move and tells you whether the side to move wins, is held to a draw, or is already lost with perfect play.
The Benchmark: What the Solver Actually Computes
How much work is "solve tic-tac-toe completely"? We instrumented the shipped full-tree minimax (no alpha-beta pruning) and counted the positions it evaluates. From the empty board with X to move, it works through 549,946 positions — the whole game tree, every line walked to its end. The cost of committing to a single first move (opponent to reply) depends on where X plays:
| X's first move | Positions evaluated | Perfect-play result |
|---|---|---|
| Corner | 59,705 | Draw |
| Edge | 63,905 | Draw |
| Center | 55,505 | Draw |
Nodes evaluated by the full-tree minimax for each opening, opponent to reply. Every first move is drawable — none loses with perfect play. Full reproducible data (per-move node counts and outcomes) is published as the LK Forge Tic-Tac-Toe Solver Benchmark dataset on Hugging Face.
Reproduce It Yourself
The game tree is small enough that the same minimax the solver runs fits in a few lines — enumerate it and you get every number above:
const LINES = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];
const winner = b => { for (const [a,c,d] of LINES)
if (b[a] && b[a]===b[c] && b[a]===b[d]) return b[a]; return null; };
function minimax(b, turn) { // +1 X wins, -1 O wins, 0 draw
const w = winner(b); if (w) return w === 'X' ? 1 : -1;
if (b.every(x => x)) return 0; // full board -> draw
let best = turn === 'X' ? -2 : 2;
for (let i = 0; i < 9; i++) if (!b[i]) {
b[i] = turn; const v = minimax(b, turn === 'X' ? 'O' : 'X'); b[i] = '';
best = turn === 'X' ? Math.max(best, v) : Math.min(best, v);
}
return best;
}
console.log(minimax(Array(9).fill(''), 'X')); // 0 -> the game is a draw
The solver runs this same evaluation on the position you enter and highlights every move that achieves the best possible result.
Frequently Asked Questions
Is there a Tic-Tac-Toe solver that plays perfectly?
Yes — this page's Tic-Tac-Toe solver is a free in-browser tool. Set up any position, choose whose turn it is, and it highlights every optimal move and tells you whether that side wins, loses or draws with perfect play. No sign-up.
What algorithm does the Tic-Tac-Toe solver use?
Minimax over the complete game tree. Because tic-tac-toe has only 5,478 reachable positions, the solver searches every legal continuation to the end, so its verdict is exact rather than an estimate. It is not a generative-AI or LLM wrapper.
Can you always win at Tic-Tac-Toe?
No — with perfect play by both sides it is always a draw. Across the 31,040 games where the first player plays optimally there are 27,456 wins, 3,584 draws and zero losses: a perfect player can be held to a draw but never beaten.
How many possible games of Tic-Tac-Toe are there?
Exactly 255,168 distinct games — 131,184 won by the first player, 77,904 by the second, and 46,080 drawn. Distinct board positions number 5,478.
Is the Tic-Tac-Toe solver free?
Completely free, no account required. It runs client-side in your browser, so nothing you enter is uploaded.