From a1db302d0de199fbf44e6b5b35815d0b277e5f0f Mon Sep 17 00:00:00 2001 From: stefiosif Date: Tue, 16 Jul 2024 21:29:58 +0300 Subject: [PATCH] Add make_move related functions into move.rs --- src/board.rs | 244 +++------------------------------------------------ src/move.rs | 233 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 242 insertions(+), 235 deletions(-) diff --git a/src/board.rs b/src/board.rs index f513c65..ea38a38 100644 --- a/src/board.rs +++ b/src/board.rs @@ -4,14 +4,13 @@ use crate::attack::{ fetch_bishop_attacks, fetch_king_attacks, fetch_knight_attacks, fetch_pawn_attacks, fetch_queen_attacks, fetch_rook_attacks, }; -use crate::bitboard::{have_common_bit, lsb, square_to_bitboard}; +use crate::bitboard::have_common_bit; use crate::movegen::{ bishop_pseudo_moves, king_pseudo_moves, knight_pseudo_moves, pawn_pseudo_moves, queen_pseudo_moves, rook_pseudo_moves, }; -use crate::r#move::{Move, MoveType, Promote}; -use crate::square::Square; -use crate::state::{Castle, State}; +use crate::r#move::Move; +use crate::state::State; #[derive(Debug, PartialEq, Eq, Clone)] pub struct Board { @@ -65,17 +64,6 @@ impl Board { } } - pub fn set_piece(&mut self, piece: Piece) { - match piece.color { - Color::Black => self.black_pieces[piece.kind.idx()].bitboard |= piece.bitboard, - Color::White => self.white_pieces[piece.kind.idx()].bitboard |= piece.bitboard, - }; - } - - pub fn set_state(&mut self, state: State) { - self.state = state; - } - pub fn white_occupancies(&self) -> Bitboard { self.white_pieces.iter().fold(0, |acc, p| p.bitboard | acc) } @@ -130,6 +118,7 @@ impl Board { moves.extend(self.pseudo_moves(color, Kind::King)); moves } + pub fn pseudo_moves(&self, color: Color, kind: Kind) -> Vec { let all_occupancies = self.all_occupancies(); let (pieces, opponent_occupancies, own_occupancies) = match color { @@ -161,125 +150,15 @@ impl Board { } } - pub fn make_move_and_reset(&mut self, mv: Move, color: Color) -> bool { - let original_board = self.clone(); - let result = self.make_move(mv, color); - *self = original_board; - - result - } - - pub fn make_move(&mut self, mv: Move, color: Color) -> bool { - self.update_board_state(&mv, color); - - let pieces = match color { - Color::White => &self.white_pieces, - Color::Black => &self.black_pieces, - }; - - self.state.update_castling_state_quiet(mv.src, color); - self.state - .update_castling_state_capture(mv.dst, Color::opponent_color(color)); - self.state.change_side(); - - let own_king_square = lsb(pieces[Kind::King.idx()].bitboard); - self.is_move_legit(own_king_square, Color::opponent_color(color)) - } - - pub fn update_board_state(&mut self, mv: &Move, color: Color) { - let (own_pieces, opponent_pieces) = match color { - Color::White => (&mut self.white_pieces, &mut self.black_pieces), - Color::Black => (&mut self.black_pieces, &mut self.white_pieces), - }; - - match &mv.move_type { - MoveType::Quiet => { - Self::move_piece(mv.src, mv.dst, own_pieces); - self.state.set_en_passant_target_square(None); - } - MoveType::Capture => { - Self::move_piece(mv.src, mv.dst, own_pieces); - Self::remove_piece(mv.dst, opponent_pieces); - self.state.set_en_passant_target_square(None); - } - MoveType::EnPassant => { - Self::move_piece(mv.src, mv.dst, own_pieces); - Self::remove_pawn_enpassant(mv.dst, opponent_pieces, color); - self.state.set_en_passant_target_square(None); - } - MoveType::DoublePush => { - Self::move_piece(mv.src, mv.dst, own_pieces); - let en_passant = match color { - Color::White => Some(mv.src + 8), - Color::Black => Some(mv.src - 8), - }; - self.state.set_en_passant_target_square(en_passant); - } - MoveType::Promotion(promote) => { - Self::remove_piece(mv.src, own_pieces); - Self::promote_piece(mv.dst, own_pieces, *promote); - self.state.set_en_passant_target_square(None); - } - MoveType::PromotionCapture(promote) => { - Self::remove_piece(mv.src, own_pieces); - Self::remove_piece(mv.dst, opponent_pieces); - Self::promote_piece(mv.dst, own_pieces, *promote); - self.state.set_en_passant_target_square(None); - } - MoveType::Castle => { - Self::move_piece(mv.src, mv.dst, own_pieces); - Self::move_rook_castle(mv.dst, own_pieces); - self.state.set_en_passant_target_square(None); - self.state.set_castling_ability(color, Castle::None) - } - } - } - - fn move_piece(src: usize, dst: usize, pieces: &mut [Piece; 6]) { - for p in pieces.iter_mut() { - if have_common_bit(p.bitboard, square_to_bitboard(src)) { - p.bitboard &= !square_to_bitboard(src); - p.bitboard |= square_to_bitboard(dst); - break; - } - } - } - - fn move_rook_castle(king_dst: usize, pieces: &mut [Piece; 6]) { - let (rook_src, rook_dst) = match king_dst { - Square::C1 | Square::C8 => (king_dst - 2, king_dst + 1), - Square::G1 | Square::G8 => (king_dst + 1, king_dst - 1), - _ => return, - }; - - Self::move_piece(rook_src, rook_dst, pieces); - } - - fn promote_piece(square: usize, pieces: &mut [Piece; 6], promote: Promote) { - match promote { - Promote::Knight => pieces[Kind::Knight.idx()].bitboard |= square_to_bitboard(square), - Promote::Bishop => pieces[Kind::Bishop.idx()].bitboard |= square_to_bitboard(square), - Promote::Rook => pieces[Kind::Rook.idx()].bitboard |= square_to_bitboard(square), - Promote::Queen => pieces[Kind::Queen.idx()].bitboard |= square_to_bitboard(square), + pub fn set_piece(&mut self, piece: Piece) { + match piece.color { + Color::Black => self.black_pieces[piece.kind.idx()].bitboard |= piece.bitboard, + Color::White => self.white_pieces[piece.kind.idx()].bitboard |= piece.bitboard, }; } - fn remove_piece(square: usize, pieces: &mut [Piece; 6]) { - for p in pieces.iter_mut() { - if have_common_bit(p.bitboard, square_to_bitboard(square)) { - p.bitboard &= !(square_to_bitboard(square)); - break; - } - } - } - - fn remove_pawn_enpassant(square: usize, pieces: &mut [Piece; 6], color: Color) { - let piece_to_remove = match color { - Color::White => square - 8, - Color::Black => square + 8, - }; - - Self::remove_piece(piece_to_remove, pieces); + pub fn set_state(&mut self, state: State) { + self.state = state; } } @@ -374,107 +253,4 @@ mod tests { //TODO: make a MoveHistory/BoardHistory/GameHistory struct Ok(()) } - - const FEN_QUIET: [&str; 2] = [ - "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R w KQkq - 0 1", - "r3k2r/2p1p1qp/2npb3/1p3p2/pP2P1pP/P1PB1Q2/3PNPP1/R3K2R b KQkq b3 0 1", - ]; - - #[test] - fn test_make_move_quiet() -> Result<(), String> { - let mut game = from_fen(FEN_QUIET[0])?; - let mv = Move::new_with_type(9, 25, MoveType::DoublePush); - game.board.make_move(mv, Color::White); - assert_eq!(game, from_fen(FEN_QUIET[1])?); - - Ok(()) - } - - const FEN_CAPTURE: [&str; 2] = [ - "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R w KQkq - 0 1", - "r3k2r/2p1p1qp/2npb3/1p3Q2/p3P1pP/P1PB4/1P1PNPP1/R3K2R b KQkq - 0 1", - ]; - - #[test] - fn test_make_move_capture() -> Result<(), String> { - let mut game = from_fen(FEN_CAPTURE[0])?; - let mv = Move::new_with_type(21, 37, MoveType::Capture); - game.board.make_move(mv, Color::White); - assert_eq!(game, from_fen(FEN_CAPTURE[1])?); - - Ok(()) - } - - const FEN_EN_PASSANT: [&str; 2] = [ - "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R b KQkq h3 0 1", - "r3k2r/2p1p1qp/2npb3/1p3p2/p3P3/P1PB1Q1p/1P1PNPP1/R3K2R w KQkq - 0 1", - ]; - - #[test] - fn test_make_move_en_passant() -> Result<(), String> { - let mut game = from_fen(FEN_EN_PASSANT[0])?; - let mv = Move::new_with_type(30, 23, MoveType::EnPassant); - game.board.make_move(mv, Color::Black); - assert_eq!(game, from_fen(FEN_EN_PASSANT[1])?); - - Ok(()) - } - - const FEN_DOUBLE_PUSH: [&str; 2] = [ - "rnbqkbnr/p1pppppp/8/8/1p5P/8/PPPPPPP1/RNBQKBNR w KQkq - 0 1", - "rnbqkbnr/p1pppppp/8/8/1pP4P/8/PP1PPPP1/RNBQKBNR b KQkq c3 0 1", - ]; - - #[test] - fn test_make_move_double_push() -> Result<(), String> { - let mut game = from_fen(FEN_DOUBLE_PUSH[0])?; - let mv = Move::new_with_type(10, 26, MoveType::DoublePush); - game.board.make_move(mv, Color::White); - assert_eq!(game, from_fen(FEN_DOUBLE_PUSH[1])?); - Ok(()) - } - - const FEN_PROMOTION: [&str; 2] = [ - "8/6P1/4n2b/1p6/1Kp5/6n1/4b3/1k6 w - - 0 1", - "6Q1/8/4n2b/1p6/1Kp5/6n1/4b3/1k6 b - - 0 1", - ]; - - #[test] - fn test_make_move_promotion() -> Result<(), String> { - let mut game = from_fen(FEN_PROMOTION[0])?; - let mv = Move::new_with_type(54, 62, MoveType::Promotion(Promote::Queen)); - game.board.make_move(mv, Color::White); - assert_eq!(game, from_fen(FEN_PROMOTION[1])?); - - Ok(()) - } - - const FEN_PROMOTION_CAPTURE: [&str; 2] = [ - "5n2/6P1/7b/1p6/1Kp5/6n1/4b3/1k6 w - - 0 1", - "5Q2/8/7b/1p6/1Kp5/6n1/4b3/1k6 b - - 0 1", - ]; - - #[test] - fn test_make_move_promotion_capture() -> Result<(), String> { - let mut game = from_fen(FEN_PROMOTION_CAPTURE[0])?; - let mv = Move::new_with_type(54, 61, MoveType::PromotionCapture(Promote::Queen)); - game.board.make_move(mv, Color::White); - assert_eq!(game, from_fen(FEN_PROMOTION_CAPTURE[1])?); - - Ok(()) - } - - const FEN_CASTLE: [&str; 2] = [ - "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R w KQkq - 0 1", - "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R4RK1 b kq - 0 1", - ]; - - #[test] - fn test_make_move_castle() -> Result<(), String> { - let mut game = from_fen(FEN_CASTLE[0])?; - 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(()) - } } diff --git a/src/move.rs b/src/move.rs index bbb2820..2f8a79c 100644 --- a/src/move.rs +++ b/src/move.rs @@ -1,3 +1,10 @@ +use crate::{ + bitboard::{have_common_bit, lsb, square_to_bitboard}, + board::{Board, Color, Kind, Piece}, + square::Square, + state::Castle, +}; + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum Promote { Knight, @@ -42,5 +49,229 @@ impl Move { } } +impl Board { + pub fn make_move(&mut self, mv: Move, color: Color) -> bool { + self.update_board_state(&mv, color); + + let pieces = match color { + Color::White => &self.white_pieces, + Color::Black => &self.black_pieces, + }; + + self.state.update_castling_state_quiet(mv.src, color); + self.state + .update_castling_state_capture(mv.dst, Color::opponent_color(color)); + self.state.change_side(); + + let own_king_square = lsb(pieces[Kind::King.idx()].bitboard); + self.is_move_legit(own_king_square, Color::opponent_color(color)) + } + + pub fn update_board_state(&mut self, mv: &Move, color: Color) { + let (own_pieces, opponent_pieces) = match color { + Color::White => (&mut self.white_pieces, &mut self.black_pieces), + Color::Black => (&mut self.black_pieces, &mut self.white_pieces), + }; + + match &mv.move_type { + MoveType::Quiet => { + Self::move_piece(mv.src, mv.dst, own_pieces); + self.state.set_en_passant_target_square(None); + } + MoveType::Capture => { + Self::move_piece(mv.src, mv.dst, own_pieces); + Self::remove_piece(mv.dst, opponent_pieces); + self.state.set_en_passant_target_square(None); + } + MoveType::EnPassant => { + Self::move_piece(mv.src, mv.dst, own_pieces); + Self::remove_pawn_enpassant(mv.dst, opponent_pieces, color); + self.state.set_en_passant_target_square(None); + } + MoveType::DoublePush => { + Self::move_piece(mv.src, mv.dst, own_pieces); + let en_passant = match color { + Color::White => Some(mv.src + 8), + Color::Black => Some(mv.src - 8), + }; + self.state.set_en_passant_target_square(en_passant); + } + MoveType::Promotion(promote) => { + Self::remove_piece(mv.src, own_pieces); + Self::promote_piece(mv.dst, own_pieces, *promote); + self.state.set_en_passant_target_square(None); + } + MoveType::PromotionCapture(promote) => { + Self::remove_piece(mv.src, own_pieces); + Self::remove_piece(mv.dst, opponent_pieces); + Self::promote_piece(mv.dst, own_pieces, *promote); + self.state.set_en_passant_target_square(None); + } + MoveType::Castle => { + Self::move_piece(mv.src, mv.dst, own_pieces); + Self::move_rook_castle(mv.dst, own_pieces); + self.state.set_en_passant_target_square(None); + self.state.set_castling_ability(color, Castle::None) + } + } + } + + fn move_piece(src: usize, dst: usize, pieces: &mut [Piece; 6]) { + for p in pieces.iter_mut() { + if have_common_bit(p.bitboard, square_to_bitboard(src)) { + p.bitboard &= !square_to_bitboard(src); + p.bitboard |= square_to_bitboard(dst); + break; + } + } + } + + fn move_rook_castle(king_dst: usize, pieces: &mut [Piece; 6]) { + let (rook_src, rook_dst) = match king_dst { + Square::C1 | Square::C8 => (king_dst - 2, king_dst + 1), + Square::G1 | Square::G8 => (king_dst + 1, king_dst - 1), + _ => return, + }; + + Self::move_piece(rook_src, rook_dst, pieces); + } + + fn promote_piece(square: usize, pieces: &mut [Piece; 6], promote: Promote) { + match promote { + Promote::Knight => pieces[Kind::Knight.idx()].bitboard |= square_to_bitboard(square), + Promote::Bishop => pieces[Kind::Bishop.idx()].bitboard |= square_to_bitboard(square), + Promote::Rook => pieces[Kind::Rook.idx()].bitboard |= square_to_bitboard(square), + Promote::Queen => pieces[Kind::Queen.idx()].bitboard |= square_to_bitboard(square), + }; + } + + fn remove_piece(square: usize, pieces: &mut [Piece; 6]) { + for p in pieces.iter_mut() { + if have_common_bit(p.bitboard, square_to_bitboard(square)) { + p.bitboard &= !(square_to_bitboard(square)); + break; + } + } + } + + fn remove_pawn_enpassant(square: usize, pieces: &mut [Piece; 6], color: Color) { + let piece_to_remove = match color { + Color::White => square - 8, + Color::Black => square + 8, + }; + + Self::remove_piece(piece_to_remove, pieces); + } +} + #[cfg(test)] -mod tests {} +mod tests { + use crate::{ + board::Color, + fen::from_fen, + r#move::{Move, MoveType, Promote}, + }; + + const FEN_QUIET: [&str; 2] = [ + "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R w KQkq - 0 1", + "r3k2r/2p1p1qp/2npb3/1p3p2/pP2P1pP/P1PB1Q2/3PNPP1/R3K2R b KQkq b3 0 1", + ]; + + #[test] + fn test_make_move_quiet() -> Result<(), String> { + let mut game = from_fen(FEN_QUIET[0])?; + let mv = Move::new_with_type(9, 25, MoveType::DoublePush); + game.board.make_move(mv, Color::White); + assert_eq!(game, from_fen(FEN_QUIET[1])?); + + Ok(()) + } + + const FEN_CAPTURE: [&str; 2] = [ + "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R w KQkq - 0 1", + "r3k2r/2p1p1qp/2npb3/1p3Q2/p3P1pP/P1PB4/1P1PNPP1/R3K2R b KQkq - 0 1", + ]; + + #[test] + fn test_make_move_capture() -> Result<(), String> { + let mut game = from_fen(FEN_CAPTURE[0])?; + let mv = Move::new_with_type(21, 37, MoveType::Capture); + game.board.make_move(mv, Color::White); + assert_eq!(game, from_fen(FEN_CAPTURE[1])?); + + Ok(()) + } + + const FEN_EN_PASSANT: [&str; 2] = [ + "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R b KQkq h3 0 1", + "r3k2r/2p1p1qp/2npb3/1p3p2/p3P3/P1PB1Q1p/1P1PNPP1/R3K2R w KQkq - 0 1", + ]; + + #[test] + fn test_make_move_en_passant() -> Result<(), String> { + let mut game = from_fen(FEN_EN_PASSANT[0])?; + let mv = Move::new_with_type(30, 23, MoveType::EnPassant); + game.board.make_move(mv, Color::Black); + assert_eq!(game, from_fen(FEN_EN_PASSANT[1])?); + + Ok(()) + } + + const FEN_DOUBLE_PUSH: [&str; 2] = [ + "rnbqkbnr/p1pppppp/8/8/1p5P/8/PPPPPPP1/RNBQKBNR w KQkq - 0 1", + "rnbqkbnr/p1pppppp/8/8/1pP4P/8/PP1PPPP1/RNBQKBNR b KQkq c3 0 1", + ]; + + #[test] + fn test_make_move_double_push() -> Result<(), String> { + let mut game = from_fen(FEN_DOUBLE_PUSH[0])?; + let mv = Move::new_with_type(10, 26, MoveType::DoublePush); + game.board.make_move(mv, Color::White); + assert_eq!(game, from_fen(FEN_DOUBLE_PUSH[1])?); + Ok(()) + } + + const FEN_PROMOTION: [&str; 2] = [ + "8/6P1/4n2b/1p6/1Kp5/6n1/4b3/1k6 w - - 0 1", + "6Q1/8/4n2b/1p6/1Kp5/6n1/4b3/1k6 b - - 0 1", + ]; + + #[test] + fn test_make_move_promotion() -> Result<(), String> { + let mut game = from_fen(FEN_PROMOTION[0])?; + let mv = Move::new_with_type(54, 62, MoveType::Promotion(Promote::Queen)); + game.board.make_move(mv, Color::White); + assert_eq!(game, from_fen(FEN_PROMOTION[1])?); + + Ok(()) + } + + const FEN_PROMOTION_CAPTURE: [&str; 2] = [ + "5n2/6P1/7b/1p6/1Kp5/6n1/4b3/1k6 w - - 0 1", + "5Q2/8/7b/1p6/1Kp5/6n1/4b3/1k6 b - - 0 1", + ]; + + #[test] + fn test_make_move_promotion_capture() -> Result<(), String> { + let mut game = from_fen(FEN_PROMOTION_CAPTURE[0])?; + let mv = Move::new_with_type(54, 61, MoveType::PromotionCapture(Promote::Queen)); + game.board.make_move(mv, Color::White); + assert_eq!(game, from_fen(FEN_PROMOTION_CAPTURE[1])?); + + Ok(()) + } + + const FEN_CASTLE: [&str; 2] = [ + "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R3K2R w KQkq - 0 1", + "r3k2r/2p1p1qp/2npb3/1p3p2/p3P1pP/P1PB1Q2/1P1PNPP1/R4RK1 b kq - 0 1", + ]; + + #[test] + fn test_make_move_castle() -> Result<(), String> { + let mut game = from_fen(FEN_CASTLE[0])?; + 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(()) + } +}