Perft: Chess move-generation test
Perft
Definition
Perft (short for “performance test”) is a deterministic count of all leaf nodes reachable by playing only legal moves from a given chess position to a fixed depth (measured in plies). Formally, perft(0) = 1 for any legal position, and perft(n) is the sum, over all legal moves m in the position, of perft(n − 1) after making m. The count includes every node in the move tree (no transposition deduplication), so it measures the raw size of the legal move tree at a given depth.
How it is used in chess
Perft is a foundational tool in chess programming and engine development. Developers use it to:
- Verify the correctness of a move generator (ensuring all legal moves are generated and no illegal moves slip in).
- Detect and localize bugs in special rules such as castling, en passant, promotions, checks, checkmate, and stalemate.
- Benchmark move generation speed (nodes per second) on standardized test positions.
Because the expected perft values for many standard positions and depths are known, engines can compare their results to these “ground truth” numbers. When counts match at each depth, the move generation is very likely correct.
Computation and terminology
Perft is computed by recursively generating all legal moves to the desired depth and counting the resulting leaf nodes. Two common variants/terms you will see:
- perft(n): The total number of leaf nodes at depth n.
- divide: A breakdown that lists, for each legal root move, the perft of the resulting child position at depth n − 1. This is used to pinpoint where a mismatch begins.
Note: Perft counts nodes in the tree, not unique positions. If two different move sequences reach the same position (a transposition), they are counted separately.
Strategic and historical significance
While perft is not a strategic tool for over-the-board play, it has been crucial to the rise of strong chess engines. Accurate move generation is the bedrock of any search; perft was popularized in the 1990s among engine authors as a reliable way to validate that bedrock. Modern engines such as Stockfish still rely on perft during development to ensure that optimizations (e.g., bitboards, magic bitboards, SIMD acceleration) don’t introduce subtle legality errors.
Classic reference values (initial position)
From the standard initial position, the perft values are:
- perft(0) = 1
- perft(1) = 20
- perft(2) = 400
- perft(3) = 8902
- perft(4) = 197281
- perft(5) = 4865609
- perft(6) = 119060324
- perft(7) = 3195901860
As an immediate sanity check, notice that perft(2) = 400 because from the starting position there are 20 legal first moves, and after any such move Black has 20 legal replies: 20 × 20 = 400.
Example positions and known perft
Initial position: All pieces on their starting squares, White to move. Sample values above are widely used for regression testing.
You can explore the initial position here:
“Kiwipete” position: A famous test position that exercises castling, en passant, pins, and promotions (to some depths). FEN:
r3k2r/p1ppqpb1/bn2pnp1/2pPN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1
Known perft values:
- perft(1) = 48
- perft(2) = 2039
- perft(3) = 97862
- perft(4) = 4085603
- perft(5) = 193690690
- perft(6) = 8031647685
Visualize the Kiwipete position:
Using “divide” to debug
Suppose your engine reports perft(3) = 8900 from the initial position instead of the known 8902. To find the bug, compute a divide at depth 3: for each of White’s 20 legal moves (e.g., 1. a3, 1. a4, 1. Nf3, 1. e4, …), print the perft(2) of the resulting position. If all branches except, say, after 1. e4 match known per-branch totals, then recursively “divide” 1. e4 at depth 2, and so on, until the exact missing or extra move is exposed (often a corner case like illegal castling through check or an overlooked capture).
Common pitfalls that perft detects
- Castling: Attempting to castle out of check, through check, or with pieces that have moved; also mixing up rook destinations in long vs short castling.
- En passant: Legality when the capture would leave one’s own king in check; correct availability only immediately after a two-square pawn advance.
- Promotions: Generating all four promotions (to Q/R/B/N) on the correct ranks and handling promoted piece moves on subsequent plies.
- Checks and pins: Disallowing moves that expose your own king to attack, including discovered checks created by en passant captures.
- Stalemate and checkmate detection at leaf nodes: Ensuring zero legal moves is counted as one leaf, with no double counting of “pass” moves.
- Fifty-move and repetition: Standard perft ignores these draw rules; mixing them in will mismatch known reference values.
Interesting facts and anecdotes
- The explosive growth of perft values from the initial position (roughly branching factor ~35 on average in middlegames) gives a concrete feel for the combinatorial complexity of chess.
- “Kiwipete” became a de facto standard because it stresses many special rules in one setup, making it perfect for catching hard-to-spot generator bugs.
- Engine authors sometimes publish not just node counts but also breakdowns like captures, checks, en passant, and promotions per depth to help others cross-validate.
- Perft is also used for Chess960/Shuffle Chess, where castling rules are more intricate—another source of generator headaches that perft can illuminate.
Practical tips
- Start by validating perft on the initial position through depth 5 or 6; then add targeted positions (e.g., en passant tests, castling edge cases, promotions).
- Implement a fast “divide” command to localize mismatches quickly.
- Use bitboards or similarly efficient data structures; “bulk counting” techniques can accelerate perft without changing correctness.
- Remember that perft is a correctness test, not a strength benchmark; match the known counts first, then optimize.
Related terms
- Move generator
- Bitboard
- Search
- Zobrist hashing
- Divide