1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//! 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;

/// 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)
}