A chess engine and interactive GUI written in Odin, built from scratch. Move generation is fully bitboard-based, with magic bitboards for O(1) sliding piece attacks.
- Full legal move generation — castling (kingside/queenside), en passant, pawn promotion
- Magic bitboards — pre-computed 2.2 MB attack tables for rooks and bishops
- Interactive GUI — drag-and-drop pieces via Raylib
- Three game modes — Player vs Player, Player vs Bot, Bot vs Bot (cycle in-game with a key)
- FEN support — load any board position from a FEN string
- Perft testing — validates move generation against known node counts (initial position, Kiwipete, en passant)
| Language | Odin |
| Graphics | Raylib (vendored with Odin) |
| Board representation | 12 × u64 bitboards per side |
| Slider attacks | Magic bitboard lookup |
- Odin compiler (latest nightly recommended)
- Raylib ships with Odin's vendor collection — no separate install needed
odin run src -out:OdinChessOr build without running:
odin build src -out:OdinChess| Input | Action |
|---|---|
| Click & drag | Move a piece |
Tab |
Cycle game mode (PvP → PvBot → BvB) |
R |
Reset board |
OdinChess/
├── src/ # Engine + renderer (single Odin package)
│ ├── types.odin # Core structs: Move, BoardState, BitBoards
│ ├── board.odin # Board logic, FEN parser, bitboard helpers
│ ├── moves.odin # Legal move generation, check detection
│ ├── magic_runtime.odin# Magic bitboard slider attack lookup
│ ├── magic_tables.odin # Pre-computed attack tables (2.2 MB)
│ ├── LookUpTables.odin # Knight/king/pawn attack tables
│ ├── agents.odin # Pluggable agent interface (random, idle)
│ ├── render.odin # Raylib rendering, animations
│ ├── perft.odin # Perft test suite
│ └── app.odin # Entry point, window init, CLI mode
├── res/
│ ├── img/ # Piece sprite atlas
│ └── font/ # Roboto Condensed
└── libs/
└── raylibDebug/ # Debug overlay UI helper
Each piece type gets its own u64 — one bit per square. Combining masks with bitwise operations makes move generation fast and branchless.
Rook and bishop attacks are computed once at startup and stored in a lookup table indexed by a magic number hash of the occupancy mask. Any slider attack on any square with any blockers resolves in two instructions.
Pseudo-legal moves are generated first, then filtered by applying each move and checking whether the king is left in check. Full FIDE rules apply.
Any function matching the AgentProc signature can play as either side. Currently ships with a random-move agent; the interface is designed to be replaced with a search-based engine.
Verified against standard positions:
| Position | Depth | Nodes |
|---|---|---|
| Initial | 5 | 4,865,609 |
| Kiwipete | 4 | 4,085,603 |
| En passant | 5 | 193,690 |
