Blazing-fast chess move generation for Python, powered by a C++20 bitboard engine.
One pip install. 200 million moves per second. A Python API that stays out of your way.
| Fast | Magic bitboards + zero-copy Cython bindings — roughly 50× faster than pure-Python move generators |
| Pythonic | A clean ChessPosition / Move API with type annotations, UCI notation and rich move metadata |
| Complete | Castling, en passant, promotions, check / checkmate / stalemate detection, legality checks |
| Verified | Move generation validated against the standard perft reference values up to depth 8 |
| Lean | No runtime dependencies beyond NumPy — and less than 1 MB per position |
Typical uses: engine prototyping, puzzle solvers, dataset generation for ML, move validation backends, perft research — anywhere Python convenience meets the need for raw speed.
pip install chessmgPrebuilt wheels cover Linux (x86_64, aarch64) and macOS (Intel, Apple Silicon) for Python 3.9–3.13. On other platforms pip falls back to building from source.
Or install the latest development version from source:
git clone https://github.com/osick/ChessMG.git
cd ChessMG
pip install .Building from source requires a C++20 compiler (g++/clang),
Cythonandnumpy.
from chessmg import ChessPosition
pos = ChessPosition() # standard starting position (or pass any FEN)
for move in pos.legal_moves(): # 20 legal moves, as rich Move objects
print(move.uci) # "e2e4", "g1f3", ...
pos.make_move("e2e4") # UCI strings or Move objects
pos.make_move("e7e5")
print(pos.fen) # rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2
pos.undo_move() # take it back
print(pos.turn) # Color.BLACK
print(pos.is_checkmate) # False
print(ChessPosition().perft(6)) # 119060324 — in about half a secondPromotions, castling and en passant just work:
pos = ChessPosition("8/4P1k1/8/8/8/8/8/4K3 w - - 0 1")
pos.make_move("e7e8q") # promote to queen
print(pos.fen) # 4Q3/6k1/8/8/8/8/8/4K3 b - - 0 1| Member | Description |
|---|---|
ChessPosition(fen) |
Create position from FEN — raises ValueError on invalid or illegal input |
legal_moves() |
List of legal Move objects |
make_move(move) |
Play a move (UCI string or Move); full move semantics |
undo_move() |
Undo the last move |
fen |
Current position as a complete 6-field FEN string |
turn |
Side to move (Color.WHITE / Color.BLACK) |
is_check / is_checkmate / is_stalemate / is_game_over |
Game state |
perft(depth) |
Count leaf nodes at the given depth |
copy() |
Independent copy of the position |
| Property | Description |
|---|---|
from_square / to_square |
Square indices (0–63) |
from_square_name / to_square_name |
Algebraic notation ("e2") |
uci |
UCI notation ("e2e4", "e7e8q") |
promotion |
PieceType for promotions, else None |
Move.from_uci("e2e4") |
Construct from a UCI string |
Legacy low-level API (click to expand)
For backward compatibility, the lower-level API remains available:
from chessmg import ChessMoveGenerator, perft, moves
gen = ChessMoveGenerator("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1")
arr = gen.moves() # numpy array of shape (n_moves, 3): [from, to, flags]
n = perft(fen, depth) # one-shot perft; returns 0 for illegal positionsSee chessmg/README.md for details.
Perft from the starting position, single-threaded (AMD Ryzen, g++ -O2):
| Depth | Nodes | Time | Speed |
|---|---|---|---|
| 5 | 4,865,609 | < 0.1 s | ~166M NPS |
| 6 | 119,060,324 | 0.5 s | ~218M NPS |
| 7 | 3,195,901,860 | 12.9 s | ~247M NPS |
| 8 | 84,998,978,956 | 6.7 min | ~213M NPS |
All results match the published perft reference values exactly.
How it's fast: magic bitboards for sliding pieces, pre-computed attack tables, perfect-hash lookups, cache-friendly data layout in the C++20 core — and Cython bindings that avoid copying on the way into Python.
Reproduce it yourself:
python tests/benchmark_perft.py
python tests/test_perft_start_fen.py 7 verboseChessMG/
├── chessmg/ # Python package
│ ├── position.py # High-level API (ChessPosition, Move)
│ ├── libchessmg.pyx # Cython bindings
│ └── libcmg/ # C++20 engine
│ ├── libcmg.* # Position wrapper, game states, perft
│ ├── libsurge.* # Bitboard core (based on surge)
│ └── tests/ # C++ unit tests
├── tests/ # Python test suite + benchmarks
├── examples/ # Example scripts
└── docs/ # Technical documentation
# Editable install with dev tools (pytest, black, mypy, pytest-cov)
pip install -e ".[dev]"
# Rebuild the extension in place after C++/Cython changes
python setup.py build_ext --inplace
# Run the test suite
pytest tests/
# Script-style correctness / perft tests (also run by `make test`)
python tests/test_correctness.py verbose
python tests/test_perft_start_fen.py 6 verbose
python tests/test_set_position.py verboseFurther reading:
- chessmg/README.md — module-level API documentation
- docs/ARCHITECTURE.md — engine internals: bitboards, move encoding, binding layers
MIT License — see LICENCE for details.
- surge — the C++ bitboard engine ChessMG's core is based on
ChessMG — chess move generation at engine speed, with Python comfort.