Core idea
Two-Phase Move Generation
Piece by piece
How Each Piece Generates Moves
Single push, double push from starting rank, diagonal captures, en passant, and promotion (queen/rook/bishop/knight — usually four moves per promotion square).
Jump to up to 8 squares from a precomputed attack table. Remove squares occupied by friendly pieces. No blocking — simplest slider-free piece.
Ray attacks stop at first blocker. Engines use magic bitboards: index a prebuilt attack table from occupancy in O(1). Queen = bishop attacks | rook attacks.
One-step moves from king attack table plus castling if rights remain, squares empty, king not in check, and squares king crosses not attacked.
Special moves
Castling, En Passant & Promotion
King moves two squares; rook jumps to other side. Engine checks: castling right bit set, path empty, king not in check, transit squares not attacked. Common bug: forgetting attacked-square test on f1/c1.
Only on the move immediately after an adjacent pawn double-push. Capturing pawn moves diagonally to the “passed-through” square; captured pawn removed from its actual square — not the destination.
White pawn on e5 can capture en passant on d6 or f6 after black …d7–d5
Pawn reaching last rank becomes Q/R/B/N. Engines emit four moves per promotion capture or push (unless you default to queen-only). Underpromotion matters in endgame tablebase positions.
Search integration
Make / Unmake Cycle
Make updates bitboards, castling rights, en passant square, halfmove clock, side to move, and XORs the Zobrist hash. Unmake restores saved state from a move struct — no board copying. This runs billions of times during search.
Validation
Perft: The Move-Gen Unit Test
| Position | Depth | Nodes | What it catches |
|---|---|---|---|
| Start position | 5 | 4,865,609 | Basic push/capture/castle bugs |
| Start position | 6 | 119,060,324 | Deep interaction bugs |
| Kiwipete FEN | 4 | 4,079,606 | Castling, en passant, checks |
| Position 3 | 4 | 797,742 | En passant edge cases |
Perft algorithm (sketch)
Count leaf nodes by recursive make/unmake — no search pruning, no evaluation. If your count ≠ published number, fix move gen before writing eval or alpha-beta.
Debugging
Common Move-Gen Bugs
- Castling allowed while in check or through attacked square
- En passant discovery check (moving pawn reveals attack on king)
- Wrong pawn capture direction for black vs white
- Promotion moves missing or only queen generated
- Not clearing en passant square after unrelated move
- Castling rights not updated when rook is captured or moves
- Filtering pseudo-legal by “would capture king” instead of make/unmake check
Move encoding
Engines pack a move into 16 bits: from square (6) + to square (6) + flags (promotion piece, castling, en passant, capture). Same list format for search, perft, and UCI output (e2e4, e7e8q).
Next in the engine series
Move generation is layer 3 of the stack — see the full architecture or train tactics on FujiBit.
Related: Engine Anatomy · All Blogs · Chess Engines