use u64 as Bitboard; use crate::board::bitboard::{have_common_bit, lsb}; use crate::board::state::State; use crate::movegen::attack_generator::{ fetch_bishop_attacks, fetch_king_attacks, fetch_knight_attacks, fetch_pawn_attacks, fetch_queen_attacks, fetch_rook_attacks, }; 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, Promote}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Board { pub white_pieces: [Piece; 6], pub black_pieces: [Piece; 6], pub state: State, } impl Board { pub const fn new() -> Self { Self { white_pieces: [ Piece::new(0xff00, PieceType::Pawn, Color::White), Piece::new(0x42, PieceType::Knight, Color::White), Piece::new(0x24, PieceType::Bishop, Color::White), Piece::new(0x81, PieceType::Rook, Color::White), Piece::new(0x8, PieceType::Queen, Color::White), Piece::new(0x10, PieceType::King, Color::White), ], black_pieces: [ Piece::new(0xff000000000000, PieceType::Pawn, Color::Black), Piece::new(0x4200000000000000, PieceType::Knight, Color::Black), Piece::new(0x2400000000000000, PieceType::Bishop, Color::Black), Piece::new(0x8100000000000000, PieceType::Rook, Color::Black), Piece::new(0x800000000000000, PieceType::Queen, Color::Black), Piece::new(0x1000000000000000, PieceType::King, Color::Black), ], state: State::new(), } } pub const fn empty_board() -> Self { Self { white_pieces: [ Piece::new(0x0, PieceType::Pawn, Color::White), Piece::new(0x0, PieceType::Knight, Color::White), Piece::new(0x0, PieceType::Bishop, Color::White), Piece::new(0x0, PieceType::Rook, Color::White), Piece::new(0x0, PieceType::Queen, Color::White), Piece::new(0x0, PieceType::King, Color::White), ], black_pieces: [ Piece::new(0x0, PieceType::Pawn, Color::Black), Piece::new(0x0, PieceType::Knight, Color::Black), Piece::new(0x0, PieceType::Bishop, Color::Black), Piece::new(0x0, PieceType::Rook, Color::Black), Piece::new(0x0, PieceType::Queen, Color::Black), Piece::new(0x0, PieceType::King, Color::Black), ], state: State::new(), } } pub fn white_occupancies(&self) -> Bitboard { self.white_pieces.iter().fold(0, |acc, p| p.bitboard | acc) } pub fn black_occupancies(&self) -> Bitboard { self.black_pieces.iter().fold(0, |acc, p| p.bitboard | acc) } pub fn all_occupancies(&self) -> Bitboard { self.white_occupancies() | self.black_occupancies() } pub fn is_attacked(&self, square: usize, opponent_color: Color) -> bool { let all_occupancies = self.all_occupancies(); let (opponent, own_color) = match opponent_color { Color::Black => (&self.black_pieces, Color::White), Color::White => (&self.white_pieces, Color::Black), }; let pawns = opponent[PieceType::Pawn].bitboard; let knights = opponent[PieceType::Knight].bitboard; let bishops = opponent[PieceType::Bishop].bitboard; let rooks = opponent[PieceType::Rook].bitboard; let queens = opponent[PieceType::Queen].bitboard; let king = opponent[PieceType::King].bitboard; have_common_bit(pawns, fetch_pawn_attacks(square, own_color)) || have_common_bit(knights, fetch_knight_attacks(square)) || have_common_bit(bishops, fetch_bishop_attacks(all_occupancies, square)) || have_common_bit(rooks, fetch_rook_attacks(all_occupancies, square)) || have_common_bit(queens, fetch_queen_attacks(all_occupancies, square)) || have_common_bit(king, fetch_king_attacks(square)) } pub fn king_under_check(&self, color: Color) -> bool { let own_king_square = match color { Color::White => lsb(self.white_pieces[PieceType::King].bitboard), Color::Black => lsb(self.black_pieces[PieceType::King].bitboard), }; self.is_attacked(own_king_square, color.opponent()) } pub fn pseudo_moves_all(&self) -> Vec { let color = self.state.current_player(); let mut moves = vec![]; moves.extend(self.pseudo_moves(color, PieceType::Pawn)); moves.extend(self.pseudo_moves(color, PieceType::Knight)); moves.extend(self.pseudo_moves(color, PieceType::Bishop)); moves.extend(self.pseudo_moves(color, PieceType::Rook)); moves.extend(self.pseudo_moves(color, PieceType::Queen)); moves.extend(self.pseudo_moves(color, PieceType::King)); moves } pub fn pseudo_moves(&self, color: Color, piece_type: PieceType) -> Vec { let all_occupancies = self.all_occupancies(); let (pieces, opponent_occupancies, own_occupancies) = match color { Color::White => ( self.white_pieces[piece_type].bitboard, self.black_occupancies(), self.white_occupancies(), ), Color::Black => ( self.black_pieces[piece_type].bitboard, self.white_occupancies(), self.black_occupancies(), ), }; match piece_type { PieceType::Pawn => pawn_pseudo_moves( pieces, all_occupancies, opponent_occupancies, self.state.en_passant_square(), color, ), PieceType::Knight => knight_pseudo_moves(pieces, all_occupancies, own_occupancies), PieceType::Bishop => bishop_pseudo_moves(pieces, all_occupancies, own_occupancies), PieceType::Rook => rook_pseudo_moves(pieces, all_occupancies, own_occupancies), PieceType::Queen => queen_pseudo_moves(pieces, all_occupancies, own_occupancies), PieceType::King => { king_pseudo_moves(pieces, all_occupancies, own_occupancies, self, color) } } } pub fn set_piece(&mut self, piece: &Piece) { match piece.color { Color::Black => self.black_pieces[piece.piece_type].bitboard |= piece.bitboard, Color::White => self.white_pieces[piece.piece_type].bitboard |= piece.bitboard, }; } pub fn set_state(&mut self, state: State) { self.state = state; } pub fn update_game_state( state: &mut State, mv: &Move, color: Color, pawn_move: bool, en_passant_square: Option, ) { state.set_en_passant_square(en_passant_square); state.update_castling_state_quiet(mv.src, color); state.update_castling_state_capture(mv.dst, color.opponent()); state.update_half_move(mv.move_type, pawn_move); state.update_full_move(color); state.change_side(); } pub fn move_piece(&mut self, src: usize, dst: usize) { let pieces = self.own_pieces(); pieces .iter_mut() .filter(|p| have_common_bit(p.bitboard, square_to_bitboard(src))) .for_each(|p| { p.bitboard &= !square_to_bitboard(src); p.bitboard |= square_to_bitboard(dst); }); //TODO: // pieces[piece_type].bitboard &= !square_to_bitboard(src); // pieces[piece_type].bitboard |= square_to_bitboard(dst); } pub fn remove_own_piece(&mut self, square: usize) { let pieces = self.own_pieces(); pieces .iter_mut() .filter(|p| have_common_bit(p.bitboard, square_to_bitboard(square))) .for_each(|p| { p.bitboard &= !square_to_bitboard(square); }); } pub fn remove_opponent_piece(&mut self, square: usize) { let pieces = self.opponent_pieces(); pieces .iter_mut() .filter(|p| have_common_bit(p.bitboard, square_to_bitboard(square))) .for_each(|p| { p.bitboard &= !square_to_bitboard(square); }); } 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), Promote::Bishop => pieces[PieceType::Bishop].bitboard |= square_to_bitboard(square), Promote::Rook => pieces[PieceType::Rook].bitboard |= square_to_bitboard(square), Promote::Queen => pieces[PieceType::Queen].bitboard |= square_to_bitboard(square), }; } 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) } 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, } } 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, } } 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), } } } impl Default for Board { fn default() -> Self { Self::new() } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct Piece { pub bitboard: Bitboard, pub piece_type: PieceType, pub color: Color, } impl Piece { pub const fn new(bitboard: Bitboard, piece_type: PieceType, color: Color) -> Self { Self { bitboard, piece_type, color, } } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum PieceType { Pawn, Knight, Bishop, Rook, Queen, King, } impl PieceType { pub const fn idx(self) -> usize { self as usize } } use std::ops::{Index, IndexMut}; use super::bitboard::square_to_bitboard; impl Index for [Piece] { type Output = Piece; fn index(&self, piece_type: PieceType) -> &Self::Output { &self[piece_type.idx()] } } impl IndexMut for [Piece] { fn index_mut(&mut self, piece_type: PieceType) -> &mut Self::Output { &mut self[piece_type.idx()] } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Color { White, Black, } impl Color { pub const fn opponent(self) -> Self { match self { Self::White => Self::Black, Self::Black => Self::White, } } } #[cfg(test)] mod tests { use crate::{ board::{fen::from_fen, square::Square}, movegen::{attack_generator::init_attacks, r#move::MoveType}, }; use super::*; const FEN_EXAMPLE: [&str; 1] = ["8/6P1/4n2b/1p6/1Kp5/6n1/4b3/1k6 w - - 0 1"]; #[test] fn test_occupancies() -> Result<(), String> { let new_game = from_fen(FEN_EXAMPLE[0])?; assert_eq!(new_game.board.white_occupancies(), 0x40000002000000); assert_eq!(new_game.board.black_occupancies(), 0x900204401002); assert_eq!(new_game.board.all_occupancies(), 0x40900206401002); Ok(()) } #[test] fn test_is_attacked() -> Result<(), String> { let new_game = from_fen(FEN_EXAMPLE[0])?; init_attacks(); assert!(new_game.board.is_attacked(54, Color::Black)); Ok(()) } const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1"; const FEN_QUIET: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2BQ3/1P1PNPP1/R3K2R b KQk - 1 1"; #[test] fn test_make_move_quiet() -> Result<(), String> { let mut game = from_fen(FEN)?; let b1c3 = Move::new(Square::F3, Square::E3); game.make_move(&b1c3); assert_eq!(game, from_fen(FEN_QUIET)?); Ok(()) } const FEN_CAPTURE: &str = "1r2k2r/2P1pq1p/2npb3/1p3QpP/p3P3/P2B4/1P1PNPP1/R3K2R b KQk - 0 1"; #[test] 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.make_move(&f3f5); assert_eq!(game, from_fen(FEN_CAPTURE)?); Ok(()) } const FEN_EN_PASSANT: &str = "1r2k2r/2P1pq1p/2npb1P1/1p3p2/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1"; #[test] 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.make_move(&h5g6); assert_eq!(game, from_fen(FEN_EN_PASSANT)?); Ok(()) } const FEN_DOUBLE_PUSH: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/pP2P3/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1"; #[test] 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.make_move(&b2b4); assert_eq!(game, from_fen(FEN_DOUBLE_PUSH)?); Ok(()) } const FEN_PROMOTION: &str = "1rQ1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1"; #[test] 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.make_move(&c7c8); assert_eq!(game, from_fen(FEN_PROMOTION)?); Ok(()) } const FEN_PROMOTION_CAPTURE: &str = "1Q2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1"; #[test] fn test_make_move_promotion_capture() -> Result<(), String> { let mut game = from_fen(FEN)?; let c7b8 = Move::new_with_type( Square::C7, Square::B8, MoveType::PromotionCapture(Promote::Queen), ); game.make_move(&c7b8); assert_eq!(game, from_fen(FEN_PROMOTION_CAPTURE)?); Ok(()) } const FEN_CASTLE: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R4RK1 b k - 1 1"; #[test] 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.make_move(&e1g1); assert_eq!(game, from_fen(FEN_CASTLE)?); Ok(()) } }