diff --git a/Cargo.lock b/Cargo.lock index 7c3efba..1d38eab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,19 +193,22 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "e54c115d4f30f52c67202f079c5f9d8b49db4691f460fdb0b4c2e838261b2ba5" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] name = "iai" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678" +source = "git+https://github.com/sigaloid/iai?rev=d56a597#d56a5971f6d5556cd9e9b92e7e0f753c9ce9cdc7" +dependencies = [ + "cfg-if", +] [[package]] name = "itertools" @@ -330,9 +333,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -359,9 +362,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.3" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "4a52d8d02cacdb176ef4678de6c052efb4b3da14b78e4db683a4252762be5433" dependencies = [ "aho-corasick", "memchr", @@ -371,9 +374,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "722166aa0d7438abbaa4d5cc2c649dac844e8c56d82fb3d33e9c34b5cd268fc6" dependencies = [ "aho-corasick", "memchr", @@ -382,9 +385,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "c3160422bbd54dd5ecfdca71e5fd59b7b8fe2b1697ab2baf64f6d05dcc66d298" [[package]] name = "rustversion" @@ -567,15 +570,35 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 94d2df3..c69b352 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,18 +9,25 @@ name = "mouse" [dependencies] getset = "0.1.6" +[build] +rustflags = ["-C", "target-cpu=native"] + +[profile.release] +codegen-units = 1 +lto = true + [dev-dependencies] criterion = "0.7.0" -iai = "0.1" +iai = { git = "https://github.com/sigaloid/iai", rev = "d56a597" } perft_fixtures = { path = "perft_fixtures" } [[bench]] name = "criterion_perft" harness = false -# [[bench]] -# name = "iai_perft" -# harness = false +[[bench]] +name = "iai_perft" +harness = false [[bench]] name = "manual_perft" diff --git a/benches/criterion_perft.rs b/benches/criterion_perft.rs index 819144c..63d5210 100644 --- a/benches/criterion_perft.rs +++ b/benches/criterion_perft.rs @@ -1,6 +1,6 @@ use criterion::{Criterion, Throughput, criterion_group, criterion_main}; use mouse::backend::perft::perft; -use mouse::{GameState, get_pseudo_legal_moves}; +use mouse::{State, get_pseudo_legal_moves}; use perft_fixtures::perft_fixtures::FAST_PERFT; pub fn criterion_perft(c: &mut Criterion) { @@ -12,7 +12,7 @@ pub fn criterion_perft(c: &mut Criterion) { let depth = perft_set_up.depth; let expected_nodes = perft_set_up.expected_nodes; - let mut state = GameState::new_from_fen(fen_string); + let mut state = State::new_from_fen(fen_string); run_criterion_perft(c, name, depth, expected_nodes, &mut state); } @@ -23,7 +23,7 @@ fn run_criterion_perft( name: String, depth: u8, expected_nodes: u64, - mut state: &mut GameState, + state: &mut State, ) { let mut group = c.benchmark_group(&*name); // group.sample_size(10); @@ -31,20 +31,14 @@ fn run_criterion_perft( // group.warm_up_time(std::time::Duration::from_millis(1000)); group.throughput(Throughput::Elements(expected_nodes)); group.bench_function(&*name, |b| { - b.iter(|| { - perft( - std::hint::black_box(&mut state), - std::hint::black_box(depth), - ) - }) + b.iter(|| perft(std::hint::black_box(state), std::hint::black_box(depth))) }); group.finish(); } pub fn criterion_make_unmake_move(c: &mut Criterion) { - let mut state = GameState::new_from_fen( - "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQ - 0 1", - ); + let state = + State::new_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQ - 0 1"); let moves = get_pseudo_legal_moves(&state); let mut group = c.benchmark_group("General make unmake"); @@ -52,8 +46,7 @@ pub fn criterion_make_unmake_move(c: &mut Criterion) { group.bench_function("General make unmake", |b| { b.iter(|| { for moove in &moves[0..moves.len()] { - state.make_move(*moove); - state.unmake_move(*moove); + let _ = state.make_move(std::hint::black_box(*moove)); } }) }); @@ -61,9 +54,8 @@ pub fn criterion_make_unmake_move(c: &mut Criterion) { } pub fn criterion_move_gen(c: &mut Criterion) { - let state = GameState::new_from_fen( - "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", - ); + let state = + State::new_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"); let expected_moves = 48; let mut group = c.benchmark_group("Move gen"); diff --git a/benches/iai_perft.rs b/benches/iai_perft.rs index 2746aa8..9864cd1 100644 --- a/benches/iai_perft.rs +++ b/benches/iai_perft.rs @@ -1,7 +1,7 @@ use iai::black_box; -use mouse::GameState; +use mouse::State; use mouse::backend::perft::perft; -use perft_fixtures::perft_fixtures::{NORMAL_PERFT, PerftFixture}; +use perft_fixtures::perft_fixtures::{FAST_PERFT, PerftFixture}; pub fn iai_perft(perft_fixture: &PerftFixture) { let fen_string = perft_fixture.perft_setup.fen; @@ -11,13 +11,40 @@ pub fn iai_perft(perft_fixture: &PerftFixture) { let depth = perft_fixture.depth; let _ = perft_fixture.expected_nodes; - let mut state = GameState::new_from_fen(fen_string); + let state = State::new_from_fen(fen_string); - perft(black_box(&mut state), black_box(depth)); + let _ = perft(black_box(&state), black_box(depth)); } pub fn starting_perft() { - iai_perft(&NORMAL_PERFT[0]); + iai_perft(&FAST_PERFT[0]); } -iai::main!(starting_perft); +pub fn position_2_perft() { + iai_perft(&FAST_PERFT[1]); +} + +pub fn position_3_perft() { + iai_perft(&FAST_PERFT[2]); +} + +pub fn position_4_perft() { + iai_perft(&FAST_PERFT[3]); +} + +pub fn position_5_perft() { + iai_perft(&FAST_PERFT[4]); +} + +pub fn position_6_perft() { + iai_perft(&FAST_PERFT[5]); +} + +iai::main!( + starting_perft, + position_2_perft, + position_3_perft, + position_4_perft, + position_5_perft, + position_6_perft +); diff --git a/benches/manual_perft.rs b/benches/manual_perft.rs index 0d641f7..ac2a7b0 100644 --- a/benches/manual_perft.rs +++ b/benches/manual_perft.rs @@ -1,4 +1,4 @@ -use mouse::GameState; +use mouse::State; use mouse::backend::perft::perft; use perft_fixtures::perft_fixtures::{NORMAL_PERFT, PerftFixture}; use std::time::Instant; @@ -27,12 +27,12 @@ pub fn manual_perft() { fn run_nps_perft(perft_fixture: PerftFixture) -> u64 { let fen = perft_fixture.perft_setup.fen; - let mut game_state = GameState::new_from_fen(fen); + let state = State::new_from_fen(fen); // Start timer to calculate nodes per second. let now = Instant::now(); - let nodes = perft(&mut game_state, perft_fixture.depth); + let nodes = perft(&state, perft_fixture.depth); let elapsed = now.elapsed(); let nodes_per_second = nodes as f64 / elapsed.as_secs_f64(); diff --git a/src/backend/movegen/check_decider.rs b/src/backend/movegen/check_decider.rs index d68c097..4d1db31 100644 --- a/src/backend/movegen/check_decider.rs +++ b/src/backend/movegen/check_decider.rs @@ -2,22 +2,20 @@ use crate::backend::movegen::compile_time::move_cache_non_sliders::{ KING_MOVES, KNIGHT_MOVES, PAWN_CAPTURE_MOVES, }; use crate::backend::movegen::move_gen_sliders::calculate_slider_move_bitboard; -use crate::backend::state::board::bitboard::Bitboard; -use crate::backend::state::game::game_state::GameState; -use crate::backend::state::piece::PieceType::{Bishop, Queen, Rook}; -use crate::backend::state::piece::{Piece, PieceColor, PieceType}; +use crate::backend::state::board::bitboard::BitBoard; +use crate::backend::state::game::state::State; +use crate::backend::state::piece::Piece::{Bishop, Queen, Rook}; +use crate::backend::state::piece::{ALL_PIECES, Piece, Side}; use crate::backend::state::square::Square; -pub fn is_in_check_on_square( - game_state: &GameState, - color: PieceColor, - king_square: Square, -) -> bool { - let friendly_bb = game_state.bb_manager().get_all_pieces_off(color); - let enemy_bb = game_state.bb_manager().get_all_pieces_off(color.opposite()); +pub fn is_in_check_on_square(game_state: &State, color: Side, king_square: Square) -> bool { + let friendly_bb = game_state.bb_manager().get_all_pieces_bb_off(color); + let enemy_bb = game_state + .bb_manager() + .get_all_pieces_bb_off(color.opposite()); // Iterate over all pieces. Let`s assume we are checking for knights. - for piece_type in PieceType::get_all_types() { + for piece_type in ALL_PIECES { // Get the bitboard that represents all possible attacks. let attack_bitboard = get_attack_bitboard_for_piece_and_square( piece_type, @@ -28,11 +26,10 @@ pub fn is_in_check_on_square( ); // Get the bitboard that marks where enemy knights are standing. - let enemy_piece = Piece::new(piece_type, color.opposite()); - let enemy_piece_bitboard = game_state.bb_manager().get_bitboard(enemy_piece); + let enemy_piece_bitboard = game_state.bb_manager().get_piece_bb(piece_type) & enemy_bb; // Check if at least one of the places we could move to contains an enemy knight. - let resulting_bitboard = attack_bitboard & *enemy_piece_bitboard; + let resulting_bitboard = attack_bitboard & enemy_piece_bitboard; // If so, we know that the king is in check. if resulting_bitboard.is_not_empty() { return true; @@ -60,7 +57,7 @@ pub fn is_in_check_on_square( /// - A boolean value: /// - `true` if the king of the specified color is in check. /// - `false` otherwise. -pub fn is_in_check(game_state: &GameState, color: PieceColor) -> bool { +pub fn is_in_check(game_state: &State, color: Side) -> bool { // Idea: // If, for example, color == white, we want to figure out if white is currently in check. // We then pretend that the white king is one after the other replaced by: pawn, rook, bishop, queen, king. @@ -76,23 +73,24 @@ pub fn is_in_check(game_state: &GameState, color: PieceColor) -> bool { } /// Returns the square where the king of the respective side is located. -fn get_kings_square(game_state: &GameState, color: PieceColor) -> Square { - let king = Piece::new(PieceType::King, color); - let king_bitboard = game_state.bb_manager().get_bitboard(king); - king_bitboard.clone().next().unwrap() +fn get_kings_square(game_state: &State, color: Side) -> Square { + let king_bitboard = game_state.bb_manager().get_piece_bb(Piece::King); + let side_bb = game_state.bb_manager().get_all_pieces_bb_off(color); + let mut bb = king_bitboard & side_bb; + bb.next().unwrap() } fn get_attack_bitboard_for_piece_and_square( - piece_type: PieceType, - piece_color: PieceColor, + piece_type: Piece, + piece_color: Side, square: Square, - friendly_bb: Bitboard, - enemy_bb: Bitboard, -) -> Bitboard { + friendly_bb: BitBoard, + enemy_bb: BitBoard, +) -> BitBoard { match piece_type { - PieceType::King => KING_MOVES[square.square_to_index()], - PieceType::Knight => KNIGHT_MOVES[square.square_to_index()], - PieceType::Pawn => PAWN_CAPTURE_MOVES[piece_color as usize][square.square_to_index()], + Piece::King => KING_MOVES[square.square_to_index()], + Piece::Knight => KNIGHT_MOVES[square.square_to_index()], + Piece::Pawn => PAWN_CAPTURE_MOVES[piece_color as usize][square.square_to_index()], Rook => calculate_slider_move_bitboard(Rook, square, friendly_bb, enemy_bb), Bishop => calculate_slider_move_bitboard(Bishop, square, friendly_bb, enemy_bb), Queen => calculate_slider_move_bitboard(Queen, square, friendly_bb, enemy_bb), diff --git a/src/backend/movegen/compile_time/move_cache_non_sliders.rs b/src/backend/movegen/compile_time/move_cache_non_sliders.rs index e2d629a..9b894b7 100644 --- a/src/backend/movegen/compile_time/move_cache_non_sliders.rs +++ b/src/backend/movegen/compile_time/move_cache_non_sliders.rs @@ -1,6 +1,6 @@ use crate::backend::constants::{SIDES, SQUARES_AMOUNT}; -use crate::backend::state::board::bitboard::Bitboard; -use crate::backend::state::piece::{PieceColor, PieceType}; +use crate::backend::state::board::bitboard::BitBoard; +use crate::backend::state::piece::{Piece, Side}; use crate::backend::state::square::Square; /// All of this gets generated at compile time, in the functions below. @@ -19,11 +19,10 @@ use crate::backend::state::square::Square; /// At runtime we have to apply some further checks to this bitboard: /// 1. Are some of these squares blocked by friendly pieces? /// 2. Would this move put me in check etc...? -pub const KING_MOVES: [Bitboard; SQUARES_AMOUNT] = calculate_potential_moves_cache(PieceType::King); +pub const KING_MOVES: [BitBoard; SQUARES_AMOUNT] = calculate_potential_moves_cache(Piece::King); /// The same as above, but for the knight. -pub const KNIGHT_MOVES: [Bitboard; SQUARES_AMOUNT] = - calculate_potential_moves_cache(PieceType::Knight); +pub const KNIGHT_MOVES: [BitBoard; SQUARES_AMOUNT] = calculate_potential_moves_cache(Piece::Knight); enum PawnMoveType { Quiet, @@ -32,15 +31,15 @@ enum PawnMoveType { } /// All quiet moves for pawns. -pub const PAWN_QUIET_MOVES: [[Bitboard; SQUARES_AMOUNT]; SIDES] = +pub const PAWN_QUIET_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] = generate_pawn_moves(PawnMoveType::Quiet); /// All capture moves for pawns. -pub const PAWN_CAPTURE_MOVES: [[Bitboard; SQUARES_AMOUNT]; SIDES] = +pub const PAWN_CAPTURE_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] = generate_pawn_moves(PawnMoveType::Capture); /// All capture moves for pawns. -pub const PAWN_DOUBLE_PUSH_MOVES: [[Bitboard; SQUARES_AMOUNT]; SIDES] = +pub const PAWN_DOUBLE_PUSH_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] = generate_pawn_moves(PawnMoveType::DoublePush); /// Initializes a collection of bitboards representing all possible moves for each square. @@ -53,8 +52,8 @@ pub const PAWN_DOUBLE_PUSH_MOVES: [[Bitboard; SQUARES_AMOUNT]; SIDES] = /// # Returns /// An array of `BitBoard` of size `SQUARES_AMOUNT`, where each entry corresponds to the /// possible moves for the square at the same index. -const fn calculate_potential_moves_cache(piece_type: PieceType) -> [Bitboard; SQUARES_AMOUNT] { - let mut potential_moves = [Bitboard::new(); SQUARES_AMOUNT]; +const fn calculate_potential_moves_cache(piece_type: Piece) -> [BitBoard; SQUARES_AMOUNT] { + let mut potential_moves = [BitBoard::new(); SQUARES_AMOUNT]; // iterate over all squares let mut square_index: usize = 0; @@ -64,8 +63,8 @@ const fn calculate_potential_moves_cache(piece_type: PieceType) -> [Bitboard; SQ // and generate the moves for that square potential_moves[square_index] = match piece_type { - PieceType::Knight => generate_knight_moves(square), - PieceType::King => generate_king_moves(square), + Piece::Knight => generate_knight_moves(square), + Piece::King => generate_king_moves(square), _ => panic!("Invalid piece type"), }; @@ -82,8 +81,8 @@ const fn calculate_potential_moves_cache(piece_type: PieceType) -> [Bitboard; SQ /// /// # Returns /// A `BitBoard` containing all valid surrounding squares a king can move to. -const fn generate_king_moves(square: Square) -> Bitboard { - let mut bitboard = Bitboard::new(); +const fn generate_king_moves(square: Square) -> BitBoard { + let mut bitboard = BitBoard::new(); // since this fn is const, we can't use a loop // instead we use a while loop @@ -116,8 +115,8 @@ const fn generate_king_moves(square: Square) -> Bitboard { } /// Same as above but for the knight. -const fn generate_knight_moves(square: Square) -> Bitboard { - let mut bitboard = Bitboard::new(); +const fn generate_knight_moves(square: Square) -> BitBoard { + let mut bitboard = BitBoard::new(); // The offsets for the knight moves. Starting in the top left corner. // `_ _ _ _ _ _ _ _` @@ -149,22 +148,22 @@ const fn generate_knight_moves(square: Square) -> Bitboard { bitboard } -const fn generate_pawn_moves(pawn_move_type: PawnMoveType) -> [[Bitboard; SQUARES_AMOUNT]; SIDES] { - let mut quiet_moves = [[Bitboard::new(); SQUARES_AMOUNT]; SIDES]; +const fn generate_pawn_moves(pawn_move_type: PawnMoveType) -> [[BitBoard; SQUARES_AMOUNT]; SIDES] { + let mut quiet_moves = [[BitBoard::new(); SQUARES_AMOUNT]; SIDES]; let mut side_index = 0; while side_index < 2 { let active_color = match side_index { - 0 => PieceColor::White, - 1 => PieceColor::Black, + 0 => Side::White, + 1 => Side::Black, _ => panic!("Invalid side index"), }; - let mut potential_moves = [Bitboard::new(); SQUARES_AMOUNT]; + let mut potential_moves = [BitBoard::new(); SQUARES_AMOUNT]; // iterate over all squares let mut square_index: usize = 0; while square_index < SQUARES_AMOUNT { - let mut bitboard = Bitboard::new(); + let mut bitboard = BitBoard::new(); // generate a square struct from the index let square = Square::index_to_square(square_index as i8); @@ -194,22 +193,14 @@ const fn generate_pawn_moves(pawn_move_type: PawnMoveType) -> [[Bitboard; SQUARE quiet_moves } -const fn generate_pawn_quiet_moves( - square: Square, - bitboard: &mut Bitboard, - active_color: PieceColor, -) { +const fn generate_pawn_quiet_moves(square: Square, bitboard: &mut BitBoard, active_color: Side) { let forward_square = square.forward_by_one(active_color); if forward_square.is_valid() { bitboard.fill_square(forward_square); } } -const fn generate_pawn_attack_moves( - square: Square, - bitboard: &mut Bitboard, - active_color: PieceColor, -) { +const fn generate_pawn_attack_moves(square: Square, bitboard: &mut BitBoard, active_color: Side) { let right_diagonal_square = square.right_by_one().forward_by_one(active_color); let left_diagonal_square = square.left_by_one().forward_by_one(active_color); @@ -223,8 +214,8 @@ const fn generate_pawn_attack_moves( const fn generate_pawn_double_push_moves( square: Square, - bitboard: &mut Bitboard, - active_color: PieceColor, + bitboard: &mut BitBoard, + active_color: Side, ) { if !square.is_pawn_start(active_color) { return; diff --git a/src/backend/movegen/moove.rs b/src/backend/movegen/moove.rs index 9918317..7e3537b 100644 --- a/src/backend/movegen/moove.rs +++ b/src/backend/movegen/moove.rs @@ -1,5 +1,5 @@ -use crate::backend::state::piece::PieceType; -use crate::backend::state::piece::PieceType::{Bishop, Knight, Queen, Rook}; +use crate::backend::state::piece::Piece; +use crate::backend::state::piece::Piece::{Bishop, Knight, Queen, Rook}; use crate::backend::state::square::Square; use getset::{CloneGetters, Setters}; use std::fmt::{Display, Formatter}; @@ -37,7 +37,7 @@ pub struct Moove { #[getset(get_clone = "pub", set = "pub")] to: Square, #[getset(get_clone = "pub", set = "pub")] - promotion_type: Option, + promotion_type: Option, } impl Moove { diff --git a/src/backend/movegen/move_gen.rs b/src/backend/movegen/move_gen.rs index ab21f94..4185cdf 100644 --- a/src/backend/movegen/move_gen.rs +++ b/src/backend/movegen/move_gen.rs @@ -4,10 +4,10 @@ use crate::backend::movegen::moove::Moove; use crate::backend::movegen::move_gen_king_util::gen_castles; use crate::backend::movegen::move_gen_pawn_util::gen_pawn_moves; use crate::backend::movegen::move_gen_sliders::get_moves_for_non_slider_piece; -use crate::backend::state::board::bitboard::Bitboard; -use crate::backend::state::game::game_state::GameState; -use crate::backend::state::piece::PieceType::{King, Knight}; -use crate::backend::state::piece::{Piece, PieceType}; +use crate::backend::state::board::bitboard::BitBoard; +use crate::backend::state::game::state::State; +use crate::backend::state::piece::Piece::{King, Knight}; +use crate::backend::state::piece::{SLIDER_PIECES, TRIVIAL_PIECES}; use crate::backend::state::square::Square; /// Generates and returns all the pseudo legal moves for the current player's pieces @@ -22,18 +22,19 @@ use crate::backend::state::square::Square; /// /// * A `Vec` containing all the computed pseudo legal moves for the current player's /// pieces. -pub fn get_pseudo_legal_moves(game_state: &GameState) -> Vec { +pub fn get_pseudo_legal_moves(game_state: &State) -> Vec { let bitboard_manager = game_state.bb_manager(); // Bitboard containing all pieces of the active color. These block moves. - let friendly_pieces_bb = bitboard_manager.get_all_pieces_off(game_state.active_color()); + let friendly_pieces_bb = bitboard_manager.get_all_pieces_bb_off(game_state.active_color()); // Bitboard containing all pieces of the opponent color. These are relevant for sliders and pawn captures. - let enemy_pieces_bb = bitboard_manager.get_all_pieces_off(game_state.active_color().opposite()); + let enemy_pieces_bb = + bitboard_manager.get_all_pieces_bb_off(game_state.active_color().opposite()); let mut all_pseudo_legal_moves = Vec::new(); let active_color = game_state.active_color(); // Move gen for king and knight (excluding castles) - for trivial_type in PieceType::get_trivial_types() { + for trivial_type in TRIVIAL_PIECES { let moves_cache = match trivial_type { Knight => KNIGHT_MOVES, King => KING_MOVES, @@ -41,7 +42,7 @@ pub fn get_pseudo_legal_moves(game_state: &GameState) -> Vec { }; let mut moves = iterate_over_bitboard_for_non_slider( moves_cache, - *bitboard_manager.get_bitboard(Piece::new(trivial_type, active_color)), + bitboard_manager.get_colored_piece_bb(trivial_type, active_color), friendly_pieces_bb, ); all_pseudo_legal_moves.append(&mut moves); @@ -64,10 +65,10 @@ pub fn get_pseudo_legal_moves(game_state: &GameState) -> Vec { ); // Gen queen, bishop and rook moves - for slider_type in PieceType::get_slider_types() { + for slider_type in SLIDER_PIECES { let mut moves = get_moves_for_non_slider_piece( slider_type, - *bitboard_manager.get_bitboard(Piece::new(slider_type, active_color)), + bitboard_manager.get_colored_piece_bb(slider_type, active_color), friendly_pieces_bb, enemy_pieces_bb, ); @@ -82,9 +83,9 @@ pub fn get_pseudo_legal_moves(game_state: &GameState) -> Vec { // ------------------------------------ pub(crate) fn iterate_over_bitboard_for_non_slider( - moves_cache: [Bitboard; SQUARES_AMOUNT], - piece_bitboard: Bitboard, - mask_bitboard: Bitboard, + moves_cache: [BitBoard; SQUARES_AMOUNT], + piece_bitboard: BitBoard, + mask_bitboard: BitBoard, ) -> Vec { // PERF: Instead of creating a new vector for each piece, we could reuse the same vector and append to it. let mut moves: Vec = Vec::new(); @@ -110,7 +111,7 @@ pub(crate) fn iterate_over_bitboard_for_non_slider( moves } -pub fn convert_bitboard_to_moves(square: Square, moves_bitboard: Bitboard) -> Vec { +pub fn convert_bitboard_to_moves(square: Square, moves_bitboard: BitBoard) -> Vec { // generate all the moves let mut moves: Vec = Vec::new(); for to_square in moves_bitboard { diff --git a/src/backend/movegen/move_gen_king_util.rs b/src/backend/movegen/move_gen_king_util.rs index 4dc3aa2..562d02a 100644 --- a/src/backend/movegen/move_gen_king_util.rs +++ b/src/backend/movegen/move_gen_king_util.rs @@ -1,16 +1,16 @@ use crate::backend::movegen::check_decider::is_in_check_on_square; use crate::backend::movegen::moove::{CastleType, Moove}; -use crate::backend::state::board::bitboard::Bitboard; -use crate::backend::state::game::game_state::GameState; +use crate::backend::state::board::bitboard::BitBoard; use crate::backend::state::game::irreversible_data::IrreversibleData; -use crate::backend::state::piece::PieceColor; +use crate::backend::state::game::state::State; +use crate::backend::state::piece::Side; use crate::backend::state::square::{B1, B8, C1, C8, D1, D8, E1, E8, F1, F8, G1, G8, Square}; // Made these values with: https://tearth.dev/bitboard-viewer/ -const WHITE_LONG_CASTLE_MASK: Bitboard = Bitboard::new_from_squares(&[B1, C1, D1]); -const WHITE_SHORT_CASTLE_MASK: Bitboard = Bitboard::new_from_squares(&[F1, G1]); -const BLACK_LONG_CASTLE_MASK: Bitboard = Bitboard::new_from_squares(&[B8, C8, D8]); -const BLACK_SHORT_CASTLE_MASK: Bitboard = Bitboard::new_from_squares(&[F8, G8]); +const WHITE_LONG_CASTLE_MASK: BitBoard = BitBoard::new_from_squares(&[B1, C1, D1]); +const WHITE_SHORT_CASTLE_MASK: BitBoard = BitBoard::new_from_squares(&[F1, G1]); +const BLACK_LONG_CASTLE_MASK: BitBoard = BitBoard::new_from_squares(&[B8, C8, D8]); +const BLACK_SHORT_CASTLE_MASK: BitBoard = BitBoard::new_from_squares(&[F8, G8]); const WHITE_LONG_CASTLE_MOVE: Moove = Moove::new(E1, C1); const WHITE_SHORT_CASTLE_MOVE: Moove = Moove::new(E1, G1); @@ -24,10 +24,10 @@ const BLACK_SHORT_CASTLE_CHECK_SQUARES: [Square; 3] = [E8, F8, G8]; pub fn gen_castles( all_pseudo_legal_moves: &mut Vec, - game_state: &GameState, - combined_bb: Bitboard, + game_state: &State, + combined_bb: BitBoard, ) { - let irreversible_data = game_state.irreversible_data_stack().last().unwrap(); + let irreversible_data = game_state.irreversible_data(); for castle_type in CastleType::get_all_types() { let (castling_rights, squares_the_king_moves_through, between_king_rook_bb, moove) = @@ -47,11 +47,11 @@ pub fn gen_castles( fn gen_castle( all_pseudo_legal_moves: &mut Vec, - game_state: &GameState, - combined_bb: Bitboard, + game_state: &State, + combined_bb: BitBoard, castling_rights: bool, squares_the_king_moves_through: [Square; 3], - between_king_rook_bb: Bitboard, + between_king_rook_bb: BitBoard, moove: Moove, ) { // do we have castling rights for this type of castle? @@ -81,17 +81,17 @@ fn gen_castle( fn get_needed_constants( irreversible_data: &IrreversibleData, castle_types: &CastleType, - piece_color: PieceColor, -) -> (bool, [Square; 3], Bitboard, Moove) { + piece_color: Side, +) -> (bool, [Square; 3], BitBoard, Moove) { match castle_types { CastleType::Long => match piece_color { - PieceColor::White => ( + Side::White => ( irreversible_data.get_long_castle_rights(piece_color), WHITE_LONG_CASTLE_CHECK_SQUARES, WHITE_LONG_CASTLE_MASK, WHITE_LONG_CASTLE_MOVE, ), - PieceColor::Black => ( + Side::Black => ( irreversible_data.get_long_castle_rights(piece_color), BLACK_LONG_CASTLE_CHECK_SQUARES, BLACK_LONG_CASTLE_MASK, @@ -99,13 +99,13 @@ fn get_needed_constants( ), }, CastleType::Short => match piece_color { - PieceColor::White => ( + Side::White => ( irreversible_data.get_short_castle_rights(piece_color), WHITE_SHORT_CASTLE_CHECK_SQUARES, WHITE_SHORT_CASTLE_MASK, WHITE_SHORT_CASTLE_MOVE, ), - PieceColor::Black => ( + Side::Black => ( irreversible_data.get_short_castle_rights(piece_color), BLACK_SHORT_CASTLE_CHECK_SQUARES, BLACK_SHORT_CASTLE_MASK, diff --git a/src/backend/movegen/move_gen_pawn_util.rs b/src/backend/movegen/move_gen_pawn_util.rs index 5a6c017..a4613c8 100644 --- a/src/backend/movegen/move_gen_pawn_util.rs +++ b/src/backend/movegen/move_gen_pawn_util.rs @@ -2,24 +2,24 @@ use crate::backend::movegen::compile_time::move_cache_non_sliders::{ PAWN_CAPTURE_MOVES, PAWN_DOUBLE_PUSH_MOVES, PAWN_QUIET_MOVES, }; use crate::backend::movegen::moove::Moove; -use crate::backend::state::board::bitboard::Bitboard; -use crate::backend::state::board::bitboard_manager::BitboardManager; -use crate::backend::state::game::game_state::GameState; -use crate::backend::state::piece::PieceType::{Pawn, Queen}; -use crate::backend::state::piece::{Piece, PieceColor, PieceType}; +use crate::backend::state::board::bb_manager::BBManager; +use crate::backend::state::board::bitboard::BitBoard; +use crate::backend::state::game::state::State; +use crate::backend::state::piece::Piece::{Pawn, Queen}; +use crate::backend::state::piece::{PROMOTABLE_PIECES, Side}; pub fn gen_pawn_moves( - game_state: &GameState, - bitboard_manager: &BitboardManager, - friendly_pieces_bb: Bitboard, - enemy_pieces_bb: Bitboard, + game_state: &State, + bitboard_manager: &BBManager, + friendly_pieces_bb: BitBoard, + enemy_pieces_bb: BitBoard, all_pseudo_legal_moves: &mut Vec, - active_color: PieceColor, + active_color: Side, ) { // Quiet pawn moves let mut moves = crate::backend::movegen::move_gen::iterate_over_bitboard_for_non_slider( PAWN_QUIET_MOVES[active_color as usize], - *bitboard_manager.get_bitboard(Piece::new(Pawn, active_color)), + bitboard_manager.get_colored_piece_bb(Pawn, active_color), friendly_pieces_bb | enemy_pieces_bb, ); promotion_logic(&mut moves); @@ -28,7 +28,7 @@ pub fn gen_pawn_moves( // Capture pawn moves let mut moves = crate::backend::movegen::move_gen::iterate_over_bitboard_for_non_slider( PAWN_CAPTURE_MOVES[active_color as usize], - *bitboard_manager.get_bitboard(Piece::new(Pawn, active_color)), + bitboard_manager.get_colored_piece_bb(Pawn, active_color), create_pawn_capture_mask(game_state, enemy_pieces_bb), ); promotion_logic(&mut moves); @@ -43,17 +43,17 @@ pub fn gen_pawn_moves( all_pseudo_legal_moves.append(&mut moves); } pub fn get_double_pawn_push_moves( - bitboard_manager: &BitboardManager, - active_color: PieceColor, - all_pieces_bb: Bitboard, + bitboard_manager: &BBManager, + active_color: Side, + all_pieces_bb: BitBoard, ) -> Vec { let mut moves: Vec = Vec::new(); - let mut pawn_bitboard = *bitboard_manager.get_bitboard(Piece::new(Pawn, active_color)); + let mut pawn_bitboard = bitboard_manager.get_colored_piece_bb(Pawn, active_color); let starting_bitboard = match active_color { - PieceColor::White => Bitboard::new_from_rank(1), - PieceColor::Black => Bitboard::new_from_rank(6), + Side::White => BitBoard::new_from_rank(1), + Side::Black => BitBoard::new_from_rank(6), }; pawn_bitboard &= starting_bitboard; @@ -84,18 +84,10 @@ pub fn get_double_pawn_push_moves( moves } -pub fn create_pawn_capture_mask( - game_state: &GameState, - enemy_pieces_bitboard: Bitboard, -) -> Bitboard { +pub fn create_pawn_capture_mask(game_state: &State, enemy_pieces_bitboard: BitBoard) -> BitBoard { // Capture pawn moves let mut pawn_capture_mask = enemy_pieces_bitboard; - match game_state - .irreversible_data_stack() - .last() - .unwrap() - .en_passant_square() - { + match game_state.irreversible_data().en_passant_square() { None => {} Some(ep_square) => { pawn_capture_mask.fill_square(ep_square); @@ -112,7 +104,7 @@ pub fn promotion_logic(moves: &mut Vec) { for index in (0..moves.len()).rev() { let moove = moves[index]; if moove.to().is_on_promotion_rank() { - for piece_type in PieceType::get_promotable_types() { + for piece_type in PROMOTABLE_PIECES { if piece_type == Queen { moves[index].set_promotion_type(Some(Queen)); continue; diff --git a/src/backend/movegen/move_gen_sliders.rs b/src/backend/movegen/move_gen_sliders.rs index 4317701..476f7c1 100644 --- a/src/backend/movegen/move_gen_sliders.rs +++ b/src/backend/movegen/move_gen_sliders.rs @@ -3,8 +3,8 @@ use crate::backend::movegen::move_gen::convert_bitboard_to_moves; use crate::backend::movegen::move_gen_sliders::SlideDirection::{ Down, DownLeft, DownRight, Left, Right, Up, UpLeft, UpRight, }; -use crate::backend::state::board::bitboard::Bitboard; -use crate::backend::state::piece::PieceType; +use crate::backend::state::board::bitboard::BitBoard; +use crate::backend::state::piece::Piece; use crate::backend::state::square::Square; enum SlideDirection { @@ -32,15 +32,15 @@ impl SlideDirection { } } - fn directions_for_piece_type(piece_type: PieceType) -> Vec { + fn directions_for_piece_type(piece_type: Piece) -> Vec { match piece_type { - PieceType::Rook => { + Piece::Rook => { vec![Up, Down, Left, Right] } - PieceType::Bishop => { + Piece::Bishop => { vec![UpRight, DownRight, DownLeft, UpLeft] } - PieceType::Queen => { + Piece::Queen => { vec![Up, Down, Left, Right, UpRight, DownRight, DownLeft, UpLeft] } _ => panic!("Piece type is not a slider"), @@ -49,10 +49,10 @@ impl SlideDirection { } pub fn get_moves_for_non_slider_piece( - piece_type: PieceType, - piece_bb: Bitboard, - friendly_pieces_bb: Bitboard, - enemy_pieces_bb: Bitboard, + piece_type: Piece, + piece_bb: BitBoard, + friendly_pieces_bb: BitBoard, + enemy_pieces_bb: BitBoard, ) -> Vec { let mut moves: Vec = Vec::new(); @@ -66,12 +66,12 @@ pub fn get_moves_for_non_slider_piece( } pub fn calculate_slider_move_bitboard( - piece_type: PieceType, + piece_type: Piece, square: Square, - friendly_pieces_bitboard: Bitboard, - enemy_pieces_bitboard: Bitboard, -) -> Bitboard { - let mut move_bitboard: Bitboard = Bitboard::new(); + friendly_pieces_bitboard: BitBoard, + enemy_pieces_bitboard: BitBoard, +) -> BitBoard { + let mut move_bitboard: BitBoard = BitBoard::new(); for direction in SlideDirection::directions_for_piece_type(piece_type) { move_bitboard |= calculate_max_slide_range( square, @@ -88,10 +88,10 @@ pub fn calculate_slider_move_bitboard( fn calculate_max_slide_range( square: Square, direction: SlideDirection, - friendly_pieces_bitboard: Bitboard, - enemy_pieces_bitboard: Bitboard, -) -> Bitboard { - let mut result = Bitboard::new(); + friendly_pieces_bitboard: BitBoard, + enemy_pieces_bitboard: BitBoard, +) -> BitBoard { + let mut result = BitBoard::new(); let mut next = direction.next(square); while next.is_valid() && !friendly_pieces_bitboard.get_square(next) { result.fill_square(next); diff --git a/src/backend/perft.rs b/src/backend/perft.rs index fd7c5ec..767892c 100644 --- a/src/backend/perft.rs +++ b/src/backend/perft.rs @@ -1,8 +1,8 @@ use crate::backend::movegen::check_decider::is_in_check; use crate::backend::movegen::move_gen::get_pseudo_legal_moves; -use crate::backend::state::game::game_state::GameState; +use crate::backend::state::game::state::State; -pub fn perft(game_state: &mut GameState, depth: u8) -> u64 { +pub fn perft(state: &State, depth: u8) -> u64 { // PERFT: I would love to make this work, but it does not atm. // let moves = get_moves(game_state); // if depth == 1 { @@ -12,18 +12,16 @@ pub fn perft(game_state: &mut GameState, depth: u8) -> u64 { return 1; } - let moves = get_pseudo_legal_moves(game_state); + let moves = get_pseudo_legal_moves(state); let mut nodes = 0; for chess_move in moves { - game_state.make_move(chess_move); + let next_state = state.make_move(chess_move); // If we are in check after making the move -> skip. - if is_in_check(game_state, game_state.active_color().opposite()) { - game_state.unmake_move(chess_move); + if is_in_check(&next_state, next_state.active_color().opposite()) { continue; } - nodes += perft(game_state, depth - 1); - game_state.unmake_move(chess_move); + nodes += perft(&next_state, depth - 1); } nodes diff --git a/src/backend/state/board/bb_manager.rs b/src/backend/state/board/bb_manager.rs new file mode 100644 index 0000000..1bee8cb --- /dev/null +++ b/src/backend/state/board/bb_manager.rs @@ -0,0 +1,80 @@ +use crate::backend::constants::PIECE_TYPE_COUNT; +use crate::backend::state::board::bitboard::BitBoard; +use crate::backend::state::piece::ALL_PIECES; +use crate::backend::state::piece::{Piece, Side}; +use crate::backend::state::square::Square; + +/// A struct that manages bitboards used for representing chess pieces and their positions on a chessboard. +/// # Fields +/// - `bitboards`: An array of bitboards where each entry represents the board state +/// +/// - `bitboard_index_to_piece`: An array that maps each index in the `bitboards` array +/// back to its corresponding `Piece`. +#[derive(Debug, Clone)] +pub struct BBManager { + white_bb: BitBoard, + black_bb: BitBoard, + piece_bbs: [BitBoard; PIECE_TYPE_COUNT], +} + +impl BBManager { + /// Generates a new BitBoardManager with all bitboards set to empty. + pub fn new() -> BBManager { + BBManager { + white_bb: BitBoard::new(), + black_bb: BitBoard::new(), + piece_bbs: [BitBoard::new(); PIECE_TYPE_COUNT], + } + } + + /// Retrieves a mutable reference to the bitboard associated with the given piece. + pub fn get_piece_bb_mut(&mut self, piece_type: Piece) -> &mut BitBoard { + &mut self.piece_bbs[piece_type as usize] + } + + /// Retrieves a copy of the `BitBoard` associated with the specified `Piece`. + pub fn get_piece_bb(&self, piece_type: Piece) -> BitBoard { + self.piece_bbs[piece_type as usize] + } + + /// Returns a `BitBoard` containing all the positions currently occupied by pieces + /// of the specified color. + pub fn get_all_pieces_bb_off(&self, color: Side) -> BitBoard { + match color { + Side::White => self.white_bb, + Side::Black => self.black_bb, + } + } + + /// Returns a `BitBoard` containing all the positions currently occupied by pieces + /// of the specified color. + pub fn get_all_pieces_bb_off_mut(&mut self, color: Side) -> &mut BitBoard { + match color { + Side::White => &mut self.white_bb, + Side::Black => &mut self.black_bb, + } + } + + pub fn get_colored_piece_bb(&self, piece_type: Piece, color: Side) -> BitBoard { + let piece_bb = self.get_piece_bb(piece_type); + let color_bb = self.get_all_pieces_bb_off(color); + piece_bb & color_bb + } + + /// Retrieves the piece located at a specific square on the chessboard. + pub fn get_piece_at_square(&self, square: Square) -> Option { + for (index, piece) in ALL_PIECES.iter().enumerate().take(self.piece_bbs.len()) { + let bitboard = self.piece_bbs[index]; + if bitboard.get_square(square) { + return Some(*piece); + } + } + None + } +} + +impl Default for BBManager { + fn default() -> Self { + Self::new() + } +} diff --git a/src/backend/state/board/bitboard.rs b/src/backend/state/board/bitboard.rs index 2a7bc3a..04d4f7f 100644 --- a/src/backend/state/board/bitboard.rs +++ b/src/backend/state/board/bitboard.rs @@ -9,20 +9,26 @@ use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, N /// # Fields /// - `value` (`u64`): The underlying 64-bit integer used to store the board's state. #[derive(Copy, Clone, Debug)] -pub struct Bitboard { +pub struct BitBoard { value: u64, } -impl Bitboard { +impl BitBoard { /// Creates a new `BitBoard` instance with an initial value of 0. /// This can't be converted to a default variant because I need it to be const. #[allow(clippy::new_without_default)] pub const fn new() -> Self { - Bitboard { value: 0 } + BitBoard { value: 0 } + } + + /// Converts a given `Square` into a corresponding bitboard. + const fn new_from_square(square: Square) -> BitBoard { + let index = square.square_to_index(); + BitBoard { value: 1 << index } } pub const fn new_from_squares(squares: &[Square]) -> Self { - let mut bitboard = Bitboard::new(); + let mut bitboard = BitBoard::new(); let mut index = 0; while index < squares.len() { @@ -34,7 +40,7 @@ impl Bitboard { } pub const fn new_from_rank(rank: i8) -> Self { - let mut bitboard = Bitboard::new(); + let mut bitboard = BitBoard::new(); let mut file = 0; while file < FILES_AMOUNT { @@ -72,8 +78,8 @@ impl Bitboard { /// * `true` if the square is occupied. /// * `false` if the square is unoccupied. pub fn get_square(&self, square: Square) -> bool { - let index = Self::square_to_bitmask(square); - let bitboard_after_mask = self.value & index; + let index = Self::new_from_square(square); + let bitboard_after_mask = self.value & index.value; bitboard_after_mask != 0 } @@ -83,8 +89,8 @@ impl Bitboard { /// /// * `square` - A `Square` instance representing the square to be marked as filled. pub const fn fill_square(&mut self, square: Square) { - let bit = Self::square_to_bitmask(square); - self.value |= bit; + let bit = Self::new_from_square(square); + self.value |= bit.value; } /// Marks a square on a board as empty by setting the corresponding bit in the `value` field. @@ -93,27 +99,12 @@ impl Bitboard { /// /// * `square` - A `Square` instance representing the square to be marked as filled. pub fn clear_square(&mut self, square: Square) { - let bit = Self::square_to_bitmask(square); - self.value ^= bit; - } - - /// Converts a given `Square` into a corresponding 64-bit bitmask. - /// - /// # Parameters - /// - `square`: A `Square` object that represents a position or coordinate - /// which can be converted into a bitmask. - /// - /// # Returns - /// - A `u64` value representing the bitmask corresponding to the input `Square`. - /// The bitmask will have exactly one bit set, corresponding to the index - /// value of the `Square`. - const fn square_to_bitmask(square: Square) -> u64 { - let index = square.square_to_index(); - 1 << index + let bit = Self::new_from_square(square); + self.value ^= bit.value; } } -impl Iterator for Bitboard { +impl Iterator for BitBoard { type Item = Square; fn next(&mut self) -> Option { @@ -130,71 +121,65 @@ impl Iterator for Bitboard { } // Various bitwise operations on BitBoards. -impl Not for Bitboard { +impl Not for BitBoard { type Output = Self; fn not(self) -> Self::Output { - Bitboard { value: !self.value } + BitBoard { value: !self.value } } } -impl BitAnd for Bitboard { +impl BitAnd for BitBoard { type Output = Self; fn bitand(self, rhs: Self) -> Self::Output { - Bitboard { + BitBoard { value: self.value & rhs.value, } } } -impl BitAndAssign for Bitboard { +impl BitAndAssign for BitBoard { fn bitand_assign(&mut self, rhs: Self) { self.value &= rhs.value; } } -impl BitOr for Bitboard { +impl BitOr for BitBoard { type Output = Self; fn bitor(self, rhs: Self) -> Self::Output { - Bitboard { + BitBoard { value: self.value | rhs.value, } } } -impl BitOrAssign for &mut Bitboard { - fn bitor_assign(&mut self, rhs: Bitboard) { - self.value |= rhs.value; - } -} - -impl BitOrAssign for Bitboard { +impl BitOrAssign for BitBoard { fn bitor_assign(&mut self, rhs: Self) { self.value |= rhs.value; } } -impl BitXor for Bitboard { +impl BitXor for BitBoard { type Output = Self; fn bitxor(self, rhs: Self) -> Self::Output { - Bitboard { + BitBoard { value: self.value ^ rhs.value, } } } -impl BitXorAssign for &mut Bitboard { - fn bitxor_assign(&mut self, rhs: Bitboard) { +impl BitXorAssign for BitBoard { + fn bitxor_assign(&mut self, rhs: BitBoard) { self.value ^= rhs.value; } } // Display implementation for BitBoards - useful for debugging. // Sadly, RustRover does not display them when debugging. -impl Display for Bitboard { +impl Display for BitBoard { /// Formats the bitboard (`self`) into a string representation, /// can be used for debugging purposes. /// diff --git a/src/backend/state/board/bitboard_manager.rs b/src/backend/state/board/bitboard_manager.rs deleted file mode 100644 index d3226f3..0000000 --- a/src/backend/state/board/bitboard_manager.rs +++ /dev/null @@ -1,201 +0,0 @@ -use crate::backend::constants::{PIECE_TYPE_COUNT, SIDES}; -use crate::backend::state::board::bitboard::Bitboard; -use crate::backend::state::piece::{Piece, PieceColor, PieceType}; -use crate::backend::state::square::Square; - -const WHITE_START_INDEX: usize = 0; -const WHITE_END_INDEX: usize = 5; -const BLACK_START_INDEX: usize = 6; -const BLACK_END_INDEX: usize = 11; - -/// A struct that manages bitboards used for representing chess pieces and their positions on a chessboard. -/// # Fields -/// - `bitboards`: An array of bitboards where each entry represents the board state -/// -/// - `bitboard_index_to_piece`: An array that maps each index in the `bitboards` array -/// back to its corresponding `Piece`. -#[derive(Debug)] -pub struct BitboardManager { - bitboards: [Bitboard; PIECE_TYPE_COUNT * SIDES], - bitboard_index_to_piece: [Piece; PIECE_TYPE_COUNT * SIDES], -} - -impl BitboardManager { - /// Generates a new BitBoardManager with all bitboards set to empty. - pub fn new() -> BitboardManager { - // This contains all the bitboards. - let bitboards = [Bitboard::new(); PIECE_TYPE_COUNT * SIDES]; - - // This maps each index in the bitboard array back to its corresponding piece. - // I'm sure there is a better way to do this, but I'm not sure how. - let mut bitboard_index_to_piece = - [Piece::new(PieceType::Pawn, PieceColor::White); PIECE_TYPE_COUNT * SIDES]; - - // insert all the white pieces - bitboard_index_to_piece[0] = Piece::new(PieceType::Pawn, PieceColor::White); - bitboard_index_to_piece[1] = Piece::new(PieceType::Rook, PieceColor::White); - bitboard_index_to_piece[2] = Piece::new(PieceType::Knight, PieceColor::White); - bitboard_index_to_piece[3] = Piece::new(PieceType::Bishop, PieceColor::White); - bitboard_index_to_piece[4] = Piece::new(PieceType::Queen, PieceColor::White); - bitboard_index_to_piece[5] = Piece::new(PieceType::King, PieceColor::White); - - // insert all the black pieces - bitboard_index_to_piece[6] = Piece::new(PieceType::Pawn, PieceColor::Black); - bitboard_index_to_piece[7] = Piece::new(PieceType::Rook, PieceColor::Black); - bitboard_index_to_piece[8] = Piece::new(PieceType::Knight, PieceColor::Black); - bitboard_index_to_piece[9] = Piece::new(PieceType::Bishop, PieceColor::Black); - bitboard_index_to_piece[10] = Piece::new(PieceType::Queen, PieceColor::Black); - bitboard_index_to_piece[11] = Piece::new(PieceType::King, PieceColor::Black); - - BitboardManager { - bitboards, - bitboard_index_to_piece, - } - } - - /// Retrieves a mutable reference to the bitboard associated with the given piece. - /// - /// # Arguments - /// * `piece` - The `Piece` for which the mutable reference to the bitboard is requested. - /// - /// # Returns - /// - /// A mutable reference to the `BitBoard` corresponding to the given `piece`. - pub fn get_bitboard_mut(&mut self, piece: Piece) -> &mut Bitboard { - let index = self.piece_to_bitboards_index(piece); - &mut self.bitboards[index] - } - - /// Retrieves a reference to the `BitBoard` associated with the specified `Piece`. - /// - /// # Parameters: - /// - `piece`: The piece for which the associated `BitBoard` - /// is being retrieved. - /// - /// # Returns: - /// - A reference to the `BitBoard` corresponding to the given `Piece`. - pub fn get_bitboard(&self, piece: Piece) -> &Bitboard { - let index = self.piece_to_bitboards_index(piece); - &self.bitboards[index] - } - - /// Retrieves the piece located at a specific square on the chessboard. - /// - /// # Parameters - /// - `square`: The square for which the piece is to be retrieved. - /// - /// # Returns - /// - `Some(Piece)` if there is a piece located at the given square. - /// - `None` if the square is empty. - pub fn get_piece_at_square(&self, square: Square) -> Option { - let index = self.get_index_for_piece_at_square(square)?; - Some(self.bitboard_index_to_piece[index]) - } - - /// Retrieves a mutable reference to the `BitBoard` corresponding to the piece located at the specified square. - /// - /// # Arguments - /// - /// * `square` - A `Square` that specifies the location of the piece whose `BitBoard` is - /// to be retrieved. - /// - /// # Returns - /// - /// * `Some(&mut BitBoard)` - A mutable reference to the `BitBoard - pub fn get_bitboard_for_piece_at_square_mut( - &mut self, - square: Square, - ) -> Option<&mut Bitboard> { - let index = self.get_index_for_piece_at_square(square)?; - Some(&mut self.bitboards[index]) - } - - /// Returns the `BitBoard` associated with a piece at the specified `square` if it exists. - /// - /// # Arguments - /// - /// * `square` - A `Square` representing the position on the chessboard where the piece is expected to be located. - /// - /// # Returns - /// - /// * `Option` - Returns `Some(BitBoard)` if a piece is present at the specified square, otherwise returns `None`. - pub fn get_bitboard_for_piece_at_square(&self, square: Square) -> Option { - let index = self.get_index_for_piece_at_square(square)?; - Some(self.bitboards[index]) - } - - /// Returns a `BitBoard` containing all the positions currently occupied by pieces - /// of the specified color. - /// - /// # Arguments - /// - /// * `color` - A `PieceColor` enum value (`PieceColor::White` or `PieceColor::Black`) - /// indicating the color of the pieces whose positions should be retrieved. - /// - /// # Returns - /// - /// A `BitBoard` that aggregates all the positions of the specified color's pieces. - pub fn get_all_pieces_off(&self, color: PieceColor) -> Bitboard { - let mut resulting_bitboard = Bitboard::new(); - - // This is quite ugly... - let start_index = match color { - PieceColor::White => WHITE_START_INDEX, - PieceColor::Black => BLACK_START_INDEX, - }; - - let end_index = match color { - PieceColor::White => WHITE_END_INDEX, - PieceColor::Black => BLACK_END_INDEX, - }; - - // Simply loop over all relevant bitboards and add them to the resulting bitboard. - // PERF: Instead of generating this when it's necessary, we could generate it once and - // incrementally update it. - // Then again, according to the benchmarks, this is not a bottleneck. - for index in start_index..=end_index { - resulting_bitboard |= self.bitboards[index]; - } - - resulting_bitboard - } - - /// Used by `get_bitboard_for_piece_at_square(_mut)` and `get_piece_at_square`. - /// Returns the index of the piece at that square if there is one. - fn get_index_for_piece_at_square(&self, square: Square) -> Option { - for index in 0..self.bitboards.len() { - let bitboard = self.bitboards[index]; - if bitboard.get_square(square) { - return Some(index); - } - } - None - } - - /// Converts a `Piece` instance into a corresponding index for bitboard representation. - /// - /// # Parameters - /// - `piece`: A `Piece` instance for which the bitboard index needs to be determined. - /// - /// # Returns - /// - `usize`: The index corresponding to the given `piece` in the bitboard. - fn piece_to_bitboards_index(&self, piece: Piece) -> usize { - let mut index = 0; - - // this causes the index to be at 0 for white and 6 for black - // it works because white as usize == 0 and black as usize == 1 - index += piece.piece_color() as usize * PIECE_TYPE_COUNT; - // this adds 0 for a pawn, 1 for a rook, etc. - index += piece.piece_type() as usize; - // the indices are - // white: pawn: 0, rook: 1, knight: 2, bishop: 3, queen: 4, king: 5 - // black: pawn: 6, rook: 7, knight: 8, bishop: 9, queen: 10, king: 11 - index - } -} - -impl Default for BitboardManager { - fn default() -> Self { - Self::new() - } -} diff --git a/src/backend/state/board/mod.rs b/src/backend/state/board/mod.rs index 7f7ff55..6614fa1 100644 --- a/src/backend/state/board/mod.rs +++ b/src/backend/state/board/mod.rs @@ -1,2 +1,2 @@ +pub mod bb_manager; pub mod bitboard; -pub mod bitboard_manager; diff --git a/src/backend/state/game/fen_parser.rs b/src/backend/state/game/fen_parser.rs index eedf7d2..1a46049 100644 --- a/src/backend/state/game/fen_parser.rs +++ b/src/backend/state/game/fen_parser.rs @@ -1,6 +1,8 @@ -use crate::backend::state::board::bitboard_manager::BitboardManager; +use crate::backend::state::board::bb_manager::BBManager; use crate::backend::state::game::irreversible_data::IrreversibleData; -use crate::backend::state::piece::{Piece, PieceColor, PieceType}; +use crate::backend::state::piece::Piece::{Bishop, King, Knight, Pawn, Queen, Rook}; +use crate::backend::state::piece::Side::{Black, White}; +use crate::backend::state::piece::{Piece, Side}; use crate::backend::state::square::Square; /// Parses a FEN (Forsyth-Edwards Notation) string and updates the corresponding game state. @@ -15,8 +17,8 @@ use crate::backend::state::square::Square; /// * `half_move_clock` - A mutable reference to a `u16` to update the current half-move clock count. pub fn parse_fen( fen_string: &str, - bit_board_manager: &mut BitboardManager, - active_color: &mut PieceColor, + bit_board_manager: &mut BBManager, + active_color: &mut Side, irreversible_data: &mut IrreversibleData, half_move_clock: &mut u16, ) { @@ -88,17 +90,17 @@ fn parse_castling_rights(irreversible_data: &mut IrreversibleData, castling_righ } } -fn parse_active_color(active_color: &mut PieceColor, active_color_string: &str) { +fn parse_active_color(active_color: &mut Side, active_color_string: &str) { match active_color_string { - "w" => *active_color = PieceColor::White, - "b" => *active_color = PieceColor::Black, + "w" => *active_color = Side::White, + "b" => *active_color = Side::Black, _ => { panic!("Invalid character in FEN string"); } } } -fn parse_position(bit_board_manager: &mut BitboardManager, positions_string: &str) { +fn parse_position(bit_board_manager: &mut BBManager, positions_string: &str) { let mut file = 0; let mut rank = 7; for char in positions_string.chars() { @@ -111,75 +113,51 @@ fn parse_position(bit_board_manager: &mut BitboardManager, positions_string: &st rank -= 1; } 'P' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Pawn, PieceColor::White)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Pawn, White, Square::new(file, rank)); file += 1; } 'p' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Pawn, PieceColor::Black)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Pawn, Black, Square::new(file, rank)); file += 1; } 'R' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Rook, PieceColor::White)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Rook, White, Square::new(file, rank)); file += 1; } 'r' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Rook, PieceColor::Black)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Rook, Black, Square::new(file, rank)); file += 1; } 'N' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Knight, PieceColor::White)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Knight, White, Square::new(file, rank)); file += 1; } 'n' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Knight, PieceColor::Black)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Knight, Black, Square::new(file, rank)); file += 1; } 'B' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Bishop, PieceColor::White)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Bishop, White, Square::new(file, rank)); file += 1; } 'b' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Bishop, PieceColor::Black)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Bishop, Black, Square::new(file, rank)); file += 1; } 'Q' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Queen, PieceColor::White)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Queen, White, Square::new(file, rank)); file += 1; } 'q' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::Queen, PieceColor::Black)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, Queen, Black, Square::new(file, rank)); file += 1; } 'K' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::King, PieceColor::White)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, King, White, Square::new(file, rank)); file += 1; } 'k' => { - bit_board_manager - .get_bitboard_mut(Piece::new(PieceType::King, PieceColor::Black)) - .fill_square(Square::new(file, rank)); + fill_square(bit_board_manager, King, Black, Square::new(file, rank)); file += 1; } _ => { @@ -187,4 +165,16 @@ fn parse_position(bit_board_manager: &mut BitboardManager, positions_string: &st } } } + + fn fill_square( + bb_manager: &mut BBManager, + piece_type: Piece, + piece_color: Side, + square: Square, + ) { + bb_manager.get_piece_bb_mut(piece_type).fill_square(square); + bb_manager + .get_all_pieces_bb_off_mut(piece_color) + .fill_square(square); + } } diff --git a/src/backend/state/game/game_state.rs b/src/backend/state/game/game_state.rs deleted file mode 100644 index fe86f40..0000000 --- a/src/backend/state/game/game_state.rs +++ /dev/null @@ -1,303 +0,0 @@ -use crate::backend::movegen::moove::{CastleType, Moove}; -use crate::backend::state::board::bitboard::Bitboard; -use crate::backend::state::board::bitboard_manager::BitboardManager; -use crate::backend::state::game::fen_parser::parse_fen; -use crate::backend::state::game::irreversible_data::IrreversibleData; -use crate::backend::state::piece::PieceType::{King, Pawn, Rook}; -use crate::backend::state::piece::{Piece, PieceColor, PieceType}; -use crate::backend::state::square::{A1, A8, D1, D8, F1, F8, H1, H8, Square}; -use getset::{CloneGetters, Getters, MutGetters}; - -const ROOK_SWAP_WHITE_LONG_CASTLE_BB: Bitboard = Bitboard::new_from_squares(&[A1, D1]); -const ROOK_SWAP_WHITE_SHORT_CASTLE_BB: Bitboard = Bitboard::new_from_squares(&[H1, F1]); -const ROOK_SWAP_BLACK_LONG_CASTLE_BB: Bitboard = Bitboard::new_from_squares(&[A8, D8]); -const ROOK_SWAP_BLACK_SHORT_CASTLE_BB: Bitboard = Bitboard::new_from_squares(&[H8, F8]); - -#[derive(Debug, Getters, MutGetters, CloneGetters)] -pub struct GameState { - #[getset(get = "pub")] - bb_manager: BitboardManager, - #[getset(get = "pub")] - irreversible_data_stack: Vec, - #[getset(get_clone = "pub")] - active_color: PieceColor, - #[getset(get = "pub")] - half_move_clock: u16, -} - -impl GameState { - /// Creates a new `GameState` instance with default values. - pub fn new() -> GameState { - GameState { - bb_manager: BitboardManager::new(), - active_color: PieceColor::White, - irreversible_data_stack: vec![IrreversibleData::new_with_castling_true()], - half_move_clock: 0, - } - } - - /// Creates a new `GameState` instance based on the fen string. - pub fn new_from_fen(fen_string: &str) -> GameState { - let mut bit_board_manager = BitboardManager::new(); - let mut active_color = PieceColor::White; - let mut irreversible_data = IrreversibleData::new(); - let mut half_move_clock = 0; - - parse_fen( - fen_string, - &mut bit_board_manager, - &mut active_color, - &mut irreversible_data, - &mut half_move_clock, - ); - - GameState { - bb_manager: bit_board_manager, - active_color, - irreversible_data_stack: vec![irreversible_data], - half_move_clock, - } - } - - /// Executes a move. - /// - /// # Arguments - /// - /// * `chess_move` - A `Moove` object representing the move to be made. - pub fn make_move(&mut self, moove: Moove) { - // The new irreversible data. - let mut irreversible_data = - IrreversibleData::new_from_previous_state(self.irreversible_data_stack.last().unwrap()); - - // Get the type of moved piece. - let moved_piece = self.bb_manager.get_piece_at_square(moove.from()).unwrap(); - - // Usually the square something was captured on (if something was captured at all) is the square we moved to... - let mut capture_square = moove.to(); - if moved_piece.piece_type() == Pawn { - // ... unless this is an en passant capture, we then need to update the capture square. - self.make_move_ep_capture(moove, &mut capture_square); - // Check if a double pawn push was played and store the en passant file - self.make_move_double_pawn_push(moove, &mut irreversible_data); - } - - // If something was captured, remove the piece and update irreversible data. - self.make_move_capture(&mut irreversible_data, capture_square); - - // Get the bitboard for the piece that was moved. - let mut moved_piece_bitboard = self.bb_manager.get_bitboard_mut(moved_piece); - - // Clear the square that the piece was moved from. - moved_piece_bitboard.clear_square(moove.from()); - - // Update the moved piece bb if it was a pawn promotion - match moove.promotion_type() { - None => {} - Some(promotion_type) => { - moved_piece_bitboard = self - .bb_manager - .get_bitboard_mut(Piece::new(promotion_type, self.active_color)); - } - } - // Fill the square it moved to. - moved_piece_bitboard.fill_square(moove.to()); - - // Some special king handling - if moved_piece.piece_type() == King { - self.make_move_king(moove, &mut irreversible_data); - } - - self.make_move_castling_rights_on_rook_move_or_capture( - &mut irreversible_data, - moved_piece.piece_type(), - moove.from(), - self.active_color, - ); - - // Take care of some basics. - self.active_color = self.active_color.opposite(); - self.irreversible_data_stack.push(irreversible_data); - } - - fn make_move_ep_capture(&mut self, moove: Moove, capture_square: &mut Square) { - let ep_square = self - .irreversible_data_stack - .last() - .unwrap() - .en_passant_square(); - - // if an en passant square exists - if let Some(ep_square) = ep_square - // and if we moved to the ep_square - && ep_square == moove.to() - { - // update the captured square to the ep_square - offset - *capture_square = moove.to().back_by_one(self.active_color); - } - } - - fn make_move_capture( - &mut self, - irreversible_data: &mut IrreversibleData, - capture_square: Square, - ) { - // Get the type of the captured piece if it exists. - let captured_piece = self.bb_manager.get_piece_at_square(capture_square); - // Clear the square on the captured piece's bitboard if it exists. - if let Some(captured_piece) = captured_piece { - // Store the captured piece type in the irreversible data. - irreversible_data.set_captured_piece(Some(captured_piece.piece_type())); - // Remove the captured piece from its bitboard. - let captured_piece_bitboard = self.bb_manager.get_bitboard_mut(captured_piece); - captured_piece_bitboard.clear_square(capture_square); - - // Remove castling rights if the captured piece was a rook on its starting square - self.make_move_castling_rights_on_rook_move_or_capture( - irreversible_data, - captured_piece.piece_type(), - capture_square, - self.active_color.opposite(), - ) - } - } - - fn make_move_double_pawn_push( - &mut self, - moove: Moove, - irreversible_data: &mut IrreversibleData, - ) { - if moove.is_double_pawn_push() { - // the pawn starting square and one forward - let ep_square = moove.to().back_by_one(self.active_color); - - irreversible_data.set_en_passant_square(Some(ep_square)); - } - } - - fn make_move_king(&mut self, moove: Moove, irreversible_data: &mut IrreversibleData) { - // If the king moved we can't castle anymore - irreversible_data.remove_long_castle_rights(self.active_color); - irreversible_data.remove_short_castle_rights(self.active_color); - - // If we castled, we need to move the rook - if moove.is_castle() { - let mut rook_bb = self - .bb_manager - .get_bitboard_mut(Piece::new(Rook, self.active_color)); - let rook_swap_bb = Self::get_rook_swap_bb(moove.get_castle_type(), self.active_color); - rook_bb ^= rook_swap_bb; - } - } - - fn get_rook_swap_bb(castle_type: CastleType, active_color: PieceColor) -> Bitboard { - match castle_type { - CastleType::Long => match active_color { - PieceColor::White => ROOK_SWAP_WHITE_LONG_CASTLE_BB, - PieceColor::Black => ROOK_SWAP_BLACK_LONG_CASTLE_BB, - }, - CastleType::Short => match active_color { - PieceColor::White => ROOK_SWAP_WHITE_SHORT_CASTLE_BB, - PieceColor::Black => ROOK_SWAP_BLACK_SHORT_CASTLE_BB, - }, - } - } - - fn make_move_castling_rights_on_rook_move_or_capture( - &mut self, - irreversible_data: &mut IrreversibleData, - piece_type: PieceType, - relevant_square: Square, - relevant_side: PieceColor, - ) { - if piece_type == Rook { - for castling_type in CastleType::get_all_types() { - let starting_square = Self::get_rook_starting_square(castling_type, relevant_side); - if relevant_square == starting_square { - irreversible_data.remove_castle_rights(relevant_side, castling_type); - } - } - } - } - - fn get_rook_starting_square(castle_type: CastleType, color: PieceColor) -> Square { - match castle_type { - CastleType::Long => match color { - PieceColor::White => A1, - PieceColor::Black => A8, - }, - CastleType::Short => match color { - PieceColor::White => H1, - PieceColor::Black => H8, - }, - } - } - - /// Reverts the last move made, restoring the board state to what it was - /// before the move. - /// - /// # Arguments - /// * `chess_move` - A `Moove` struct representing the chess move that needs to be reverted. - pub fn unmake_move(&mut self, moove: Moove) { - // Flip whose turn it is. - self.active_color = self.active_color.opposite(); - // Get the last irreversible data. - let irreversible_data = self.irreversible_data_stack.pop().unwrap(); - - let moved_piece = self.bb_manager.get_piece_at_square(moove.to()).unwrap(); - // Get the bitboard for the piece that was moved. - let mut moved_piece_bitboard = self.bb_manager.get_bitboard_mut(moved_piece); - - // Clear the square it moved to. - moved_piece_bitboard.clear_square(moove.to()); - - // Update the moved piece bb if it was a pawn promotion - match moove.promotion_type() { - None => {} - Some(_) => { - moved_piece_bitboard = self - .bb_manager - .get_bitboard_mut(Piece::new(Pawn, self.active_color)); - } - } - - // Fill the square that the piece was moved from. - moved_piece_bitboard.fill_square(moove.from()); - - // Some special king handling - if moved_piece.piece_type() == PieceType::King && moove.is_castle() { - let mut rook_bb = self - .bb_manager - .get_bitboard_mut(Piece::new(Rook, self.active_color)); - let rook_swap_bb = Self::get_rook_swap_bb(moove.get_castle_type(), self.active_color); - rook_bb ^= rook_swap_bb; - } - - // If some piece was captured, put it back on the board. - if let Some(captured_piece) = irreversible_data.captured_piece() { - let piece = Piece::new(captured_piece, self.active_color.opposite()); - let bitboard = self.bb_manager.get_bitboard_mut(piece); - - let mut capture_square = moove.to(); - // en passant - let en_passant_square = self - .irreversible_data_stack - .last() - .unwrap() - .en_passant_square(); - - if let Some(en_passant_square) = en_passant_square - && moove.to() == en_passant_square - { - capture_square = moove.to().back_by_one(self.active_color); - } - - bitboard.fill_square(capture_square); - } - } -} - -impl Default for GameState { - fn default() -> Self { - Self::new() - } -} diff --git a/src/backend/state/game/irreversible_data.rs b/src/backend/state/game/irreversible_data.rs index 80eab9c..e04503c 100644 --- a/src/backend/state/game/irreversible_data.rs +++ b/src/backend/state/game/irreversible_data.rs @@ -1,16 +1,14 @@ use crate::backend::movegen::moove::CastleType; -use crate::backend::state::piece::{PieceColor, PieceType}; +use crate::backend::state::piece::{Piece, Side}; use crate::backend::state::square::Square; use getset::{CloneGetters, Setters}; /// The `IrreversibleData` struct stores data that is irreversible. /// For example, this remembers what kind of piece was captured for `unmake_move()`. -#[derive(CloneGetters, Setters, Debug)] +#[derive(CloneGetters, Setters, Debug, Clone)] pub struct IrreversibleData { #[getset(get_clone = "pub", set = "pub")] - half_move_clock: u8, - #[getset(get_clone = "pub", set = "pub")] - captured_piece: Option, + captured_piece: Option, #[getset(get_clone = "pub", set = "pub")] en_passant_square: Option, #[getset(get_clone = "pub", set = "pub")] @@ -26,7 +24,6 @@ pub struct IrreversibleData { impl IrreversibleData { pub fn new() -> IrreversibleData { IrreversibleData { - half_move_clock: 0, captured_piece: None, en_passant_square: None, white_long_castle_rights: false, @@ -38,7 +35,6 @@ impl IrreversibleData { pub fn new_with_castling_true() -> IrreversibleData { IrreversibleData { - half_move_clock: 0, captured_piece: None, en_passant_square: None, white_long_castle_rights: true, @@ -50,7 +46,6 @@ impl IrreversibleData { pub fn new_from_previous_state(previous_state: &IrreversibleData) -> IrreversibleData { IrreversibleData { - half_move_clock: previous_state.half_move_clock + 1, captured_piece: None, en_passant_square: None, white_long_castle_rights: previous_state.white_long_castle_rights, @@ -60,35 +55,35 @@ impl IrreversibleData { } } - pub fn get_long_castle_rights(&self, color: PieceColor) -> bool { + pub fn get_long_castle_rights(&self, color: Side) -> bool { match color { - PieceColor::White => self.white_long_castle_rights, - PieceColor::Black => self.black_long_castle_rights, + Side::White => self.white_long_castle_rights, + Side::Black => self.black_long_castle_rights, } } - pub fn get_short_castle_rights(&self, color: PieceColor) -> bool { + pub fn get_short_castle_rights(&self, color: Side) -> bool { match color { - PieceColor::White => self.white_short_castle_rights, - PieceColor::Black => self.black_short_castle_rights, + Side::White => self.white_short_castle_rights, + Side::Black => self.black_short_castle_rights, } } - pub fn remove_long_castle_rights(&mut self, color: PieceColor) { + pub fn remove_long_castle_rights(&mut self, color: Side) { match color { - PieceColor::White => self.white_long_castle_rights = false, - PieceColor::Black => self.black_long_castle_rights = false, + Side::White => self.white_long_castle_rights = false, + Side::Black => self.black_long_castle_rights = false, } } - pub fn remove_short_castle_rights(&mut self, color: PieceColor) { + pub fn remove_short_castle_rights(&mut self, color: Side) { match color { - PieceColor::White => self.white_short_castle_rights = false, - PieceColor::Black => self.black_short_castle_rights = false, + Side::White => self.white_short_castle_rights = false, + Side::Black => self.black_short_castle_rights = false, } } - pub fn remove_castle_rights(&mut self, color: PieceColor, castle_type: CastleType) { + pub fn remove_castle_rights(&mut self, color: Side, castle_type: CastleType) { match castle_type { CastleType::Long => self.remove_long_castle_rights(color), CastleType::Short => self.remove_short_castle_rights(color), diff --git a/src/backend/state/game/mod.rs b/src/backend/state/game/mod.rs index a318ad6..336a647 100644 --- a/src/backend/state/game/mod.rs +++ b/src/backend/state/game/mod.rs @@ -1,3 +1,3 @@ mod fen_parser; -pub mod game_state; pub mod irreversible_data; +pub mod state; diff --git a/src/backend/state/game/state.rs b/src/backend/state/game/state.rs new file mode 100644 index 0000000..ee9d1f0 --- /dev/null +++ b/src/backend/state/game/state.rs @@ -0,0 +1,247 @@ +use crate::backend::movegen::moove::{CastleType, Moove}; +use crate::backend::state::board::bb_manager::BBManager; +use crate::backend::state::board::bitboard::BitBoard; +use crate::backend::state::game::fen_parser::parse_fen; +use crate::backend::state::game::irreversible_data::IrreversibleData; +use crate::backend::state::piece::Piece::{King, Pawn, Rook}; +use crate::backend::state::piece::{Piece, Side}; +use crate::backend::state::square::{A1, A8, D1, D8, F1, F8, H1, H8, Square}; +use getset::{CloneGetters, Getters, MutGetters}; + +const ROOK_SWAP_WHITE_LONG_CASTLE_BB: BitBoard = BitBoard::new_from_squares(&[A1, D1]); +const ROOK_SWAP_WHITE_SHORT_CASTLE_BB: BitBoard = BitBoard::new_from_squares(&[H1, F1]); +const ROOK_SWAP_BLACK_LONG_CASTLE_BB: BitBoard = BitBoard::new_from_squares(&[A8, D8]); +const ROOK_SWAP_BLACK_SHORT_CASTLE_BB: BitBoard = BitBoard::new_from_squares(&[H8, F8]); + +#[derive(Debug, Getters, MutGetters, CloneGetters, Clone)] +pub struct State { + #[getset(get = "pub")] + bb_manager: BBManager, + #[getset(get = "pub")] + irreversible_data: IrreversibleData, + #[getset(get_clone = "pub")] + active_color: Side, + #[getset(get = "pub")] + half_move_clock: u16, +} + +impl State { + /// Creates a new `GameState` instance with default values. + pub fn new() -> State { + State { + bb_manager: BBManager::new(), + active_color: Side::White, + irreversible_data: IrreversibleData::new_with_castling_true(), + half_move_clock: 0, + } + } + + /// Creates a new `GameState` instance based on the fen string. + pub fn new_from_fen(fen_string: &str) -> State { + let mut bb_manager = BBManager::new(); + let mut active_color = Side::White; + let mut irreversible_data = IrreversibleData::new(); + let mut half_move_clock = 0; + + parse_fen( + fen_string, + &mut bb_manager, + &mut active_color, + &mut irreversible_data, + &mut half_move_clock, + ); + + State { + bb_manager, + active_color, + irreversible_data, + half_move_clock, + } + } + + /// Executes a move. + /// + /// # Arguments + /// + /// * `chess_move` - A `Moove` object representing the move to be made. + pub fn make_move(&self, moove: Moove) -> State { + let mut next_state = self.clone(); + // The new irreversible data. + let mut next_ir_data = IrreversibleData::new_from_previous_state(&self.irreversible_data); + + // Get the type of moved piece. + let moved_piece = self.bb_manager.get_piece_at_square(moove.from()).unwrap(); + + // Usually the square something was captured on (if something was captured at all) is the square we moved to... + let mut capture_square = moove.to(); + if moved_piece == Pawn { + // ... unless this is an en passant capture, we then need to update the capture square. + next_state.make_move_ep_capture(moove, &mut capture_square); + // Check if a double pawn push was played and store the en passant file + next_state.make_move_double_pawn_push(moove, &mut next_ir_data); + } + + // If something was captured, remove the piece and update irreversible data. + next_state.make_move_capture(&mut next_ir_data, capture_square); + + // Get the bitboard for the piece that was moved. + let mut moved_piece_bitboard = next_state.bb_manager.get_piece_bb_mut(moved_piece); + + // Clear the square that the piece was moved from. + moved_piece_bitboard.clear_square(moove.from()); + + // Update the moved piece bb if it was a pawn promotion + match moove.promotion_type() { + None => {} + Some(promotion_type) => { + moved_piece_bitboard = next_state.bb_manager.get_piece_bb_mut(promotion_type); + } + } + // Fill the square it moved to. + moved_piece_bitboard.fill_square(moove.to()); + + next_state + .bb_manager + .get_all_pieces_bb_off_mut(self.active_color) + .fill_square(moove.to()); + next_state + .bb_manager + .get_all_pieces_bb_off_mut(self.active_color) + .clear_square(moove.from()); + + // Some special king handling + if moved_piece == King { + next_state.make_move_king(moove, &mut next_ir_data); + } + + next_state.make_move_castling_rights_on_rook_move_or_capture( + &mut next_ir_data, + moved_piece, + moove.from(), + self.active_color, + ); + + // Take care of some basics. + next_state.active_color = self.active_color.opposite(); + next_state.irreversible_data = next_ir_data; + next_state + } + + fn make_move_ep_capture(&mut self, moove: Moove, capture_square: &mut Square) { + let ep_square = self.irreversible_data.en_passant_square(); + + // if an en passant square exists + if let Some(ep_square) = ep_square + // and if we moved to the ep_square + && ep_square == moove.to() + { + // update the captured square to the ep_square - offset + *capture_square = moove.to().back_by_one(self.active_color); + } + } + + fn make_move_capture( + &mut self, + irreversible_data: &mut IrreversibleData, + capture_square: Square, + ) { + // Get the type of the captured piece if it exists. + let captured_piece = self.bb_manager.get_piece_at_square(capture_square); + // Clear the square on the captured piece's bitboard if it exists. + if let Some(captured_piece) = captured_piece { + // Store the captured piece type in the irreversible data. + irreversible_data.set_captured_piece(Some(captured_piece)); + // Remove the captured piece from its bitboard. + let captured_piece_bitboard = self.bb_manager.get_piece_bb_mut(captured_piece); + captured_piece_bitboard.clear_square(capture_square); + self.bb_manager + .get_all_pieces_bb_off_mut(self.active_color.opposite()) + .clear_square(capture_square); + + // Remove castling rights if the captured piece was a rook on its starting square + self.make_move_castling_rights_on_rook_move_or_capture( + irreversible_data, + captured_piece, + capture_square, + self.active_color.opposite(), + ) + } + } + + fn make_move_double_pawn_push( + &mut self, + moove: Moove, + irreversible_data: &mut IrreversibleData, + ) { + if moove.is_double_pawn_push() { + // the pawn starting square and one forward + let ep_square = moove.to().back_by_one(self.active_color); + + irreversible_data.set_en_passant_square(Some(ep_square)); + } + } + + fn make_move_king(&mut self, moove: Moove, irreversible_data: &mut IrreversibleData) { + // If the king moved we can't castle anymore + irreversible_data.remove_long_castle_rights(self.active_color); + irreversible_data.remove_short_castle_rights(self.active_color); + + // If we castled, we need to move the rook + if moove.is_castle() { + let rook_bb = self.bb_manager.get_piece_bb_mut(Rook); + let rook_swap_bb = Self::get_rook_swap_bb(moove.get_castle_type(), self.active_color); + *rook_bb ^= rook_swap_bb; + let friendly_bb = self.bb_manager.get_all_pieces_bb_off_mut(self.active_color); + *friendly_bb ^= rook_swap_bb; + } + } + + fn get_rook_swap_bb(castle_type: CastleType, active_color: Side) -> BitBoard { + match castle_type { + CastleType::Long => match active_color { + Side::White => ROOK_SWAP_WHITE_LONG_CASTLE_BB, + Side::Black => ROOK_SWAP_BLACK_LONG_CASTLE_BB, + }, + CastleType::Short => match active_color { + Side::White => ROOK_SWAP_WHITE_SHORT_CASTLE_BB, + Side::Black => ROOK_SWAP_BLACK_SHORT_CASTLE_BB, + }, + } + } + + fn make_move_castling_rights_on_rook_move_or_capture( + &mut self, + irreversible_data: &mut IrreversibleData, + piece_type: Piece, + relevant_square: Square, + relevant_side: Side, + ) { + if piece_type == Rook { + for castling_type in CastleType::get_all_types() { + let starting_square = Self::get_rook_starting_square(castling_type, relevant_side); + if relevant_square == starting_square { + irreversible_data.remove_castle_rights(relevant_side, castling_type); + } + } + } + } + + fn get_rook_starting_square(castle_type: CastleType, color: Side) -> Square { + match castle_type { + CastleType::Long => match color { + Side::White => A1, + Side::Black => A8, + }, + CastleType::Short => match color { + Side::White => H1, + Side::Black => H8, + }, + } + } +} + +impl Default for State { + fn default() -> Self { + Self::new() + } +} diff --git a/src/backend/state/piece.rs b/src/backend/state/piece.rs index c120c18..411b907 100644 --- a/src/backend/state/piece.rs +++ b/src/backend/state/piece.rs @@ -1,8 +1,6 @@ -use getset::CloneGetters; - /// Represents the different pieces. #[derive(Copy, Clone, Debug, Ord, Eq, PartialEq, PartialOrd)] -pub enum PieceType { +pub enum Piece { Pawn, Rook, Knight, @@ -11,74 +9,38 @@ pub enum PieceType { King, } -impl PieceType { - /// Returns an array containing all piece types. - pub fn get_all_types() -> [PieceType; 6] { - [ - PieceType::Knight, - PieceType::King, - PieceType::Pawn, - PieceType::Bishop, - PieceType::Rook, - PieceType::Queen, - ] - } +pub const ALL_PIECES: [Piece; 6] = [ + Piece::Pawn, + Piece::Rook, + Piece::Knight, + Piece::Bishop, + Piece::Queen, + Piece::King, +]; - /// Returns an array containing all piece types that a pawn can promote to. - pub fn get_promotable_types() -> [PieceType; 4] { - [ - PieceType::Rook, - PieceType::Knight, - PieceType::Bishop, - PieceType::Queen, - ] - } +pub const PROMOTABLE_PIECES: [Piece; 4] = [Piece::Rook, Piece::Knight, Piece::Bishop, Piece::Queen]; - pub fn get_slider_types() -> [PieceType; 3] { - [PieceType::Rook, PieceType::Bishop, PieceType::Queen] - } +pub const SLIDER_PIECES: [Piece; 3] = [Piece::Rook, Piece::Bishop, Piece::Queen]; - pub fn get_trivial_types() -> [PieceType; 2] { - [PieceType::Knight, PieceType::King] - } -} +pub const TRIVIAL_PIECES: [Piece; 2] = [Piece::Knight, Piece::King]; /// Represents the color of a piece. #[derive(Copy, Clone, Debug)] -pub enum PieceColor { +pub enum Side { White, Black, } -impl PieceColor { +impl Side { /// Returns the opposite color of the current `PieceColor`. - pub const fn opposite(self) -> PieceColor { + pub const fn opposite(self) -> Side { match self { - PieceColor::White => PieceColor::Black, - PieceColor::Black => PieceColor::White, + Side::White => Side::Black, + Side::Black => Side::White, } } - pub fn get_all_colors() -> [PieceColor; 2] { - [PieceColor::White, PieceColor::Black] - } -} - -/// The `Piece` struct stores the type and color of a chess piece. -#[derive(Copy, Clone, Debug, CloneGetters)] -pub struct Piece { - #[getset(get_clone = "pub")] - piece_type: PieceType, - #[getset(get_clone = "pub")] - piece_color: PieceColor, -} - -impl Piece { - /// Creates a new instance of a `Piece` with the specified type and color. - pub fn new(piece_type: PieceType, piece_color: PieceColor) -> Self { - Piece { - piece_type, - piece_color, - } + pub fn get_all_colors() -> [Side; 2] { + [Side::White, Side::Black] } } diff --git a/src/backend/state/square.rs b/src/backend/state/square.rs index 4ca683e..aae3793 100644 --- a/src/backend/state/square.rs +++ b/src/backend/state/square.rs @@ -1,4 +1,4 @@ -use crate::backend::state::piece::PieceColor; +use crate::backend::state::piece::Side; use getset::Setters; use std::fmt::{Display, Formatter}; @@ -125,22 +125,22 @@ impl Square { self.rank == 0 || self.rank == 7 } - pub const fn is_pawn_start(&self, color: PieceColor) -> bool { + pub const fn is_pawn_start(&self, color: Side) -> bool { match color { - PieceColor::White => self.rank == 1, - PieceColor::Black => self.rank == 6, + Side::White => self.rank == 1, + Side::Black => self.rank == 6, } } // forward means the direction that the pawns travel in for that side - pub const fn forward_by_one(&self, color: PieceColor) -> Square { + pub const fn forward_by_one(&self, color: Side) -> Square { match color { - PieceColor::White => Square::new(self.file, self.rank + 1), - PieceColor::Black => Square::new(self.file, self.rank - 1), + Side::White => Square::new(self.file, self.rank + 1), + Side::Black => Square::new(self.file, self.rank - 1), } } - pub const fn back_by_one(&self, color: PieceColor) -> Square { + pub const fn back_by_one(&self, color: Side) -> Square { self.forward_by_one(color.opposite()) } diff --git a/src/lib.rs b/src/lib.rs index 28c9dcf..1c9d2e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,6 @@ pub mod backend; pub use backend::movegen::moove::Moove; pub use backend::movegen::move_gen::get_pseudo_legal_moves; pub use backend::state::board::bitboard; -pub use backend::state::game::game_state::GameState; +pub use backend::state::game::state::State; pub use backend::state::piece; pub use backend::state::square; diff --git a/src/main.rs b/src/main.rs index 0b5dd2f..e23b244 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use crate::backend::movegen::check_decider::is_in_check; use crate::backend::movegen::moove::Moove; use crate::backend::movegen::move_gen::get_pseudo_legal_moves; use crate::backend::perft::perft; -use crate::backend::state::game::game_state::GameState; +use crate::backend::state::game::state::State; use std::env; use std::env::Args; @@ -26,7 +26,7 @@ pub fn run_perftree_debug(mut input: Args) { let depth = depth.parse::().unwrap(); let fen = &input.next().unwrap(); - let mut game_state = GameState::new_from_fen(fen); + let mut game_state = State::new_from_fen(fen); for mooves in input { // Code golfing @@ -34,36 +34,36 @@ pub fn run_perftree_debug(mut input: Args) { .split_whitespace() .map(Moove::new_from_uci_notation) .for_each(|moove| { - game_state.make_move(moove); + game_state = game_state.make_move(moove); }); } root_debug_perft(&mut game_state, depth as u8); } -pub fn root_debug_perft(game_state: &mut GameState, depth: u8) -> u64 { +pub fn root_debug_perft(root_state: &mut State, depth: u8) -> u64 { // Total nodes searched. let mut nodes = 0; // Generate all root moves. - let mut moves = get_pseudo_legal_moves(game_state); + let mut moves = get_pseudo_legal_moves(root_state); // Sort them in the same way as perftree does moves.sort(); for chess_move in moves { - game_state.make_move(chess_move); + let state = root_state.make_move(chess_move); // If we are in check after making the move -> skip. - if is_in_check(game_state, game_state.active_color().opposite()) { - game_state.unmake_move(chess_move); + if is_in_check(&state, state.active_color().opposite()) { + // game_state.unmake_move(chess_move); continue; } // Recursively calculate nodes for this position. - let nodes_for_this_position = perft(game_state, depth - 1); + let nodes_for_this_position = perft(&state, depth - 1); nodes += nodes_for_this_position; // print info for https://github.com/agausmann/perftree println!("{} {:?}", chess_move, nodes_for_this_position); - game_state.unmake_move(chess_move); + // game_state.unmake_move(chess_move); } println!(); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 2f0025a..78bc95c 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,4 +1,4 @@ -use mouse::GameState; +use mouse::State; use mouse::backend::perft::perft; use perft_fixtures::perft_fixtures::{FAST_PERFT, LONG_PERFT, NORMAL_PERFT, PerftFixture}; @@ -34,8 +34,8 @@ fn test_single_perft_fixture(perft_fixture: &PerftFixture) { println!("Testing {:?} with {:?} nodes", name, expected_nodes); - let mut state = GameState::new_from_fen(fen); - let nodes = perft(&mut state, depth); + let state = State::new_from_fen(fen); + let nodes = perft(&state, depth); assert_eq!(nodes, expected_nodes, "Testing {:?}", name); } @@ -45,153 +45,152 @@ fn test_single_perft_fixture(perft_fixture: &PerftFixture) { // --------------------------------------------- // #[test] fn test_perft_01() { - let mut game_state = GameState::new_from_fen("8/1n2k3/5n1n/2n5/4N3/2N5/1N2KN2/8 w - - 0 1"); - let nodes = perft(&mut game_state, 4); + let state = State::new_from_fen("8/1n2k3/5n1n/2n5/4N3/2N5/1N2KN2/8 w - - 0 1"); + let nodes = perft(&state, 4); assert_eq!(nodes, 472915); // This currently takes too long to run. - // let nodes = perft(&mut game_state, 5); + // let nodes = perft(&state, 5); // assert_eq!(nodes, 11949411); } #[test] fn test_perft_02() { - let mut game_state = GameState::new_from_fen("4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"); - let nodes = perft(&mut game_state, 4); + let state = State::new_from_fen("4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"); + let nodes = perft(&state, 4); assert_eq!(nodes, 98766); } #[test] fn test_perft_03() { - let mut game_state = GameState::new_from_fen("4k3/ppp5/7p/8/8/8/PPP5/4K3 w - - 0 1"); + let state = State::new_from_fen("4k3/ppp5/7p/8/8/8/PPP5/4K3 w - - 0 1"); - let nodes = perft(&mut game_state, 4); + let nodes = perft(&state, 4); assert_eq!(nodes, 17684); } #[test] fn test_perft_04() { - let mut game_state = GameState::new_from_fen("4k3/ppp5/7p/8/8/8/PPP5/4K3 w - - 0 1"); + let state = State::new_from_fen("4k3/ppp5/7p/8/8/8/PPP5/4K3 w - - 0 1"); - let nodes = perft(&mut game_state, 5); + let nodes = perft(&state, 5); assert_eq!(nodes, 197056); } #[test] fn test_perft_05() { - let mut game_state = GameState::new_from_fen("7k/3p4/8/2P5/8/8/8/7K b - - 0 1"); + let state = State::new_from_fen("7k/3p4/8/2P5/8/8/8/7K b - - 0 1"); - let nodes = perft(&mut game_state, 4); + let nodes = perft(&state, 4); assert_eq!(nodes, 896); - let nodes = perft(&mut game_state, 5); + let nodes = perft(&state, 5); assert_eq!(nodes, 6583); } #[test] fn test_perft_06() { - let mut game_state = GameState::new_from_fen("7k/8/8/8/8/2K5/2P5/8 w - - 0 1"); + let state = State::new_from_fen("7k/8/8/8/8/2K5/2P5/8 w - - 0 1"); - let nodes = perft(&mut game_state, 1); + let nodes = perft(&state, 1); assert_eq!(nodes, 7); } #[test] fn test_perft_07() { - let mut game_state = GameState::new_from_fen("8/3P1k2/8/8/8/8/8/7K b - - 0 1"); + let state = State::new_from_fen("8/3P1k2/8/8/8/8/8/7K b - - 0 1"); - let nodes = perft(&mut game_state, 1); + let nodes = perft(&state, 1); assert_eq!(nodes, 7); - let nodes = perft(&mut game_state, 2); + let nodes = perft(&state, 2); assert_eq!(nodes, 49); // Missing slider logic atm - // let nodes = perft(&mut game_state, 3); + // let nodes = perft(&state, 3); // assert_eq!(nodes, 289); } #[test] fn test_perft_08() { - let mut game_state = GameState::new_from_fen("8/1ppP1k2/1n6/3P2P1/8/8/8/7K b - - 0 1"); + let state = State::new_from_fen("8/1ppP1k2/1n6/3P2P1/8/8/8/7K b - - 0 1"); - let nodes = perft(&mut game_state, 2); + let nodes = perft(&state, 2); assert_eq!(nodes, 117); } #[test] fn test_perft_09() { - let mut game_state = GameState::new_from_fen("7k/P7/8/8/8/8/8/7K w - - 0 1"); + let state = State::new_from_fen("7k/P7/8/8/8/8/8/7K w - - 0 1"); - let nodes = perft(&mut game_state, 1); + let nodes = perft(&state, 1); assert_eq!(nodes, 7); } #[test] fn test_perft_10() { - let mut game_state = GameState::new_from_fen("6k1/8/8/8/8/8/8/Q1R1B2K b - - 0 1"); + let state = State::new_from_fen("6k1/8/8/8/8/8/8/Q1R1B2K b - - 0 1"); - let nodes = perft(&mut game_state, 4); + let nodes = perft(&state, 4); assert_eq!(nodes, 15773); } #[test] fn test_perft_11() { - let mut game_state = GameState::new_from_fen("6k1/6Q1/8/8/8/8/8/2R1B2K b - - 0 1"); + let state = State::new_from_fen("6k1/6Q1/8/8/8/8/8/2R1B2K b - - 0 1"); - let nodes = perft(&mut game_state, 1); + let nodes = perft(&state, 1); assert_eq!(nodes, 1); } #[test] fn test_perft_12() { - let mut game_state = - GameState::new_from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); + let state = State::new_from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); - let nodes = perft(&mut game_state, 5); + let nodes = perft(&state, 5); assert_eq!(nodes, 4865609); } #[test] fn test_perft_13() { // test if basic castling moves get generated - let mut game_state = GameState::new_from_fen("6k1/8/8/8/8/8/8/R3K2R w KQ - 0 1"); + let state = State::new_from_fen("6k1/8/8/8/8/8/8/R3K2R w KQ - 0 1"); - let nodes = perft(&mut game_state, 1); + let nodes = perft(&state, 1); assert_eq!(nodes, 26); } #[test] fn test_perft_14() { // test if basic castling moves get generated - with checkers - let mut game_state = GameState::new_from_fen("2r2rk1/8/8/8/8/8/8/R3K2R w KQ - 0 1"); + let state = State::new_from_fen("2r2rk1/8/8/8/8/8/8/R3K2R w KQ - 0 1"); - let nodes = perft(&mut game_state, 1); + let nodes = perft(&state, 1); assert_eq!(nodes, 22); } #[test] fn test_perft_15() { // test if basic castling moves get generated - with blockers - let mut game_state = GameState::new_from_fen("2r2rk1/8/8/8/8/8/8/R1B1K1NR w KQ - 0 1"); + let state = State::new_from_fen("2r2rk1/8/8/8/8/8/8/R1B1K1NR w KQ - 0 1"); - let nodes = perft(&mut game_state, 1); + let nodes = perft(&state, 1); assert_eq!(nodes, 28); } #[test] fn test_perft_16() { // test if basic castling moves get made and unmade correctly - let mut game_state = GameState::new_from_fen("6k1/8/8/8/8/8/8/R3K2R w KQ - 0 1"); + let state = State::new_from_fen("6k1/8/8/8/8/8/8/R3K2R w KQ - 0 1"); - let nodes = perft(&mut game_state, 4); + let nodes = perft(&state, 4); assert_eq!(nodes, 10438); } #[test] fn test_perft_17() { // test if basic castling moves get made and unmade correctly - let mut game_state = GameState::new_from_fen("r5k1/8/8/8/8/8/8/R3K2R w KQ - 0 1"); + let state = State::new_from_fen("r5k1/8/8/8/8/8/8/R3K2R w KQ - 0 1"); - let nodes = perft(&mut game_state, 4); + let nodes = perft(&state, 4); assert_eq!(nodes, 112371); }