use strum_macros::EnumIter; use u64 as Bitboard; use crate::board::bitboard::{have_common_bit, lsb, square_to_bitboard}; 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, MoveType, Promote}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Board { pub pieces: [Bitboard; 6], pub color: [Bitboard; 2], pub state: State, } impl Board { pub const fn startpos() -> Self { Self { pieces: [ 0xff00000000ff00, 0x4200000000000042, 0x2400000000000024, 0x8100000000000081, 0x800000000000008, 0x1000000000000010, ], color: [0xffff, 0xffff000000000000], state: State::new(), } } pub const fn new_empty() -> Self { Self { pieces: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0], color: [0x0, 0x0], state: State::new(), } } pub fn is_attacked(&self, square: usize, opponent_color: Color) -> bool { let all_occupancies = self.color[Color::White] | self.color[Color::Black]; let own_color = opponent_color.opponent(); let opponent_color_bb = &self.color[opponent_color]; let pawns = self.pieces[PieceType::Pawn] & opponent_color_bb; let knights = self.pieces[PieceType::Knight] & opponent_color_bb; let bishops = self.pieces[PieceType::Bishop] & opponent_color_bb; let rooks = self.pieces[PieceType::Rook] & opponent_color_bb; let queens = self.pieces[PieceType::Queen] & opponent_color_bb; let king = self.pieces[PieceType::King] & opponent_color_bb; 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 = lsb(self.pieces[PieceType::King] & self.color[color]); 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_all_captures(&self) -> Vec { self.pseudo_moves_all() .into_iter() .filter(|m| { matches!( m.move_type, MoveType::Capture | MoveType::PromotionCapture(_) ) }) .collect() } pub fn pseudo_moves(&self, color: Color, piece_type: PieceType) -> Vec { let all_occupancies = self.color[Color::White] | self.color[Color::Black]; let pieces = self.pieces[piece_type] & self.color[color]; let own_occupancies = self.color[color]; let opponent_occupancies = self.color[color.opponent()]; 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, bb: Bitboard, piece_type: PieceType, color: Color) { self.pieces[piece_type] |= bb; self.color[color] |= bb; } pub fn set_state(&mut self, state: State) { self.state = state; } pub fn is_pawn_move(&self, square: usize) -> bool { let side = self.state.current_player(); let own_pawns = self.pieces[PieceType::Pawn] & self.color[side]; have_common_bit(square_to_bitboard(square), own_pawns) } pub fn move_piece(&mut self, src: usize, dst: usize, piece_type: PieceType) { self.pieces[piece_type] &= !square_to_bitboard(src); self.pieces[piece_type] |= square_to_bitboard(dst); self.color[self.state.current_player()] &= !square_to_bitboard(src); self.color[self.state.current_player()] |= square_to_bitboard(dst); } pub fn insert_own_piece(&mut self, square: usize, piece_type: PieceType) { self.pieces[piece_type] |= square_to_bitboard(square); self.color[self.state.current_player()] |= square_to_bitboard(square); } pub fn insert_opponent_piece(&mut self, square: usize, piece_type: PieceType) { self.pieces[piece_type] |= square_to_bitboard(square); self.color[self.state.next_player()] |= square_to_bitboard(square); } pub fn remove_own_piece(&mut self, square: usize, piece_type: PieceType) { self.pieces[piece_type] &= !square_to_bitboard(square); self.color[self.state.current_player()] &= !square_to_bitboard(square); } pub fn remove_opponent_piece(&mut self, square: usize, piece_type: PieceType) { self.pieces[piece_type] &= !square_to_bitboard(square); self.color[self.state.next_player()] &= !square_to_bitboard(square); } pub fn promote_piece(&mut self, square: usize, promote: &Promote) { match promote { Promote::Knight => self.pieces[PieceType::Knight] |= square_to_bitboard(square), Promote::Bishop => self.pieces[PieceType::Bishop] |= square_to_bitboard(square), Promote::Rook => self.pieces[PieceType::Rook] |= square_to_bitboard(square), Promote::Queen => self.pieces[PieceType::Queen] |= square_to_bitboard(square), }; self.color[self.state.current_player()] |= square_to_bitboard(square); } } impl Default for Board { fn default() -> Self { Self::new_empty() } } #[derive(Debug, PartialEq, Eq, Clone, Copy, EnumIter)] pub enum PieceType { Pawn, Knight, Bishop, Rook, Queen, King, } impl PieceType { pub const fn idx(self) -> usize { self as usize } pub const fn score(self) -> i32 { match self { Self::Pawn => 100, Self::Knight => 320, Self::Bishop => 330, Self::Rook => 500, Self::Queen => 900, Self::King => 20000, } } } use std::ops::{Index, IndexMut}; impl Index for [T] { type Output = T; fn index(&self, piece_type: PieceType) -> &Self::Output { &self[piece_type.idx()] } } impl IndexMut for [T] { fn index_mut(&mut self, piece_type: PieceType) -> &mut Self::Output { &mut self[piece_type.idx()] } } #[derive(Debug, PartialEq, Eq, Clone, Copy, EnumIter)] pub enum Color { White, Black, } impl Color { pub const fn idx(self) -> usize { self as usize } pub const fn opponent(self) -> Self { match self { Self::White => Self::Black, Self::Black => Self::White, } } } impl Index for [T] { type Output = T; fn index(&self, color: Color) -> &Self::Output { &self[color.idx()] } } impl IndexMut for [T] { fn index_mut(&mut self, color: Color) -> &mut Self::Output { &mut self[color.idx()] } } #[cfg(test)] mod tests { use crate::{board::fen::from_fen, movegen::attack_generator::init_attacks}; 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.color[Color::White], 0x40000002000000); assert_eq!(new_game.board.color[Color::Black], 0x900204401002); assert_eq!( new_game.board.color[Color::White] | new_game.board.color[Color::Black], 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(()) } }