chess_lib/
lib.rs

1//! chess-lib
2//!
3//! This is a chess movement generation library.
4
5// TODO Remove it once the code base is stable
6#![allow(dead_code)]
7
8use rand_pcg::Pcg64Mcg;
9
10use crate::engine::common::{File, Move, Promotion, Rank, Square};
11use crate::engine::magics::{find_and_print_all_magics, load_magics_table, BISHOP, ROOK};
12use crate::engine::move_generator::MoveGenerator;
13
14mod engine;
15
16/// Generate all legal moves for a given FEN string
17///
18/// # Examples
19///
20/// ```
21/// use chess_lib::generate_legal_moves;
22///
23/// let moves = generate_legal_moves("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
24///
25/// assert_eq!(moves.len(), 20);
26/// assert!(moves.contains(&"a2a3".to_string()));
27///
28/// let moves = generate_legal_moves("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -");
29///
30/// assert_eq!(moves.len(), 48);
31/// assert!(moves.contains(&"e1g1".to_string()));
32/// ```
33pub fn generate_legal_moves(fen: &str) -> Vec<String> {
34    let board = engine::board::Board::from_fen(fen).unwrap();
35    let tables = load_magics_table();
36
37    let mut move_gen = MoveGenerator::new(board, &tables);
38
39    let moves = move_gen.generate_moves();
40
41    moves.iter().map(|m| format!("{}", m.1)).collect()
42}
43
44/// Run perft test
45///
46/// # Examples
47///
48/// ```
49/// use chess_lib::run_perft;
50///
51/// // Starting position after move e2 to e4
52/// let result = run_perft(5, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", vec!["e2e4"]);
53///
54/// assert_eq!(result, 9771632);
55///
56/// // Kiwipete position: https://www.chessprogramming.org/Perft_Results#Position_2
57/// let result = run_perft(4, "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -", vec![]);
58///
59/// assert_eq!(result, 4085603);
60/// ```
61pub fn run_perft(depth: usize, fen: &str, moves: Vec<&str>) -> usize {
62    let mut board = engine::board::Board::from_fen(fen).unwrap();
63    let tables = load_magics_table();
64
65    for input_move in moves {
66        println!("Applying move: {}", input_move);
67        board = board.apply_move(parse_move(input_move)).unwrap();
68    }
69
70    let move_gen = MoveGenerator::new(board, &tables);
71
72    engine::move_generator::perft(move_gen, depth)
73}
74
75/// Only used to generate the magic tables code
76pub fn generate_slider_moves() {
77    println!("// Automatically generated");
78    println!("use crate::engine::magics::MagicEntry;");
79    println!();
80
81    let mut rng = Pcg64Mcg::new(0);
82
83    find_and_print_all_magics(&ROOK, "ROOK", &mut rng);
84    println!();
85
86    find_and_print_all_magics(&BISHOP, "BISHOP", &mut rng);
87    println!();
88}
89
90fn parse_move(input: &str) -> Move {
91    let chars: Vec<char> = input.chars().collect();
92    assert!(chars.len() == 4 || chars.len() == 5);
93
94    let from_file = File::from_char(chars[0]).unwrap();
95    let from_rank = Rank::from_char(chars[1]).unwrap();
96
97    let to_file = File::from_char(chars[2]).unwrap();
98    let to_rank = Rank::from_char(chars[3]).unwrap();
99
100    let mut promotion = None;
101    if chars.len() == 5 {
102        promotion = Promotion::from_char(chars[4]);
103    }
104
105    let from = Square::new(from_file, from_rank);
106    let to = Square::new(to_file, to_rank);
107
108    // TODO Does not handle castling correctly
109    Move::new(from, to, promotion)
110}