✨ Infographic · Bitboards

Explain Chess Engine
Magic Bitboards

Bishops and rooks slide across rays — but which squares can they reach? Magic bitboards answer in one multiply, one shift, one table lookup.

📅 June 29, 2026 ⏱ 11 min read 🏷️ Sliding Attacks · O(1)
Knights jump — their attacks fit in a fixed 64-entry table. Bishops and rooks are sliders: attack squares depend on every piece blocking each ray. Scanning the board ray-by-ray works but is slow inside search. Magic bitboards (popularized by Pradu Kannan, ~2007) compress occupancy into a tiny index for a precomputed attack table.

The problem

Sliding Attacks Need Occupancy

🐢 Ray scanning

Walk each direction until edge or blocker. Simple to code, but loops on every attack query — costly when move gen and eval call attacks millions of times per second.

⚡ Magic bitboards

Precompute all attack patterns for relevant occupancies. At runtime: mask → multiply by magic → shift → index → load attacks. Constant time per slider.

The magic lookup (runtime)
index = ((occupancy & mask[sq]) * magic[sq]) >> shift[sq]
attacks = table[sq][index]

Each square has its own mask, magic number, shift, and slice of the attack table. Built once at engine startup.

01
🎭
Blocker mask
02
✖️
× Magic
03
➡️
>> Shift
04
📇
Table index
05
♗♖
Attack bits
64Squares, each with own magic
107,648Combined rook + bishop table entries
O(1)Attack query at runtime
Queen= rookAttacks | bishopAttacks

Deep dive

Building Blocks

M
Blocker mask

For each square, a bitboard of squares that can block rays excluding the edges. Only these bits affect the attack pattern — rooks see 10–12 relevant bits per square, bishops only 5–9.

mask[sq] includes blockers, not destination Rook 1,024–4,096 configurations Bishop 32–512 configurations
Magic number

A carefully chosen 64-bit constant. When multiplied by the masked occupancy, the high bits (after shift) become a perfect hash — every distinct occupancy maps to a unique table index with no collisions.

Offline search finds magic at init Failed magic → try next candidate
// Find magic for square (startup, once) for (magic = random64(); ; magic = random64()) { if (hasCollisions(sq, magic)) continue; store(magic); break; }
T
Attack table

For each square, an array of attack bitboards — one entry per occupancy configuration. Filled by brute-force ray scanning during initialization (slow is fine here; runtime must be fast).

// Runtime — called constantly in move gen inline uint64_t rookAttacks(int sq, uint64_t occ) { occ &= rookMask[sq]; return rookTable[sq][(occ * rookMagic[sq]) >> rookShift[sq]]; }
Bishop vs rook

Separate masks, magics, shifts, and tables for bishops (diagonals) and rooks (rank + file). A queen on d4 combines both lookups — still O(1).

Bishop smaller tables on average Rook more blocker combinations
P
PEXT / BMI2 (modern CPUs)

Intel PEXT extracts masked bits into a dense index without multiply-shift magic. Used in Stockfish and many modern engines on supported hardware — same idea, different instruction.

index = pext(occ, mask) Fallback to magics on older CPUs

Visual

Rook on d4 — Occupancy → Attacks

Board occupancy (blockers on rays)

Cyan = blockers on d-file / 4th rank · Purple ♖ = rook

Rook attack bitboard (result)

Orange = squares the rook can move to or capture on

Reference

Leapers vs Sliders vs Queen

PieceAttack methodDepends on occupancy?Typical approach
PawnDirection tables + capturesYes (blockers, EP)pawnAttacks[sq][color]
KnightFixed 8 targetsNoknightAttacks[sq]
KingFixed 8 targetsNokingAttacks[sq]
BishopDiagonal raysYesMagic / PEXT table
RookRank + file raysYesMagic / PEXT table
QueenBishop ∪ rookYesbishopAttacks | rookAttacks

No magic? Fallbacks exist

Beginner engines often use fancy bitboards (carry-less multiply ray tricks) or plain occupancy scanning. Magic/PEXT is the standard for performance — but correctness matters more than method. Validate with perft after wiring attacks into move gen.

Continue the engine series

Magic bitboards feed move generation — explore the full stack on FujiBit.

Related: Move Generation · Engine Anatomy · All Blogs