diff --git a/src/board/board.rs b/src/board/board.rs index 4174ccb..988c0e8 100644 --- a/src/board/board.rs +++ b/src/board/board.rs @@ -10,14 +10,13 @@ use crate::movegen::move_generator::{ bishop_pseudo_moves, king_pseudo_moves, knight_pseudo_moves, pawn_pseudo_moves, queen_pseudo_moves, rook_pseudo_moves, }; -use crate::movegen::r#move::{Move, MoveType, Promote}; +use crate::movegen::r#move::{Move, Promote}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Board { pub white_pieces: [Piece; 6], pub black_pieces: [Piece; 6], pub state: State, - pub history: History, } impl Board { @@ -40,7 +39,6 @@ impl Board { Piece::new(0x1000000000000000, PieceType::King, Color::Black), ], state: State::new(), - history: History::new(), } } @@ -63,7 +61,6 @@ impl Board { Piece::new(0x0, PieceType::King, Color::Black), ], state: State::new(), - history: History::new(), } } @@ -191,117 +188,7 @@ impl Board { None } - pub fn make_move(&mut self, mv: &Move) { - let color = self.state.current_player(); - let pawn_move = self.is_pawn_move(mv.src); - let mut en_passant_square = None; - self.history - .push_move_parameters(MoveParameters::build(&self, mv)); - - match &mv.move_type { - MoveType::Quiet => { - self.move_piece(mv.src, mv.dst); - } - MoveType::Capture => { - self.move_piece(mv.src, mv.dst); - self.remove_opponent_piece(mv.dst); - } - MoveType::EnPassant => { - self.move_piece(mv.src, mv.dst); - self.remove_pawn_enpassant(mv.dst, color); - } - MoveType::DoublePush => { - self.move_piece(mv.src, mv.dst); - en_passant_square = match color { - Color::White => Some(mv.src + 8), - Color::Black => Some(mv.src.saturating_sub(8)), - }; - } - MoveType::Promotion(promote) => { - self.remove_own_piece(mv.src); - self.promote_piece(mv.dst, promote); - } - MoveType::PromotionCapture(promote) => { - self.remove_own_piece(mv.src); - self.remove_opponent_piece(mv.dst); - self.promote_piece(mv.dst, promote); - } - MoveType::Castle => { - self.move_piece(mv.src, mv.dst); - self.move_rook_castle(mv.dst); - self.state.set_castling_ability(color, Castle::None); - } - } - - Self::update_game_state(&mut self.state, mv, color, pawn_move, en_passant_square); - } - - pub fn unmake_move(&mut self) { - let move_parameters = - std::mem::take(&mut self.history.pop_move_parameters()).unwrap_or_default(); - let color_before_move = self.state.change_side(); - self.state.revert_full_move(color_before_move); - self.state.en_passant_square = move_parameters.en_passant_square; - - if let Some(new_castling_ability) = move_parameters.castling_ability { - self.state.castling_ability = new_castling_ability; - } - - if let Some(new_halfmove_clock) = move_parameters.halfmove_clock { - self.state.halfmove_clock = new_halfmove_clock; - } - - let mv = move_parameters.mv.unwrap(); - - match &mv.move_type { - MoveType::Quiet | MoveType::DoublePush => { - self.move_piece(mv.dst, mv.src); - } - MoveType::Capture | MoveType::Promotion(_) | MoveType::PromotionCapture(_) => { - if let (Some(captured_piece_type), Some(promoted_piece_type)) = ( - move_parameters.captured_piece, - move_parameters.promoted_piece, - ) { - let (own_pieces, opponent_pieces) = self.all_pieces(); - - opponent_pieces[captured_piece_type].bitboard |= square_to_bitboard(mv.dst); - own_pieces[promoted_piece_type].bitboard &= !square_to_bitboard(mv.dst); - own_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(mv.src); - } else if let Some(captured_piece_type) = move_parameters.captured_piece { - self.move_piece(mv.dst, mv.src); - let opponent_pieces = self.opponent_pieces(); - opponent_pieces[captured_piece_type].bitboard |= square_to_bitboard(mv.dst); - } else if let Some(promoted_piece_type) = move_parameters.promoted_piece { - let own_pieces = self.own_pieces(); - - own_pieces[promoted_piece_type].bitboard &= !square_to_bitboard(mv.dst); - own_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(mv.src); - } - } - MoveType::EnPassant => { - self.move_piece(mv.dst, mv.src); - let enemy_pawn_square = match color_before_move { - Color::White => mv.dst - 8, - Color::Black => mv.dst + 8, - }; - let opponent_pieces = self.opponent_pieces(); - opponent_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(enemy_pawn_square); - } - MoveType::Castle => { - self.move_piece(mv.dst, mv.src); - let (rook_src, rook_dst) = match mv.dst { - Square::C1 | Square::C8 => (mv.dst - 2, mv.dst + 1), - Square::G1 | Square::G8 => (mv.dst + 1, mv.dst - 1), - _ => return, - }; - let own_pieces = self.own_pieces(); - own_pieces[PieceType::Rook].bitboard &= !square_to_bitboard(rook_dst); - own_pieces[PieceType::Rook].bitboard |= square_to_bitboard(rook_src); - } - } - } - - fn update_game_state( + pub fn update_game_state( state: &mut State, mv: &Move, color: Color, @@ -316,7 +203,7 @@ impl Board { state.change_side(); } - fn move_piece(&mut self, src: usize, dst: usize) { + pub fn move_piece(&mut self, src: usize, dst: usize) { let pieces = self.own_pieces(); pieces .iter_mut() @@ -327,7 +214,7 @@ impl Board { }); } - fn remove_own_piece(&mut self, square: usize) { + pub fn remove_own_piece(&mut self, square: usize) { let pieces = self.own_pieces(); pieces .iter_mut() @@ -337,7 +224,7 @@ impl Board { }); } - fn remove_opponent_piece(&mut self, square: usize) { + pub fn remove_opponent_piece(&mut self, square: usize) { let pieces = self.opponent_pieces(); pieces .iter_mut() @@ -347,7 +234,7 @@ impl Board { }); } - fn move_rook_castle(&mut self, king_dst: usize) { + pub fn move_rook_castle(&mut self, king_dst: usize) { 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), @@ -357,7 +244,7 @@ impl Board { self.move_piece(rook_src, rook_dst); } - fn promote_piece(&mut self, square: usize, promote: &Promote) { + pub fn promote_piece(&mut self, square: usize, promote: &Promote) { let pieces = self.own_pieces(); match promote { Promote::Knight => pieces[PieceType::Knight].bitboard |= square_to_bitboard(square), @@ -367,7 +254,7 @@ impl Board { }; } - fn remove_pawn_enpassant(&mut self, square: usize, color: Color) { + pub fn remove_pawn_enpassant(&mut self, square: usize, color: Color) { let piece_to_remove = match color { Color::White => square - 8, Color::Black => square + 8, @@ -376,26 +263,26 @@ impl Board { self.remove_opponent_piece(piece_to_remove); } - fn is_pawn_move(&mut self, square: usize) -> bool { + pub fn is_pawn_move(&mut self, square: usize) -> bool { let pieces = self.own_pieces(); have_common_bit(square_to_bitboard(square), pieces[PieceType::Pawn].bitboard) } - fn own_pieces(&mut self) -> &mut [Piece; 6] { + pub fn own_pieces(&mut self) -> &mut [Piece; 6] { match self.state.current_player() { Color::White => &mut self.white_pieces, Color::Black => &mut self.black_pieces, } } - fn opponent_pieces(&mut self) -> &mut [Piece; 6] { + pub fn opponent_pieces(&mut self) -> &mut [Piece; 6] { match self.state.current_player() { Color::White => &mut self.black_pieces, Color::Black => &mut self.white_pieces, } } - fn all_pieces(&mut self) -> (&mut [Piece; 6], &mut [Piece; 6]) { + pub fn all_pieces(&mut self) -> (&mut [Piece; 6], &mut [Piece; 6]) { match self.state.current_player() { Color::White => (&mut self.white_pieces, &mut self.black_pieces), Color::Black => (&mut self.black_pieces, &mut self.white_pieces), @@ -444,9 +331,7 @@ impl PieceType { use std::ops::{Index, IndexMut}; use super::bitboard::square_to_bitboard; -use super::history::{History, MoveParameters}; use super::square::Square; -use super::state::Castle; impl Index for [Piece] { type Output = Piece; @@ -516,7 +401,7 @@ mod tests { fn test_make_move_quiet() -> Result<(), String> { let mut game = from_fen(FEN)?; let b1c3 = Move::new(Square::F3, Square::E3); - game.board.make_move(&b1c3); + game.make_move(&b1c3); assert_eq!(game, from_fen(FEN_QUIET)?); @@ -529,7 +414,7 @@ mod tests { fn test_make_move_capture() -> Result<(), String> { let mut game = from_fen(FEN)?; let f3f5 = Move::new_with_type(Square::F3, Square::F5, MoveType::Capture); - game.board.make_move(&f3f5); + game.make_move(&f3f5); assert_eq!(game, from_fen(FEN_CAPTURE)?); @@ -543,7 +428,7 @@ mod tests { fn test_make_move_en_passant() -> Result<(), String> { let mut game = from_fen(FEN)?; let h5g6 = Move::new_with_type(Square::H5, Square::G6, MoveType::EnPassant); - game.board.make_move(&h5g6); + game.make_move(&h5g6); assert_eq!(game, from_fen(FEN_EN_PASSANT)?); @@ -557,7 +442,7 @@ mod tests { fn test_make_move_double_push() -> Result<(), String> { let mut game = from_fen(FEN)?; let b2b4 = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush); - game.board.make_move(&b2b4); + game.make_move(&b2b4); assert_eq!(game, from_fen(FEN_DOUBLE_PUSH)?); Ok(()) @@ -569,7 +454,7 @@ mod tests { fn test_make_move_promotion() -> Result<(), String> { let mut game = from_fen(FEN)?; let c7c8 = Move::new_with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen)); - game.board.make_move(&c7c8); + game.make_move(&c7c8); assert_eq!(game, from_fen(FEN_PROMOTION)?); Ok(()) @@ -586,7 +471,7 @@ mod tests { Square::B8, MoveType::PromotionCapture(Promote::Queen), ); - game.board.make_move(&c7b8); + game.make_move(&c7b8); assert_eq!(game, from_fen(FEN_PROMOTION_CAPTURE)?); Ok(()) @@ -598,7 +483,7 @@ mod tests { fn test_make_move_castle() -> Result<(), String> { let mut game = from_fen(FEN)?; let e1g1 = Move::new_with_type(Square::E1, Square::G1, MoveType::Castle); - game.board.make_move(&e1g1); + game.make_move(&e1g1); assert_eq!(game, from_fen(FEN_CASTLE)?); Ok(()) } diff --git a/src/board/fen.rs b/src/board/fen.rs index ea18c19..e343db9 100644 --- a/src/board/fen.rs +++ b/src/board/fen.rs @@ -25,7 +25,10 @@ pub fn from_fen(fen: &str) -> Result { halfmove_clock, fullmove_counter, )); - Ok(Game { board }) + Ok(Game { + board, + history: History::new(), + }) } pub fn piece_placement(pieces: &str) -> Result { @@ -123,6 +126,8 @@ fn castling_ability(castling: &str) -> Result<[Castle; 2], FenError> { use std::collections::HashMap; +use super::history::History; + fn en_passant_square(square: &str) -> Result, FenError> { let mut sqr = square.chars(); diff --git a/src/board/game.rs b/src/board/game.rs index 641aad5..d816daa 100644 --- a/src/board/game.rs +++ b/src/board/game.rs @@ -1,7 +1,16 @@ -use crate::board::fen::from_fen; +use crate::{ + board::fen::from_fen, + movegen::r#move::{Move, MoveType}, +}; use String as FenError; -use super::board::{Board, Color}; +use super::{ + bitboard::square_to_bitboard, + board::{Board, Color, PieceType}, + history::{History, MoveParameters}, + square::Square, + state::Castle, +}; impl PartialEq for Game { fn eq(&self, other: &Self) -> bool { @@ -14,12 +23,14 @@ impl PartialEq for Game { #[derive(Debug, Eq)] pub struct Game { pub board: Board, + pub history: History, } impl Game { pub const fn new() -> Self { Self { board: Board::new(), + history: History::new(), } } @@ -30,6 +41,118 @@ impl Game { pub const fn current_player(&self) -> Color { self.board.state.current_player() } + + pub fn make_move(&mut self, mv: &Move) { + let board = &mut self.board; + let color = board.state.current_player(); + let pawn_move = board.is_pawn_move(mv.src); + let mut en_passant_square = None; + self.history + .push_move_parameters(MoveParameters::build(board, mv)); + + match &mv.move_type { + MoveType::Quiet => { + board.move_piece(mv.src, mv.dst); + } + MoveType::Capture => { + board.move_piece(mv.src, mv.dst); + board.remove_opponent_piece(mv.dst); + } + MoveType::EnPassant => { + board.move_piece(mv.src, mv.dst); + board.remove_pawn_enpassant(mv.dst, color); + } + MoveType::DoublePush => { + board.move_piece(mv.src, mv.dst); + en_passant_square = match color { + Color::White => Some(mv.src + 8), + Color::Black => Some(mv.src.saturating_sub(8)), + }; + } + MoveType::Promotion(promote) => { + board.remove_own_piece(mv.src); + board.promote_piece(mv.dst, promote); + } + MoveType::PromotionCapture(promote) => { + board.remove_own_piece(mv.src); + board.remove_opponent_piece(mv.dst); + board.promote_piece(mv.dst, promote); + } + MoveType::Castle => { + board.move_piece(mv.src, mv.dst); + board.move_rook_castle(mv.dst); + board.state.set_castling_ability(color, Castle::None); + } + } + + Board::update_game_state(&mut board.state, mv, color, pawn_move, en_passant_square); + } + + pub fn unmake_move(&mut self) { + let board = &mut self.board; + let move_parameters = + std::mem::take(&mut self.history.pop_move_parameters()).unwrap_or_default(); + let color_before_move = board.state.change_side(); + board.state.revert_full_move(color_before_move); + board.state.en_passant_square = move_parameters.en_passant_square; + + if let Some(new_castling_ability) = move_parameters.castling_ability { + board.state.castling_ability = new_castling_ability; + } + + if let Some(new_halfmove_clock) = move_parameters.halfmove_clock { + board.state.halfmove_clock = new_halfmove_clock; + } + + let mv = move_parameters.mv.unwrap(); + + match &mv.move_type { + MoveType::Quiet | MoveType::DoublePush => { + board.move_piece(mv.dst, mv.src); + } + MoveType::Capture | MoveType::Promotion(_) | MoveType::PromotionCapture(_) => { + if let (Some(captured_piece_type), Some(promoted_piece_type)) = ( + move_parameters.captured_piece, + move_parameters.promoted_piece, + ) { + let (own_pieces, opponent_pieces) = board.all_pieces(); + + opponent_pieces[captured_piece_type].bitboard |= square_to_bitboard(mv.dst); + own_pieces[promoted_piece_type].bitboard &= !square_to_bitboard(mv.dst); + own_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(mv.src); + } else if let Some(captured_piece_type) = move_parameters.captured_piece { + board.move_piece(mv.dst, mv.src); + let opponent_pieces = board.opponent_pieces(); + opponent_pieces[captured_piece_type].bitboard |= square_to_bitboard(mv.dst); + } else if let Some(promoted_piece_type) = move_parameters.promoted_piece { + let own_pieces = board.own_pieces(); + + own_pieces[promoted_piece_type].bitboard &= !square_to_bitboard(mv.dst); + own_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(mv.src); + } + } + MoveType::EnPassant => { + board.move_piece(mv.dst, mv.src); + let enemy_pawn_square = match color_before_move { + Color::White => mv.dst - 8, + Color::Black => mv.dst + 8, + }; + let opponent_pieces = board.opponent_pieces(); + opponent_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(enemy_pawn_square); + } + MoveType::Castle => { + board.move_piece(mv.dst, mv.src); + let (rook_src, rook_dst) = match mv.dst { + Square::C1 | Square::C8 => (mv.dst - 2, mv.dst + 1), + Square::G1 | Square::G8 => (mv.dst + 1, mv.dst - 1), + _ => return, + }; + let own_pieces = board.own_pieces(); + own_pieces[PieceType::Rook].bitboard &= !square_to_bitboard(rook_dst); + own_pieces[PieceType::Rook].bitboard |= square_to_bitboard(rook_src); + } + } + } } impl Default for Game { diff --git a/src/board/history.rs b/src/board/history.rs index e56c308..2643039 100644 --- a/src/board/history.rs +++ b/src/board/history.rs @@ -109,14 +109,14 @@ mod tests { let mut game = from_fen(FEN)?; let board_before_make = game.board.clone(); let mv = Move::new_with_type(Square::B2, Square::B3, MoveType::Quiet); - game.board.make_move(&mv); - game.board.unmake_move(); + game.make_move(&mv); + game.unmake_move(); assert_eq!(board_before_make, game.board); let mv = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush); - game.board.make_move(&mv); - game.board.unmake_move(); + game.make_move(&mv); + game.unmake_move(); assert_eq!(board_before_make, game.board); @@ -128,14 +128,14 @@ mod tests { let mut game = from_fen(FEN)?; let board_before_make = game.board.clone(); let mv = Move::new_with_type(Square::D3, Square::B5, MoveType::Capture); - game.board.make_move(&mv); - game.board.unmake_move(); + game.make_move(&mv); + game.unmake_move(); assert_eq!(board_before_make, game.board); let mv = Move::new_with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen)); - game.board.make_move(&mv); - game.board.unmake_move(); + game.make_move(&mv); + game.unmake_move(); assert_eq!(board_before_make, game.board); @@ -144,8 +144,8 @@ mod tests { Square::B8, MoveType::PromotionCapture(Promote::Queen), ); - game.board.make_move(&mv); - game.board.unmake_move(); + game.make_move(&mv); + game.unmake_move(); assert_eq!(board_before_make, game.board); @@ -157,8 +157,8 @@ mod tests { let mut game = from_fen(FEN_2)?; let board_before_make = game.board.clone(); let mv = Move::new_with_type(Square::A4, Square::B3, MoveType::EnPassant); - game.board.make_move(&mv); - game.board.unmake_move(); + game.make_move(&mv); + game.unmake_move(); assert_eq!(board_before_make, game.board); @@ -170,8 +170,8 @@ mod tests { let mut game = from_fen(FEN)?; let board_before_make = game.board.clone(); let mv = Move::new_with_type(Square::E1, Square::C1, MoveType::Castle); - game.board.make_move(&mv); - game.board.unmake_move(); + game.make_move(&mv); + game.unmake_move(); assert_eq!(board_before_make, game.board); diff --git a/src/interface/uci.rs b/src/interface/uci.rs index fa3252a..a011831 100644 --- a/src/interface/uci.rs +++ b/src/interface/uci.rs @@ -111,7 +111,7 @@ pub fn uci_position(position: &mut SplitWhitespace) -> Result { for mv_str in position { let mv = Move::parse_from_str(&game.board, mv_str)?; - game.board.make_move(&mv); + game.make_move(&mv); } Ok(game) diff --git a/src/search/negamax.rs b/src/search/negamax.rs index 250f723..d4a37b8 100644 --- a/src/search/negamax.rs +++ b/src/search/negamax.rs @@ -26,16 +26,16 @@ pub fn negamax( pseudo_legal_moves.sort_unstable_by_key(|mv| score_by_mvv_lva(&game.board, *mv)); for mv in pseudo_legal_moves { - game.board.make_move(&mv); + game.make_move(&mv); if game.board.king_under_check(color) { - game.board.unmake_move(); + game.unmake_move(); continue; } legal_moves += 1; let move_score = -negamax(game, -beta, -alpha, depth - 1, plies + 1).1; - game.board.unmake_move(); + game.unmake_move(); if move_score > best_score { best_score = move_score; diff --git a/src/search/perft.rs b/src/search/perft.rs index c7b76af..529e6d6 100644 --- a/src/search/perft.rs +++ b/src/search/perft.rs @@ -11,7 +11,7 @@ pub fn driver(game: &mut Game, nodes: &mut u64, depth: u8) { for mv in pseudo_moves { let original_board = game.board.clone(); - game.board.make_move(&mv); + game.make_move(&mv); if game.board.king_under_check(color) { game.board = original_board; diff --git a/src/search/quiescence.rs b/src/search/quiescence.rs index c3dc105..4ede767 100644 --- a/src/search/quiescence.rs +++ b/src/search/quiescence.rs @@ -33,15 +33,15 @@ pub fn quiescence(game: &mut Game, mut alpha: i32, beta: i32) -> (Option, captures.sort_unstable_by_key(|mv| score_by_mvv_lva(&game.board, *mv)); for mv in captures { - game.board.make_move(&mv); + game.make_move(&mv); if game.board.king_under_check(color) { - game.board.unmake_move(); + game.unmake_move(); continue; } let move_score = -quiescence(game, -beta, -alpha).1; - game.board.unmake_move(); + game.unmake_move(); if move_score >= beta { return (Some(mv), beta);