chessgen
A simple and fast chess moves generator.
This is my next and probably last rewrite of a chess moves generator into a new programming language.
Since 2014, I have implemented the same engine in:
Rust implementation is the newest and most advanced in terms of code quality and features.
Performance comparison between different language versions.
PerfT computes number of all possible moves for a given depth and is a common
way how to test performance and result of a chess generator.
Rust implementation is very comparable in performance to C++ and it surprisingly behaves faster for bigger depth of
moves.
The reason for a longer startup of a perft binary is probably an initialisation of a cache array using a loop and Mutex:
cache: Box<[Mutex<PerfTCacheEntry>]>
...
let mut cache = Vec::with_capacity(cache_size);
for _ in 0..cache_size {
cache.push(Mutex::new(PerfTCacheEntry::new()))
}
vs C++
std::atomic<CacheEntry> *cache;
...
cache = new std::atomic<CacheEntry>[cacheSize];
Bellow are perft results for a standard chessboard layout on my AMD Ryzen 7 5800H, Ubuntu 23.04.
Multithreaded, with cache:
Multithreaded, no cache:
Examples
Initializing ChessBoard
use chessgen::ChessBoard;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut board = ChessBoard::STANDARD;
board = ChessBoard::from_fen(ChessBoard::STANDARD_BOARD_FEN)?;
board = ChessBoard::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")?;
board = ChessBoard::from_string(
"
a b c d e f g h
8 r n b q k b n r 8
7 p p p p p p p p 7
6 - - - - - - - - 6
5 - - - - - - - - 5
4 - - - - - - - - 4
3 - - - - - - - - 3
2 P P P P P P P P 2
1 R N B Q K B N R 1
a b c d e f g h
",
)?;
board = ChessBoard::from_string(
"
r n b q k b n r
p p p p p p p p
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
P P P P P P P P
R N B Q K B N R
",
)?;
println!("{}", board);
Ok(())
}
Output:
Generating attacks and moves
See: ChessProgramming Pseudo Legal Move
use chessgen::{ChessBoard, Color};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let board = ChessBoard::from_string(
"
- - - - q - - k
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - B - - -
- - - Q K - - -
",
)?;
println!("Attacks:\n{}", board.attacks(Color::Black));
print!("Legal moves: ");
for m in board.legal_moves() {
print!("{} ", m)
}
println!("\n");
print!("Pseudo legal moves: ");
board.moves(&mut |m| print!("{} ", m));
println!("\n");
Ok(())
}
Output:
Applying a move
use chessgen::{ChessBoard, Move};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut board = ChessBoard::from_string(
"
- - - - q - - k
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - B - - -
- - - Q K - - -
",
)?;
board = board.validate_and_apply_move(&Move::from_string("d1d8")?)?;
println!("{}", board);
Ok(())
}
Output:
Running PerfT
use chessgen::{ChessBoard, PerfT};
use std::time::Instant;
const CACHE_SIZE: usize = 64 * 1024 * 1024;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let board = ChessBoard::STANDARD;
let depth = 5;
let start = Instant::now();
let count = PerfT::new(CACHE_SIZE).perft_n(&board, depth);
let duration = start.elapsed();
println!("perfT finished:");
println!(" FEN: {}", board.to_fen());
println!(" depth: {}", depth);
println!(" count: {}", count);
println!(" time: {:?}", duration);
Ok(())
}
Output:
Displaying chess board
You may implement custom display of the chessboard using
ChessBoard::piece_at()
use chessgen::{ChessBoard, Index};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let board = ChessBoard::STANDARD;
for i in Index::ALL_FIELDS {
if i.index > 0 && i.rank() != Index::new(i.index - 1).rank() {
println!();
}
let file = i.file();
let rank = i.rank();
let translated_i = Index::from_rank_and_file(7 - rank, file);
let output = match board.piece_at(translated_i) {
Some((color, piece)) => piece.to_char(color),
None => '-',
};
print!(" {} ", output);
}
println!();
Ok(())
}
Output: