How the Chess Solver Works: Alpha-Beta Search Explained
When you set up a position in the chess solver and ask for the best move, the board is handed to a real search engine that runs entirely in your browser. It is not Stockfish and it is not a language model — it is a compact iterative-deepening negamax search with alpha-beta pruning, the same family of algorithm behind classical chess engines. To show how it actually behaves, we lifted its exact shipped code out of the page and ran it headless: in a sharp middlegame it searches about a million positions where a brute-force look would need over twelve billion, and it locates a forced back-rank mate after visiting just 33 positions. Here is exactly how it decides.
The Core Idea: Negamax Search
Chess is an adversarial game: every move you consider is answered by an opponent doing their level best to hurt you. The right tool is minimax — you maximise your position while assuming the opponent minimises it — and the solver uses its tidy one-function form, negamax. Because a position that is good for you is exactly as bad for your opponent, one score works for both sides if you simply negate it each ply. The search builds a tree of legal replies several moves deep, scores the leaf positions, and backs the values up so that each side is assumed to pick its own best move. The move at the root with the best backed-up score is the one the solver plays.
Why negamax and not expectimax? Games against chance, like 2048, average over random outcomes with expectimax. Chess has no dice — the opponent is a deliberate adversary — so the solver assumes the strongest reply at every turn and plays the move that survives it. That is the classic textbook split: expectimax for games against nature, minimax/negamax for adversarial games like chess.
Alpha-Beta: Skipping Moves That Cannot Matter
A full-width search is impossibly wide — a typical middlegame offers around 35 legal moves, so looking six moves ahead is on the order of 35⁶ ≈ 1.8 billion positions. Alpha-beta pruning is what makes it tractable. It keeps two bounds — alpha (the best you are already guaranteed) and beta (the best the opponent will allow) — and the moment a reply proves a branch cannot beat what you have already found, it stops searching that branch entirely. Whole subtrees are never visited, because their result cannot change the decision.
Pruning only pays off if strong moves are tried first, so the solver spends effort on move ordering before it searches: winning captures first, then two "killer" moves that caused cut-offs at the same depth elsewhere, then a history table of quiet moves that have worked before. Good ordering is the difference between alpha-beta touching the square root of the tree and barely pruning at all.
Searching Smarter, Not Just Deeper
On top of plain alpha-beta, the engine carries the standard enhancements a classical chess program needs to search deep without blundering at the edge of its vision:
- Iterative deepening. Rather than jumping straight to depth 6, it searches depth 1, then 2, then 3, and so on. Each shallow pass fills the move-ordering tables that make the next, deeper pass prune far more — and it means the solver always has a complete best move ready when its time budget runs out.
- Quiescence search. Stopping the count at a fixed depth mid-capture would let the engine misjudge a position — the "horizon effect." At every leaf the search keeps going through captures only until the position is quiet, so it never stops halfway through a trade.
- Null-move pruning. If letting the opponent move twice in a row still leaves you winning, the real position is almost certainly winning too — so that branch can be cut cheaply. The engine skips this trick in check and low-material endgames where passing would be misleading.
- Check extensions. Forcing lines matter, so when a move gives check the search looks one ply deeper down that line, following tactics to their resolution instead of cutting them off.
The Evaluation: Scoring a Quiet Position
The search still has to score positions it cannot play out to checkmate. That job falls to a fast evaluation with two parts: material, the familiar pawn-1, knight/bishop-3, rook-5, queen-9 balance, and piece-square tables — a fixed bonus or penalty for each piece on each square, so a knight in the centre outscores one in the corner and pawns are rewarded for advancing. Add the two together, from the side-to-move's perspective, and every leaf gets a single number the negamax search can compare.
How Hard Does It Actually Work?
Claims about search strength are easy to write and easy to disprove, so we measured it. We ran the solver's exact engine headless on four standard positions — a game start, an open Italian middlegame, the tactically dense "Kiwipete" test position, and a rook endgame — searching each to fixed depths and recording the node count the engine reports. The clearest single number is the effective branching factor: the true branching per move if you spread the whole search evenly across its depth (nodes ^ 1/depth). The lower it is, the harder alpha-beta is pruning.
Put in terms of the raw game tree, the pruning is dramatic. A brute-force search that branched at every legal move would balloon to legal-moves⁶ positions at six plies; the solver visits a tiny sliver of that:
| Position | Legal moves | Naive (b⁶) | Nodes searched | Tree skipped |
|---|---|---|---|---|
| Opening (start) | 20 | 6.4 × 10⁷ | 58,505 | 99.91% |
| Rook endgame | 22 | 1.1 × 10⁸ | 116,454 | 99.90% |
| Open middlegame | 34 | 1.5 × 10⁹ | 423,525 | 99.97% |
| Tactical (Kiwipete) | 48 | 1.2 × 10¹⁰ | 1,007,245 | 99.99% |
Fixed depth 6, shipped engine run headless. "Naive" is the upper-bound size of an unpruned minimax that branched at the position's own legal-move count; the solver's search includes a quiescence tail beyond depth 6, which the node counts already include.
The cost of each extra ply is the other half of the story. Iterative deepening from the start position shows the familiar exponential climb — but a gentle one, because deeper passes reuse the ordering the shallow passes built:
Every number
The full iterative-deepening sweep from the start position, exactly as the shipped engine reports it:
| Depth | Nodes | Time | Eff. branching |
|---|---|---|---|
| 1 | 40 | 1 ms | 40.0 |
| 2 | 140 | 3 ms | 11.8 |
| 3 | 1,176 | 13 ms | 10.6 |
| 4 | 3,073 | 44 ms | 7.5 |
| 5 | 27,970 | 250 ms | 7.8 |
| 6 | 58,505 | 643 ms | 6.2 |
| 7 | 490,520 | 4.5 s | 6.5 |
| 8 | 1,129,703 | 13.1 s | 5.7 |
Single-threaded, no time limit, one CPU core. In the live tool the search is time-bounded instead, so it returns in well under a second at its default strength and deepens only while the clock allows.
Finding forced mate. When a forced checkmate exists, the search returns it as mate-in-N with the mating line rather than a numeric score. On a simple back-rank position — a lone rook against a king boxed in by its own pawns — it returns Ra8# after visiting just 33 positions: move ordering tries the checking rook move first, and alpha-beta discards everything else the instant the mate is found.
Reproduce It Yourself
Every number above comes from the solver's exact shipped engine, run headless — nothing is estimated. The engine is a single plain JavaScript file you can download and run with Node:
import fs from 'fs'; import vm from 'vm';
// grab the shipped engine: lkforge.com/tools/puzzles/js/chess-engine.js
const code = fs.readFileSync('chess-engine.js', 'utf8');
const ctx = {}; vm.createContext(ctx);
vm.runInContext(code +
'\nglobalThis.api = { newGame, parseFEN, legalMoves, search, moveToSAN };', ctx);
const { newGame, search } = ctx.api;
const r = search(newGame(), { maxDepth: 6 }); // search the start position
console.log(r.nodes); // 58505 — positions visited
console.log(r.depth); // 6
Swap newGame() for parseFEN('<your FEN>') to benchmark any position. The same search() is what the button in the chess solver calls — the page just draws its result.
Frequently Asked Questions
Is there a chess solver that finds the best move?
Yes — this page's chess solver is a free in-browser tool. Set up a position or paste a FEN and a real search engine reports the best move, an evaluation, the likely line and mate-in-N. No sign-up, and it runs entirely in your browser.
What algorithm does the chess solver use?
Iterative-deepening negamax with alpha-beta pruning, a quiescence search for captures, null-move pruning, check extensions, killer and history move ordering, and a material plus piece-square evaluation. It is not Stockfish and not a generative-AI or LLM wrapper.
How strong is the chess solver?
Measured headless, alpha-beta and move ordering cut an effective branching factor of roughly 6 to 10 out of the 20 to 48 legal moves a position offers — searching about a million positions at depth 6 in a sharp middlegame where a brute-force look would need over twelve billion, and finding a forced back-rank mate in 33 positions. It is tuned for instant, responsive browser analysis rather than engine-versus-engine play.
Can the chess solver find mate-in-N?
Yes — when a forced mate exists the search reports it as mate-in-N along with the mating line. On a back-rank position it returns Ra8# after visiting only 33 positions.
Is the chess solver free?
Completely free, with no account required. The engine runs client-side in your browser, so nothing you enter is uploaded.
Can I analyze my own position or paste a FEN?
Yes — drag pieces to set up any legal position, or paste a FEN string, and the solver analyzes that exact position.