#chess-engine #chess #performance-testing #notation #moves #square #fen

chessie

Fast chess library, suitable for use in chess engines

6 releases (stable)

2.0.0 Nov 2, 2024
1.2.1 Oct 12, 2024
1.0.0 Sep 26, 2024
0.1.0 Sep 20, 2024

#78 in Game dev

Download history 207/week @ 2024-09-17 193/week @ 2024-09-24 16/week @ 2024-10-01 495/week @ 2024-10-08 52/week @ 2024-10-15 143/week @ 2024-10-29 26/week @ 2024-11-05

243 downloads per month

MPL-2.0 license

2MB
5K SLoC

Chessie

A fast Chess / Chess960 library, suitable for use in chess engines.

Overview

This library provides a clean, easy-to-use API for creating and working with chess games. It supports Forsyth-Edwards Notation (FEN) strings for creating positions, as well as Universal Chess Interface (UCI) notation for pieces, squares, moves, and more.

One minor goal of mine for this project was to include documentation tests for every function. As a result, nearly every function in this library has examples of how to use them that double as unit tests.

Examples

Simple performance test (perft):

use chessie::Game;

fn perft(game: &Game, depth: usize) -> u64 {
    // Recursion limit; return 1, since we're fathoming this node.
    if depth == 0 {
        return 1;
    }

    // Recursively accumulate the nodes from the remaining depths
    game.get_legal_moves().into_iter().fold(0, |nodes, mv| {
        nodes + perft(&game.with_move_made(mv), depth - 1)
    })
}

let game = Game::default(); // Default starting position
let nodes = perft(&game, 2);
assert_eq!(nodes, 400);
  • Note: This library has a perft function included.

Only generate moves from specific squares (Knights, in this case):

use chessie::{Game, Color};
let game = Game::default(); // Default starting position
let mask = game.knights(Color::White);
for mv in game.get_legal_moves_from(mask) {
    print!("{mv} ");
}
// b1a3 b1c3 g1f3 g1h3

Only generate moves that capture enemy pieces (en passant excluded):

use chessie::Game;
let game = Game::from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1").unwrap();
for mv in game.into_iter().only_captures() {
    print!("{mv} ");
}
// e2a6 g2h3 f3h3 f3f6 d5e6 e5g6 e5d7 e5f7

More examples can be found in the examples/ directory.

Features

Several state-of-the-art chess programming ideas have been incorporated into this library, as well as several Rust-specific paradigms.

Current

  • User-friendly, heavily-documented, easy-to-read, safe API.
  • (Almost) every function has examples in its documentation.
  • Ability to create, modify, and read chess positions.
  • Compact representation of primitive types such as squares (8 bits) and moves (16 bits).
  • Incremental move generation API through Rust's Iterator trait, and allow generation of moves to/from specific squares (such as only generating moves for pawns, or only generating captures).
  • Bulk move generation using some faster techniques than iterators, as well.
  • Bitboards for piece layout and move generation.
  • Magic Bitboards for sliding piece move generation.
  • And many more that I may have neglected to mention
  • Chess960 (and Double Chess960) support, with utility functions for converting to/from standard/Chess960 notation for castling rights, FEN strings, and castling moves.

Future

Acknowledgements:

Special thanks in particular to:

Changelog

  • 2.0.0:
    • Breaking:
      • Refactored CastlingRights to store a Square instead of a File.
      • Removed print_perft in favor of the following functions: perft, splitperft, perft_generic.
    • Added support for Chess960 (Fischer Random) and Double Chess960 (Double Fischer Random).
      • Internally changed the representation of Castling moves to "King takes Rook" to support Chess960.
      • Added utilities for converting to/from Chess960 and standard castling moves.
      • All Display implementations now utilize the alternate formatter (#) to print either standard or Chess960 notation for FEN strings, castling rights, and castling moves (Thanks to cozy_chess for this idea).
    • Made modules private so their re-exports don't clutter up the docs.
    • Added a Display implementation for MoveKind.
    • Improved movegen efficiency a bit.
    • Added Game::attacks_by_color to quickly access attack/defend maps.
    • Updated examples/:
      • Removed splitperft.rs, as it was redundant.
      • Changed perft.rs to use clap and be overall cleaner.
  • 1.2.1:
    • Fixed major bug causing Zobrist keys to not update properly when making moves on positions (thanks @Serdra on the EP discord).
  • 1.2.0:
    • Added Position::can_draw_by_insufficient_material.
    • Fixed bug causing Square::is_light to return the opposite bool.
  • 1.1.0:
    • Fixed bug causing illegal positions to crash move generator
    • Implemented FromIterator<Square> for Bitboard.
    • Added function for playing null moves, and adjusted how Game is printed.
  • 1.0.0:
    • Massive performance increase (over 200%) in move generation (benchmarks).
    • Game::get_legal_moves now computes legal moves in bulk rather than relying on MoveGenIter::collect.
    • Some breaking changes with method names in Bitboard and other primitives.
    • General code cleanup (more docs, more tests, etc.).
    • Added examples/ and benches.
    • Moved prng.rs into chessie-types so it can be used for magic generation, removing our dependency on rand.
    • Added #[inline(always)] to a lot of functions/methods. Seems to have improved efficiency.
    • Modified CastlingRights and it's storage in Position. Should make Chess960 integration easier later.
  • 0.1.0:
    • Initial release

Dependencies