diff --git a/src/board.rs b/src/board.rs index 6786d48..5c69b2b 100644 --- a/src/board.rs +++ b/src/board.rs @@ -4,7 +4,7 @@ use crate::attack::{ get_bishop_attacks, get_king_attacks, get_knight_attacks, get_pawn_attacks, get_queen_attacks, get_rook_attacks, }; -use crate::game::State; +use crate::game::{Castle, State}; use crate::movegen::{ bishop_pseudo_moves, king_pseudo_moves, knight_pseudo_moves, pawn_pseudo_moves, queen_pseudo_moves, rook_pseudo_moves, @@ -88,12 +88,12 @@ impl Board { pub fn is_attacked(&self, sq: usize, opponent_color: Color) -> bool { let all_occupancies = self.get_all_occupancies(); - let enemy = match opponent_color { - Color::Black => &self.black_pieces, - Color::White => &self.white_pieces, + let (enemy, own_color) = match opponent_color { + Color::Black => (&self.black_pieces, Color::White), + Color::White => (&self.white_pieces, Color::Black), }; - enemy[Kind::Pawn.idx()].bitboard & get_pawn_attacks(sq, opponent_color) != 0 + enemy[Kind::Pawn.idx()].bitboard & get_pawn_attacks(sq, own_color) != 0 || enemy[Kind::Knight.idx()].bitboard & get_knight_attacks(sq) != 0 || enemy[Kind::Bishop.idx()].bitboard & get_bishop_attacks(all_occupancies, sq) != 0 || enemy[Kind::Rook.idx()].bitboard & get_rook_attacks(all_occupancies, sq) != 0 @@ -156,14 +156,18 @@ impl Board { pub fn make_move(&mut self, mv: Move, color: Color) -> bool { self.update_board_state(&mv, color); - self.state.update_castling_state(mv.source as u8, color); - self.state.change_side(); let pieces = match color { Color::White => &self.white_pieces, Color::Black => &self.black_pieces, }; + self.state + .update_castling_state_quiet(mv.source as u8, color); + self.state + .update_castling_state_capture(mv.target as u8, Color::opponent_color(color)); + self.state.change_side(); + let own_king_square = pieces[Kind::King.idx()].bitboard.trailing_zeros() as usize; self.is_move_legit(own_king_square, Color::opponent_color(color)) } @@ -191,7 +195,10 @@ impl Board { } MoveType::DoublePush => { Board::move_piece(mv.source as u8, mv.target as u8, own_pieces); - let en_passant = Some(mv.source as u8 + 8); + let en_passant = match color { + Color::White => Some((mv.source + 8) as u8), + Color::Black => Some((mv.source - 8) as u8), + }; self.state.set_en_passant_target_square(en_passant); } MoveType::Promotion(promote) => { @@ -209,6 +216,7 @@ impl Board { Board::move_piece(mv.source as u8, mv.target as u8, own_pieces); Board::move_rook_castle(mv.target as u8, own_pieces); self.state.set_en_passant_target_square(None); + self.state.set_castling_ability(color, Castle::None) } } } @@ -230,13 +238,7 @@ impl Board { _ => return, }; - for p in pieces.iter_mut() { - if p.bitboard & 1_u64 << rook_source != 0 { - p.bitboard &= !(1_u64 << rook_source); - p.bitboard |= 1_u64 << rook_target; - break; - } - } + Board::move_piece(rook_source, rook_target, pieces); } fn promote_piece(square: u8, pieces: &mut [Piece; 6], promote: Promote) { @@ -464,28 +466,6 @@ mod tests { let mv = Move::new_with_type(4, 6, MoveType::Castle); game.board.make_move(mv, Color::White); assert_eq!(game, from_fen(FEN_CASTLE[1])?); - - Ok(()) - } - - #[test] - fn test_perftree_snapshots() -> Result<(), String> { - init_attacks(); - - let mut game = from_fen("rnbqkbnr/ppppp1pp/5p2/7Q/8/4P3/PPPP1PPP/RNB1KBNR w KQkq - 0 1")?; - for mv in game.board.pseudo_moves_all(Color::Black) { - if game.board.make_move_and_reset(mv, Color::Black) { - println!("{:?}", mv) - } - } - - let mut game = from_fen("rnbqkbnr/1ppppppp/p7/8/Q7/2P5/PP1PPPPP/RNB1KBNR w KQkq - 0 1")?; - for mv in game.board.pseudo_moves_all(Color::Black) { - if game.board.make_move_and_reset(mv, Color::Black) { - println!("{:?}", mv) - } - } - Ok(()) } } diff --git a/src/game.rs b/src/game.rs index 931935a..f95b678 100644 --- a/src/game.rs +++ b/src/game.rs @@ -78,26 +78,37 @@ impl State { } } - pub fn update_castling_state(&mut self, square: u8, color: Color) { - if square == 0 || square == 7 { - match (color, self.get_castling_ability(color)) { - (_, Castle::Both) => self.set_castling_ability(color, Castle::Long), - (_, Castle::Short) => self.set_castling_ability(color, Castle::None), + pub fn update_castling_state_quiet(&mut self, source: u8, color: Color) { + self.update_castling_state_capture(source, color); + + match (source, color) { + (4, Color::White) => self.set_castling_ability(color, Castle::None), + (60, Color::Black) => self.set_castling_ability(color, Castle::None), + _ => (), + } + } + + pub fn update_castling_state_capture(&mut self, target: u8, color: Color) { + let (short_square, long_square) = match color { + Color::White => (7, 0), + Color::Black => (63, 56), + }; + + if target == short_square { + match self.get_castling_ability(color) { + Castle::Both => self.set_castling_ability(color, Castle::Long), + Castle::Short => self.set_castling_ability(color, Castle::None), _ => (), } } - if square == 56 || square == 63 { - match (color, self.get_castling_ability(color)) { - (_, Castle::Both) => self.set_castling_ability(color, Castle::Short), - (_, Castle::Short) => self.set_castling_ability(color, Castle::None), + if target == long_square { + match self.get_castling_ability(color) { + Castle::Both => self.set_castling_ability(color, Castle::Short), + Castle::Long => self.set_castling_ability(color, Castle::None), _ => (), } } - - if square == 4 || square == 60 { - self.set_castling_ability(color, Castle::None) - } } pub fn change_side(&mut self) { diff --git a/src/movegen.rs b/src/movegen.rs index 9cac5b7..0e4b42c 100644 --- a/src/movegen.rs +++ b/src/movegen.rs @@ -50,7 +50,7 @@ fn white_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec { let to = single_push_targets.trailing_zeros(); let from = to - 8; - if from as u64 & 0xff000000000000 != 0 { + if 1_u64 << from as u64 & 0xff000000000000 != 0 { moves.extend([ Move::new_with_type(from, to, MoveType::Promotion(Promote::Knight)), Move::new_with_type(from, to, MoveType::Promotion(Promote::Bishop)), @@ -82,7 +82,7 @@ fn black_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec { let to = single_push_targets.trailing_zeros(); let from = to + 8; - if from as u64 & 0xff000000000000 != 0 { + if 1_u64 << from as u64 & 0xff00 != 0 { moves.extend([ Move::new_with_type(from, to, MoveType::Promotion(Promote::Knight)), Move::new_with_type(from, to, MoveType::Promotion(Promote::Bishop)), @@ -119,7 +119,7 @@ fn white_pawn_capture_moves( let from = w_pawns_capture_east.trailing_zeros(); let to = from + 9; - if from as u64 & 0xff000000000000 != 0 { + if 1_u64 << to as u64 & 0xff00000000000000 != 0 { moves.extend([ Move::new_with_type(from, to, MoveType::PromotionCapture(Promote::Knight)), Move::new_with_type(from, to, MoveType::PromotionCapture(Promote::Bishop)), @@ -135,7 +135,7 @@ fn white_pawn_capture_moves( let from = w_pawns_capture_west.trailing_zeros(); let to = from + 7; - if from as u64 & 0xff000000000000 != 0 { + if 1_u64 << to as u64 & 0xff00000000000000 != 0 { moves.extend([ Move::new_with_type(from, to, MoveType::PromotionCapture(Promote::Knight)), Move::new_with_type(from, to, MoveType::PromotionCapture(Promote::Bishop)), @@ -150,13 +150,14 @@ fn white_pawn_capture_moves( if let Some(en_passant_square) = en_passant_square { let attacked_from = get_pawn_attacks(en_passant_square as usize, Color::Black); - let result = attacked_from & pawns; - if result != 0 { + let mut result = attacked_from & pawns; + while result != 0 { moves.push(Move::new_with_type( result.trailing_zeros(), en_passant_square as u32, MoveType::EnPassant, )); + result &= result - 1; } }; moves @@ -174,7 +175,7 @@ fn black_pawn_capture_moves( while b_pawns_capture_east != 0 { let from = b_pawns_capture_east.trailing_zeros(); let to = from - 7; - if from as u64 & 0xff00 != 0 { + if 1_u64 << to as u64 & 0xff != 0 { moves.extend([ Move::new_with_type(from, to, MoveType::PromotionCapture(Promote::Knight)), Move::new_with_type(from, to, MoveType::PromotionCapture(Promote::Bishop)), @@ -190,7 +191,7 @@ fn black_pawn_capture_moves( while b_pawns_capture_west != 0 { let from = b_pawns_capture_west.trailing_zeros(); let to = from - 9; - if from as u64 & 0xff00 != 0 { + if 1_u64 << to as u64 & 0xff != 0 { moves.extend([ Move::new_with_type(from, to, MoveType::PromotionCapture(Promote::Knight)), Move::new_with_type(from, to, MoveType::PromotionCapture(Promote::Bishop)), @@ -205,13 +206,14 @@ fn black_pawn_capture_moves( if let Some(en_passant_square) = en_passant_square { let attacked_from = get_pawn_attacks(en_passant_square as usize, Color::White); - let result = attacked_from & pawns; - if result != 0 { + let mut result = attacked_from & pawns; + while result != 0 { moves.push(Move::new_with_type( result.trailing_zeros(), en_passant_square as u32, MoveType::EnPassant, )); + result &= result - 1; } }; moves @@ -373,11 +375,16 @@ fn king_castling_moves(board: &Board, color: Color, all_occupancies: Bitboard) - add_move_if_empty_path(path_short, king_to_short); add_move_if_empty_path(path_long, king_to_long); } - (Castle::Both, Castle::Short) | (Castle::Short, Castle::Short) => { + + (Castle::Both, Castle::Short) + | (Castle::Short, Castle::Short) + | (Castle::Short, Castle::Both) => { add_move_if_empty_path(path_short, king_to_short); } - (Castle::Both, Castle::Long) | (Castle::Long, Castle::Long) => { - add_move_if_empty_path(path_long, king_to_long) + (Castle::Both, Castle::Long) + | (Castle::Long, Castle::Long) + | (Castle::Long, Castle::Both) => { + add_move_if_empty_path(path_long, king_to_long); } _ => (), }; @@ -386,7 +393,7 @@ fn king_castling_moves(board: &Board, color: Color, all_occupancies: Bitboard) - fn king_and_adj_square_safety(board: &Board, color: Color) -> Castle { let (king_at, opponent_color) = match color { - Color::White => (3, Color::Black), + Color::White => (4, Color::Black), Color::Black => (60, Color::White), }; @@ -394,12 +401,12 @@ fn king_and_adj_square_safety(board: &Board, color: Color) -> Castle { return Castle::None; } - let short_attacked = board.is_attacked(king_at - 1, opponent_color); - let long_attacked = board.is_attacked(king_at + 1, opponent_color); + let short_attacked = board.is_attacked(king_at + 1, opponent_color); + let long_attacked = board.is_attacked(king_at - 1, opponent_color); match (short_attacked, long_attacked) { (true, true) => Castle::None, - (true, false) => Castle::Short, - (false, true) => Castle::Long, + (true, false) => Castle::Long, + (false, true) => Castle::Short, (false, false) => Castle::Both, } } @@ -495,7 +502,7 @@ mod tests { ]; let mut actual = new_game.board.pseudo_moves(Color::White, Kind::Bishop); actual.sort(); - assert_eq!(expected, actual); + assert_eq!(expected, actual); Ok(()) } @@ -594,16 +601,4 @@ mod tests { Ok(()) } - - #[test] - fn test_random() -> Result<(), String> { - init_attacks(); - let game = from_fen("rnbqkbnr/ppppp1pp/5p2/7Q/8/4P3/PPPP1PPP/RNB1KBNR w KQkq - 0 1")?; - - for mv in game.board.pseudo_moves_all(Color::Black) { - println!("{:?}", mv); - } - - Ok(()) - } } diff --git a/src/perft.rs b/src/perft.rs index e92d94f..527ade8 100644 --- a/src/perft.rs +++ b/src/perft.rs @@ -1,14 +1,13 @@ use crate::game::Game; -pub fn perft_driver(game: &mut Game, nodes: &mut u64, depth: u8) { +pub fn driver(game: &mut Game, nodes: &mut u64, depth: u8) { if depth == 0 { *nodes += 1; return; } - let pseudo_moves = - game.board.pseudo_moves_all(game.board.state.next_turn()); - + let pseudo_moves = game.board.pseudo_moves_all(game.board.state.next_turn()); + for mv in pseudo_moves { let original_board = game.board.clone(); if !game.board.make_move(mv, game.board.state.next_turn()) { @@ -16,20 +15,22 @@ pub fn perft_driver(game: &mut Game, nodes: &mut u64, depth: u8) { continue; } - // print_perftree(mv.source, mv.target, depth, nodes); + print_perftree(mv.source, mv.target, depth, nodes); - perft_driver(game, nodes, depth - 1); + driver(game, nodes, depth - 1); game.board = original_board; } #[allow(dead_code)] fn print_perftree(source: u32, target: u32, depth: u8, nodes: &mut u64) { - println!( - "{}{} - depth: {}", - square_to_notation(source as u8), - square_to_notation(target as u8), - depth - ); + // println!( + // "{}{} - depth: {} - nodes: {}", + // square_to_notation(source as u8), + // square_to_notation(target as u8), + // depth, + // nodes + // ); + if depth == MAX_DEPTH { println!( "{}{} {}", @@ -39,16 +40,15 @@ pub fn perft_driver(game: &mut Game, nodes: &mut u64, depth: u8) { ); } } - } -const MAX_DEPTH: u8 = 4; +const MAX_DEPTH: u8 = 3; pub fn perftree_script() { - let fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + let fen = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"; let mut game = crate::fen::from_fen(fen).unwrap(); let (mut nodes, depth): (u64, u8) = (0, MAX_DEPTH); - perft_driver(&mut game, &mut nodes, depth); + driver(&mut game, &mut nodes, depth); println!(); println!("{}", nodes) } @@ -81,7 +81,7 @@ pub fn square_to_notation(square: u8) -> &'static str { mod tests { use crate::{attack::init_attacks, fen::from_fen}; - use super::perft_driver; + use super::driver; // Examples from https://www.chessprogramming.org/Perft_Results const FEN_PERFT: [&str; 6] = [ @@ -93,164 +93,90 @@ mod tests { "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", ]; - #[test] - fn test_perft_driver_1() -> Result<(), String> { + fn perft(fen: &str, depth: u8) -> Result { init_attacks(); - let mut game = from_fen(FEN_PERFT[0])?; - let (mut nodes, depth): (u64, u8) = (0, 1); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes , 20); + let mut game = from_fen(fen)?; + let mut nodes = 0; + driver(&mut game, &mut nodes, depth); - let mut game = from_fen(FEN_PERFT[0])?; - let (mut nodes, depth): (u64, u8) = (0, 2); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes, 400); + Ok(nodes) + } - let mut game = from_fen(FEN_PERFT[0])?; - let (mut nodes, depth): (u64, u8) = (0, 3); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes, 8902); + #[test] + fn test_perft_1() -> Result<(), String> { + init_attacks(); - let mut game = from_fen(FEN_PERFT[0])?; - let (mut nodes, depth): (u64, u8) = (0, 4); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes, 197281); + assert_eq!(perft(FEN_PERFT[0], 1)?, 20); + assert_eq!(perft(FEN_PERFT[0], 2)?, 400); + assert_eq!(perft(FEN_PERFT[0], 3)?, 8902); + assert_eq!(perft(FEN_PERFT[0], 4)?, 197281); + assert_eq!(perft(FEN_PERFT[0], 5)?, 4865609); Ok(()) } #[test] - fn test_perft_driver_2() -> Result<(), String> { + fn test_perft_2() -> Result<(), String> { init_attacks(); - let mut game = from_fen(FEN_PERFT[1])?; - let (mut nodes, depth): (u64, u8) = (0, 1); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes, 48); - - let mut game = from_fen(FEN_PERFT[1])?; - let (mut nodes, depth): (u64, u8) = (0, 2); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes, 2039); - - // let mut game = from_fen(FEN_PERFT[1])?; - // let (mut nodes, depth): (u64, u8) = (0, 3); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 97862); - - // let mut game = from_fen(FEN_PERFT[1])?; - // let (mut nodes, depth): (u64, u8) = (0, 4); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 4085603); + assert_eq!(perft(FEN_PERFT[1], 1)?, 48); + assert_eq!(perft(FEN_PERFT[1], 2)?, 2039); + assert_eq!(perft(FEN_PERFT[1], 3)?, 97862); + assert_eq!(perft(FEN_PERFT[1], 4)?, 4085603); + assert_eq!(perft(FEN_PERFT[1], 5)?, 193690690); Ok(()) } #[test] - fn test_perft_driver_3() -> Result<(), String> { + fn test_perft_3() -> Result<(), String> { init_attacks(); - // let mut game = from_fen(FEN_PERFT[2])?; - // let (mut nodes, depth): (u64, u8) = (0, 1); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 14); - - // let mut game = from_fen(FEN_PERFT[2])?; - // let (mut nodes, depth): (u64, u8) = (0, 2); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 191); - - // let mut game = from_fen(FEN_PERFT[2])?; - // let (mut nodes, depth): (u64, u8) = (0, 3); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 2812); - - // let mut game = from_fen(FEN_PERFT[2])?; - // let (mut nodes, depth): (u64, u8) = (0, 4); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 43238); + assert_eq!(perft(FEN_PERFT[2], 1)?, 14); + assert_eq!(perft(FEN_PERFT[2], 2)?, 191); + assert_eq!(perft(FEN_PERFT[2], 3)?, 2812); + assert_eq!(perft(FEN_PERFT[2], 4)?, 43238); + assert_eq!(perft(FEN_PERFT[2], 5)?, 674624); Ok(()) } #[test] - fn test_perft_driver_4() -> Result<(), String> { + fn test_perft_4() -> Result<(), String> { init_attacks(); - let mut game = from_fen(FEN_PERFT[3])?; - let (mut nodes, depth): (u64, u8) = (0, 1); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes , 6); - - // let mut game = from_fen(FEN_PERFT[3])?; - // let (mut nodes, depth): (u64, u8) = (0, 2); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 264); - - // let mut game = from_fen(FEN_PERFT[3])?; - // let (mut nodes, depth): (u64, u8) = (0, 3); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 9467); - - // let mut game = from_fen(FEN_PERFT[3])?; - // let (mut nodes, depth): (u64, u8) = (0, 4); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 422333); + assert_eq!(perft(FEN_PERFT[3], 1)?, 6); + assert_eq!(perft(FEN_PERFT[3], 2)?, 264); + assert_eq!(perft(FEN_PERFT[3], 3)?, 9467); + assert_eq!(perft(FEN_PERFT[3], 4)?, 422333); + assert_eq!(perft(FEN_PERFT[3], 5)?, 15833292); Ok(()) } #[test] - fn test_perft_driver_5() -> Result<(), String> { + fn test_perft_5() -> Result<(), String> { init_attacks(); - // let mut game = from_fen(FEN_PERFT[4])?; - // let (mut nodes, depth): (u64, u8) = (0, 1); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes , 44); - - // let mut game = from_fen(FEN_PERFT[4])?; - // let (mut nodes, depth): (u64, u8) = (0, 2); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 1486); - - // let mut game = from_fen(FEN_PERFT[4])?; - // let (mut nodes, depth): (u64, u8) = (0, 3); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 62379); - - // let mut game = from_fen(FEN_PERFT[4])?; - // let (mut nodes, depth): (u64, u8) = (0, 4); - // perft_driver(&mut game, &mut nodes, depth); - // assert_eq!(nodes, 2103487); + assert_eq!(perft(FEN_PERFT[4], 1)?, 44); + assert_eq!(perft(FEN_PERFT[4], 2)?, 1486); + assert_eq!(perft(FEN_PERFT[4], 3)?, 62379); + assert_eq!(perft(FEN_PERFT[4], 4)?, 2103487); + assert_eq!(perft(FEN_PERFT[4], 5)?, 89941194); Ok(()) } #[test] - fn test_perft_driver_6() -> Result<(), String> { + fn test_perft_6() -> Result<(), String> { init_attacks(); - let mut game = from_fen(FEN_PERFT[5])?; - let (mut nodes, depth): (u64, u8) = (0, 1); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes , 46); - - let mut game = from_fen(FEN_PERFT[5])?; - let (mut nodes, depth): (u64, u8) = (0, 2); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes, 2079); - - let mut game = from_fen(FEN_PERFT[5])?; - let (mut nodes, depth): (u64, u8) = (0, 3); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes, 89890); - - let mut game = from_fen(FEN_PERFT[5])?; - let (mut nodes, depth): (u64, u8) = (0, 4); - perft_driver(&mut game, &mut nodes, depth); - assert_eq!(nodes, 3894594); + assert_eq!(perft(FEN_PERFT[5], 1)?, 46); + assert_eq!(perft(FEN_PERFT[5], 2)?, 2079); + assert_eq!(perft(FEN_PERFT[5], 3)?, 89890); + assert_eq!(perft(FEN_PERFT[5], 4)?, 3894594); + assert_eq!(perft(FEN_PERFT[5], 5)?, 164075551); Ok(()) }