chess-engine

♔chess-engine♚

A dependency-free chess engine library built to run anywhere.

Demo | Docs | Contact Me

Written in Rust🦀💖
Chess in the terminal Chess in the web

Why write a Chess engine?

Above all, this video by Tom7 is my inspiration for this project. He’s absolutely brilliant and I implore you to watch his content.

I love chess a lot. It’s definitely one of my favorite games ever. However, I’ve always been disappointed when trying to write programs that play chess digitally (particularly in a compiled language). Although several amazing engines exist, it’s near impossible to find a neat library for chess-related-programming that runs on everything.

chess-engine is a solution to my problem. If you want a chess engine that runs on embedded devices, the terminal, the desktop (with a gui), and the web, this is probably your best bet.

How does it work?

This particular AI (along with most other chess AIs) works using the Minimax algorithm, along with Alpha-Beta pruning for optimization.

Now, let’s unpack that.

The Minimax algorithm essentially iterates through all possible moves recursively, and evaluates all of the boards after the moves are played. If the board is more favorable, it will encourage playing its parent move, but if a board is less favorable, then it will select against playing a given move.

Minimax

Additionally, when the AI attempts to see past just the current board, it will assume the human always responds with the best moves. As a result, the computer almost never blunders. This allows the computer to almost always play objectively better moves than the player.

Embedded in the Web

Because it has zero dependencies, it’s extremely simple to embed in the web browser using wasm. Try playing it yourself!

Average AI Setting

Try playing it yourself!

Usage

The Board structure has a few different methods that allow users to generate moves from a given position, including get_best_next_move, get_worst_next_move, and get_legal_moves. These are particularly handy for writing chess AIs to play against.

fn main() {
    let board = Board::default();

    // Get the best move with 4 moves of lookahead
    let best_move = board.get_best_next_move(4);
    // Get the worst move with 3 moves of lookahead
    let worst_move = board.get_worst_next_move(3);

    // Get all of the possible legal moves for the given player
    let legal_moves = board.get_legal_moves();
    // Print the board
    println!("{}", board);

    print!("CPU chose to ");
    match best_move {
        Move::Piece(from, to) => println!("move {} to {}", from, to),
        Move::KingSideCastle => println!("castle kingside"),
        Move::QueenSideCastle => println!("castle queenside"),
        Move::Resign => println!("resign")
    }
}

To add some variation or more advanced play, consider writing an AI that plays known openings that build better positions before using the get_best_next_move method!

Custom Boards

Additionally, users can create their own custom Board objects other than the default one. This is done using the BoardBuilder structure. The BoardBuilder structure supports enabling and disabling castling, placing rows and columns of pieces, and placing individual pieces.

Keep in mind when using a BoardBuilder that castling is disabled by default!

Play the Horde Chess Variant

Play the Horde Chess Variant
fn main() {
    // `BoardBuilder::new()` returns an empty board
    // with castling disabled.
    // Creating a board builder from another board
    // structure will preserve
    // all settings from the board (such as castling
    // and the last en-passant move).

    // This BoardBuilder constructs the "Horde" chess variant!
    let board = BoardBuilder::from(Board::default())
            .row(Piece::Pawn(WHITE, A1))
            .row(Piece::Pawn(WHITE, A2))
            .row(Piece::Pawn(WHITE, A3))
            .row(Piece::Pawn(WHITE, A4))
            .piece(Piece::Pawn(WHITE, F5))
            .piece(Piece::Pawn(WHITE, G5))
            .piece(Piece::Pawn(WHITE, B5))
            .piece(Piece::Pawn(WHITE, C5))
            .build();

    // The CPU can also play variants!
    let cpu_move = board.get_best_next_move(3);
    
    match board.play_move(cpu_move) {
        GameResult::Continuing(next_board) => {
            println!("{}", next_board);
        }

        GameResult::Victory(winner) => {
            // You can use the ! operator on a player's
            // color to invert.
            println!("{} loses. {} is victorious.",
              !winner, winner
            );
        }

        GameResult::IllegalMove(x) => {
            eprintln!("{} is an illegal move.", x);
        }

        GameResult::Stalemate => {
            println!("Drawn game.");
        }
    }
}

About the Author

Website | Blog | GitHub

I'm a freshman in college, mainly working on side projects like these in the ~30 minute breaks between classes. If you enjoy my projects, consider supporting me by buying me a coffee!

Buy Me A Coffee