chess-lib 0.1.3

A chess movement generator library.
Documentation
//! chess-lib
//!
//! This is a chess movement generation library.

// TODO Remove it once the code base is stable
#![allow(dead_code)]

use rand_pcg::Pcg64Mcg;

use crate::engine::common::{File, Move, Promotion, Rank, Square};
use crate::engine::magics::{find_and_print_all_magics, load_magics_table, BISHOP, ROOK};
use crate::engine::move_generator::MoveGenerator;

mod engine;

/// Generate all legal moves for a given FEN string
///
/// # Examples
///
/// ```
/// use chess_lib::generate_legal_moves;
///
/// let moves = generate_legal_moves("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
///
/// assert_eq!(moves.len(), 20);
/// assert!(moves.contains(&"a2a3".to_string()));
///
/// let moves = generate_legal_moves("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -");
///
/// assert_eq!(moves.len(), 48);
/// assert!(moves.contains(&"e1g1".to_string()));
/// ```
pub fn generate_legal_moves(fen: &str) -> Vec<String> {
    let board = engine::board::Board::from_fen(fen).unwrap();
    let tables = load_magics_table();

    let mut move_gen = MoveGenerator::new(board, &tables);

    let moves = move_gen.generate_moves();

    moves.iter().map(|m| format!("{}", m.1)).collect()
}

/// Run perft test
///
/// # Examples
///
/// ```
/// use chess_lib::run_perft;
///
/// // Starting position after move e2 to e4
/// let result = run_perft(5, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", vec!["e2e4"]);
///
/// assert_eq!(result, 9771632);
///
/// // Kiwipete position: https://www.chessprogramming.org/Perft_Results#Position_2
/// let result = run_perft(4, "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -", vec![]);
///
/// assert_eq!(result, 4085603);
/// ```
pub fn run_perft(depth: usize, fen: &str, moves: Vec<&str>) -> usize {
    let mut board = engine::board::Board::from_fen(fen).unwrap();
    let tables = load_magics_table();

    for input_move in moves {
        println!("Applying move: {}", input_move);
        board = board.apply_move(parse_move(input_move)).unwrap();
    }

    let move_gen = MoveGenerator::new(board, &tables);

    engine::move_generator::perft(move_gen, depth)
}

/// Only used to generate the magic tables code
pub fn generate_slider_moves() {
    println!("// Automatically generated");
    println!("use crate::engine::magics::MagicEntry;");
    println!();

    let mut rng = Pcg64Mcg::new(0);

    find_and_print_all_magics(&ROOK, "ROOK", &mut rng);
    println!();

    find_and_print_all_magics(&BISHOP, "BISHOP", &mut rng);
    println!();
}

fn parse_move(input: &str) -> Move {
    let chars: Vec<char> = input.chars().collect();
    assert!(chars.len() == 4 || chars.len() == 5);

    let from_file = File::from_char(chars[0]).unwrap();
    let from_rank = Rank::from_char(chars[1]).unwrap();

    let to_file = File::from_char(chars[2]).unwrap();
    let to_rank = Rank::from_char(chars[3]).unwrap();

    let mut promotion = None;
    if chars.len() == 5 {
        promotion = Promotion::from_char(chars[4]);
    }

    let from = Square::new(from_file, from_rank);
    let to = Square::new(to_file, to_rank);

    // TODO Does not handle castling correctly
    Move::new(from, to, promotion)
}