From d9e76a224f45dd864e8ae8406e0cd8025f8d5c55 Mon Sep 17 00:00:00 2001 From: stefiosif Date: Sun, 12 Jan 2025 20:02:16 +0200 Subject: [PATCH] Reduce bitboards from 12 to 8, add color info in mailbox representation --- src/board/board.rs | 177 ++++++++++---------------------- src/board/fen.rs | 5 +- src/board/game.rs | 73 +++++++------ src/board/history.rs | 22 ++-- src/board/mailbox.rs | 73 ++++++------- src/board/state.rs | 7 ++ src/board/zobrist.rs | 37 +++---- src/evaluation/evaluation.rs | 35 +++---- src/movegen/attack_generator.rs | 128 ++++++++++------------- src/movegen/move.rs | 10 +- src/search/move_ordering.rs | 2 +- 11 files changed, 238 insertions(+), 331 deletions(-) diff --git a/src/board/board.rs b/src/board/board.rs index 81e5eca..00e9de5 100644 --- a/src/board/board.rs +++ b/src/board/board.rs @@ -1,3 +1,4 @@ +use strum_macros::EnumIter; use u64 as Bitboard; use crate::board::bitboard::{have_common_bit, lsb, square_to_bitboard}; @@ -14,62 +15,42 @@ use crate::movegen::r#move::{Move, MoveType, Promote}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Board { - pub white_pieces: [Piece; 6], - pub black_pieces: [Piece; 6], + pub pieces: [Bitboard; 6], + pub color: [Bitboard; 2], + 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), + pieces: [ + 0xff00000000ff00, + 0x4200000000000042, + 0x2400000000000024, + 0x8100000000000081, + 0x800000000000008, + 0x1000000000000010, ], + color: [0xffff, 0xffff000000000000], 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), - ], + pieces: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0], + color: [0x0, 0x0], state: State::new(), } } - pub fn white_occupancies(&self) -> Bitboard { - self.white_pieces.iter().fold(0, |acc, p| p.bitboard | acc) + fn white_occupancies(&self) -> Bitboard { + self.color[Color::White] } - pub fn black_occupancies(&self) -> Bitboard { - self.black_pieces.iter().fold(0, |acc, p| p.bitboard | acc) + fn black_occupancies(&self) -> Bitboard { + self.color[Color::Black] } pub fn all_occupancies(&self) -> Bitboard { @@ -78,17 +59,15 @@ impl Board { 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 own_color = opponent_color.opponent(); + let opponent_color_bb = &self.color[opponent_color]; - 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; + 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)) @@ -99,11 +78,7 @@ impl Board { } 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), - }; - + let own_king_square = lsb(self.pieces[PieceType::King] & self.color[color]); self.is_attacked(own_king_square, color.opponent()) } @@ -134,18 +109,9 @@ impl Board { 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(), - ), - }; + 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( @@ -165,70 +131,56 @@ impl Board { } } - 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_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(&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 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) { - let pieces = self.own_pieces(); - pieces[piece_type].bitboard &= !square_to_bitboard(src); - pieces[piece_type].bitboard |= square_to_bitboard(dst); + 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) { - let pieces = self.own_pieces(); - pieces[piece_type].bitboard |= square_to_bitboard(square); + 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) { - let pieces = self.opponent_pieces(); - pieces[piece_type].bitboard |= square_to_bitboard(square); + 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) { - let pieces = self.own_pieces(); - pieces[piece_type].bitboard &= !square_to_bitboard(square); + 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) { - let pieces = self.opponent_pieces(); - pieces[piece_type].bitboard &= !square_to_bitboard(square); + 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) { - 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), + 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); } } @@ -238,24 +190,7 @@ impl Default for Board { } } -#[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)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumIter)] pub enum PieceType { Pawn, Knight, @@ -286,7 +221,7 @@ impl IndexMut for [T] { } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumIter)] pub enum Color { White, Black, diff --git a/src/board/fen.rs b/src/board/fen.rs index 19ac09a..6c75c4e 100644 --- a/src/board/fen.rs +++ b/src/board/fen.rs @@ -1,4 +1,4 @@ -use crate::board::board::{Board, Color, Piece, PieceType}; +use crate::board::board::{Board, Color, PieceType}; use crate::board::game::Game; use crate::board::state::{Castle, State}; use String as FenError; @@ -71,7 +71,7 @@ pub fn piece_placement(pieces: &str) -> Result { )) } } { - board.set_piece(&Piece::new(1 << square, piece_type, color)); + board.set_piece(square_to_bitboard(square as usize), piece_type, color); file += 1; }; } @@ -132,6 +132,7 @@ fn castling_ability(castling: &str) -> Result<[Castle; 2], FenError> { use std::collections::HashMap; +use super::bitboard::square_to_bitboard; use super::history::History; use super::mailbox::Mailbox; use super::zobrist::zobrist_keys; diff --git a/src/board/game.rs b/src/board/game.rs index 37a07c7..8c3b931 100644 --- a/src/board/game.rs +++ b/src/board/game.rs @@ -15,8 +15,8 @@ use super::{ impl PartialEq for Game { fn eq(&self, other: &Self) -> bool { - self.board.white_pieces == other.board.white_pieces - && self.board.black_pieces == other.board.black_pieces + self.board.pieces == other.board.pieces + && self.board.color == other.board.color && self.board.state == other.board.state } } @@ -47,6 +47,10 @@ impl Game { self.board.state.current_player() } + pub const fn next_player(&self) -> Color { + self.board.state.next_player() + } + pub fn make_move(&mut self, mv: &Move) { self.history .push_move_parameters(MoveParameters::build(self, mv)); @@ -66,20 +70,20 @@ impl Game { let piece_at_src = mailbox .piece_at(mv.src) - .expect("Expected piece at: {mv.src}"); + .unwrap_or_else(|| panic!("Expected piece at: {}", mv.src)); let piece_at_dst = mailbox.piece_at(mv.dst); match &mv.move_type { MoveType::Quiet => { - board.move_piece(mv.src, mv.dst, piece_at_src); - hash.update_quiet(mv.src, mv.dst, piece_at_src, color); + board.move_piece(mv.src, mv.dst, piece_at_src.0); + hash.update_quiet(mv.src, mv.dst, piece_at_src.0, color); hash.drop_en_passant_hash(board.state.en_passant_square()); mailbox.set_piece_at(mv.dst, Some(piece_at_src)); } MoveType::Capture => { let piece_at_dst = piece_at_dst.expect("Expected piece at: {mv.dst}"); - board.move_piece(mv.src, mv.dst, piece_at_src); - board.remove_opponent_piece(mv.dst, piece_at_dst); - hash.update_capture(mv.src, mv.dst, piece_at_src, piece_at_dst, color); + board.remove_opponent_piece(mv.dst, piece_at_dst.0); + hash.update_capture(mv.src, mv.dst, piece_at_src.0, piece_at_dst.0, color); + board.move_piece(mv.src, mv.dst, piece_at_src.0); hash.drop_en_passant_hash(board.state.en_passant_square()); mailbox.set_piece_at(mv.dst, Some(piece_at_src)); } @@ -88,11 +92,11 @@ impl Game { board.remove_opponent_piece(ep_capture, PieceType::Pawn); hash.update_en_passant(mv.src, mv.dst, ep_capture, color); hash.drop_en_passant_hash(board.state.en_passant_square()); - mailbox.set_piece_at(mv.dst, Some(PieceType::Pawn)); + mailbox.set_piece_at(mv.dst, Some((PieceType::Pawn, color))); mailbox.set_piece_at(ep_capture, None); } MoveType::DoublePush => { - board.move_piece(mv.src, mv.dst, piece_at_src); + board.move_piece(mv.src, mv.dst, piece_at_src.0); en_passant_square = match color { Color::White => Some(mv.src + 8), Color::Black => Some(mv.src.saturating_sub(8)), @@ -102,23 +106,23 @@ impl Game { mailbox.set_piece_at(mv.dst, Some(piece_at_src)); } MoveType::Promotion(promote) => { - board.remove_own_piece(mv.src, piece_at_src); + board.remove_own_piece(mv.src, piece_at_src.0); board.promote_piece(mv.dst, promote); hash.update_promotion(mv.src, mv.dst, promote, color); hash.drop_en_passant_hash(board.state.en_passant_square()); - mailbox.set_piece_at(mv.dst, Some(promote.into_piece_type())); + mailbox.set_piece_at(mv.dst, Some((promote.into_piece_type(), color))); } MoveType::PromotionCapture(promote) => { let piece_at_dst = piece_at_dst.expect("Expected piece at dst: {mv.dst}"); - board.remove_own_piece(mv.src, piece_at_src); - board.remove_opponent_piece(mv.dst, piece_at_dst); + board.remove_own_piece(mv.src, piece_at_src.0); + board.remove_opponent_piece(mv.dst, piece_at_dst.0); board.promote_piece(mv.dst, promote); - hash.update_promotion_capture(mv.src, mv.dst, piece_at_dst, promote, color); + hash.update_promotion_capture(mv.src, mv.dst, piece_at_dst.0, promote, color); hash.drop_en_passant_hash(board.state.en_passant_square()); - mailbox.set_piece_at(mv.dst, Some(promote.into_piece_type())); + mailbox.set_piece_at(mv.dst, Some((promote.into_piece_type(), color))); } MoveType::Castle => { - board.move_piece(mv.src, mv.dst, piece_at_src); + board.move_piece(mv.src, mv.dst, piece_at_src.0); 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), @@ -126,11 +130,11 @@ impl Game { }; board.move_piece(rook_src, rook_dst, PieceType::Rook); board.state.set_castling_ability(color, Castle::None); - hash.update_castle(mv.src, mv.dst, piece_at_src, rook_src, rook_dst, color); + hash.update_castle(mv.src, mv.dst, piece_at_src.0, rook_src, rook_dst, color); hash.drop_en_passant_hash(board.state.en_passant_square()); mailbox.set_piece_at(mv.dst, Some(piece_at_src)); mailbox.set_piece_at(rook_src, None); - mailbox.set_piece_at(rook_dst, Some(PieceType::Rook)); + mailbox.set_piece_at(rook_dst, Some((PieceType::Rook, color))); } } @@ -172,7 +176,7 @@ impl Game { let piece_at_dst = mailbox.piece_at(mv.dst).expect("Expected set piece"); match &mv.move_type { MoveType::Quiet | MoveType::DoublePush => { - board.move_piece(mv.dst, mv.src, piece_at_dst); + board.move_piece(mv.dst, mv.src, piece_at_dst.0); mailbox.set_piece_at(mv.src, mailbox.piece_at(mv.dst)); mailbox.set_piece_at(mv.dst, None); } @@ -180,19 +184,19 @@ impl Game { let captured_piece = move_parameters .captured_piece .expect("Expected captured piece to unmake Capture"); - board.move_piece(mv.dst, mv.src, piece_at_dst); - board.insert_opponent_piece(mv.dst, captured_piece); - mailbox.set_piece_at(mv.src, move_parameters.moved_piece); + board.move_piece(mv.dst, mv.src, piece_at_dst.0); + board.insert_opponent_piece(mv.dst, captured_piece.0); + mailbox.set_piece_at(mv.src, move_parameters.moving_piece); mailbox.set_piece_at(mv.dst, Some(captured_piece)); } MoveType::Promotion(_) => { let promoted_piece = move_parameters .promoted_piece .expect("Expected promoted piece to unmake Promotion"); - board.remove_own_piece(mv.dst, promoted_piece); + board.remove_own_piece(mv.dst, promoted_piece.0); board.insert_own_piece(mv.src, PieceType::Pawn); - mailbox.set_piece_at(mv.src, Some(PieceType::Pawn)); + mailbox.set_piece_at(mv.src, Some((PieceType::Pawn, color_before_move))); mailbox.set_piece_at(mv.dst, None); } MoveType::PromotionCapture(_) => { @@ -202,10 +206,10 @@ impl Game { let captured_piece = move_parameters .captured_piece .expect("Expected captured piece to unmake PromotionCapture"); - board.remove_own_piece(mv.dst, promoted_piece); - board.insert_opponent_piece(mv.dst, captured_piece); + board.remove_own_piece(mv.dst, promoted_piece.0); + board.insert_opponent_piece(mv.dst, captured_piece.0); board.insert_own_piece(mv.src, PieceType::Pawn); - mailbox.set_piece_at(mv.src, Some(PieceType::Pawn)); + mailbox.set_piece_at(mv.src, Some((PieceType::Pawn, color_before_move))); mailbox.set_piece_at(mv.dst, Some(captured_piece)); } MoveType::EnPassant => { @@ -213,10 +217,13 @@ impl Game { Color::White => mv.dst - 8, Color::Black => mv.dst + 8, }; - board.move_piece(mv.dst, mv.src, piece_at_dst); + board.move_piece(mv.dst, mv.src, piece_at_dst.0); board.insert_opponent_piece(enemy_pawn_square, PieceType::Pawn); - mailbox.set_piece_at(mv.src, Some(PieceType::Pawn)); - mailbox.set_piece_at(enemy_pawn_square, Some(PieceType::Pawn)); + mailbox.set_piece_at(mv.src, Some((PieceType::Pawn, color_before_move))); + mailbox.set_piece_at( + enemy_pawn_square, + Some((PieceType::Pawn, color_before_move.opponent())), + ); } MoveType::Castle => { let (rook_src, rook_dst) = match mv.dst { @@ -224,12 +231,12 @@ impl Game { Square::G1 | Square::G8 => (mv.dst + 1, mv.dst - 1), _ => return, }; - board.move_piece(mv.dst, mv.src, piece_at_dst); + board.move_piece(mv.dst, mv.src, piece_at_dst.0); board.remove_own_piece(rook_dst, PieceType::Rook); board.insert_own_piece(rook_src, PieceType::Rook); mailbox.set_piece_at(mv.src, mailbox.piece_at(mv.dst)); mailbox.set_piece_at(mv.dst, None); - mailbox.set_piece_at(rook_src, Some(PieceType::Rook)); + mailbox.set_piece_at(rook_src, Some((PieceType::Rook, color_before_move))); mailbox.set_piece_at(rook_dst, None); } } diff --git a/src/board/history.rs b/src/board/history.rs index 194e4b1..895cfc0 100644 --- a/src/board/history.rs +++ b/src/board/history.rs @@ -1,7 +1,7 @@ use crate::movegen::r#move::{Move, MoveType}; use super::{ - board::PieceType, + board::{Color, PieceType}, game::Game, mailbox::Mailbox, state::{Castle, State}, @@ -32,9 +32,9 @@ impl History { #[derive(Debug, Clone, PartialEq, Eq)] pub struct MoveParameters { pub mv: Option, - pub moved_piece: Option, - pub captured_piece: Option, - pub promoted_piece: Option, + pub moving_piece: Option<(PieceType, Color)>, + pub captured_piece: Option<(PieceType, Color)>, + pub promoted_piece: Option<(PieceType, Color)>, pub castling_ability: Option<[Castle; 2]>, pub en_passant_square: Option, pub halfmove_clock: Option, @@ -45,7 +45,7 @@ impl MoveParameters { pub const fn new() -> Self { Self { mv: None, - moved_piece: None, + moving_piece: None, captured_piece: None, promoted_piece: None, castling_ability: None, @@ -59,9 +59,9 @@ impl MoveParameters { let mut move_parameters = Self::new(); move_parameters.add_move(*mv); move_parameters.add_irreversible_parameters(&game.board.state); - move_parameters.add_moved_piece(&game.mailbox, mv); + move_parameters.add_moving_piece(&game.mailbox, mv); move_parameters.add_captured_piece(&game.mailbox, mv); - move_parameters.add_promoted_piece(mv); + move_parameters.add_promoted_piece(mv, game.current_player()); move_parameters.add_zobrist_hash(&game.hash); move_parameters @@ -77,8 +77,8 @@ impl MoveParameters { self.halfmove_clock = Some(state.halfmove_clock); } - fn add_moved_piece(&mut self, mailbox: &Mailbox, mv: &Move) { - self.moved_piece = mailbox.piece_at(mv.src); + fn add_moving_piece(&mut self, mailbox: &Mailbox, mv: &Move) { + self.moving_piece = mailbox.piece_at(mv.src); } fn add_captured_piece(&mut self, mailbox: &Mailbox, mv: &Move) { @@ -87,9 +87,9 @@ impl MoveParameters { } } - fn add_promoted_piece(&mut self, mv: &Move) { + fn add_promoted_piece(&mut self, mv: &Move, color: Color) { if let MoveType::Promotion(promote) | MoveType::PromotionCapture(promote) = mv.move_type { - self.promoted_piece = Some(promote.into_piece_type()) + self.promoted_piece = Some((promote.into_piece_type(), color)); } } diff --git a/src/board/mailbox.rs b/src/board/mailbox.rs index 7530439..ad17d8e 100644 --- a/src/board/mailbox.rs +++ b/src/board/mailbox.rs @@ -1,41 +1,46 @@ +use strum::IntoEnumIterator; + use super::{ bitboard::{have_common_bit, square_to_bitboard}, - board::{Board, PieceType}, + board::{Board, Color, PieceType}, }; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Mailbox { - pub mailbox: [Option; 64], + pub mailbox: [Option<(PieceType, Color)>; 64], } impl Mailbox { pub fn from_board(board: &Board) -> Self { - let mut mailbox: [Option; 64] = [None; 64]; + let mut mailbox: [Option<(PieceType, Color)>; 64] = [None; 64]; - for (square, mailbox_square) in mailbox.iter_mut().enumerate() { - *mailbox_square = board - .white_pieces - .iter() - .chain(board.black_pieces.iter()) - .find(|p| have_common_bit(p.bitboard, square_to_bitboard(square))) - .map(|p| p.piece_type); + for (square, m) in mailbox.iter_mut().enumerate() { + *m = PieceType::iter() + .flat_map(|p| Color::iter().map(move |c| (p, c))) + .find(|&(p, c)| { + have_common_bit(board.pieces[p] & board.color[c], square_to_bitboard(square)) + }); } Self { mailbox } } - pub fn set_piece_at(&mut self, square: usize, piece_type: Option) { - self.mailbox[square] = piece_type; + pub fn set_piece_at(&mut self, square: usize, piece: Option<(PieceType, Color)>) { + self.mailbox[square] = piece; } - pub const fn piece_at(&self, square: usize) -> Option { + pub const fn piece_at(&self, square: usize) -> Option<(PieceType, Color)> { self.mailbox[square] } } #[cfg(test)] mod tests { - use crate::board::{board::PieceType, fen::from_fen, square::Square}; + use crate::board::{ + board::{Color, PieceType}, + fen::from_fen, + square::Square, + }; use super::Mailbox; @@ -44,38 +49,23 @@ mod tests { #[test] fn test_from_board() -> Result<(), String> { - let game = from_fen(FEN_MATE_IN_1)?; - let mailbox = Mailbox::from_board(&game.board); - #[rustfmt::skip] - let expected: [Option; 64] = [ - None, None, None, None, None, Some(PieceType::King), None, None, - None, None, None, None, None, None, None, None, - None, None, None, None, Some(PieceType::Queen), None, Some(PieceType::King), None, - None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, - ]; - - assert_eq!(expected, mailbox.mailbox); - let game = from_fen(FEN_STARTPOS)?; let mailbox = Mailbox::from_board(&game.board); + #[rustfmt::skip] - let expected: [Option; 64] = [ - Some(PieceType::Rook), Some(PieceType::Knight), Some(PieceType::Bishop), Some(PieceType::Queen), - Some(PieceType::King), Some(PieceType::Bishop), Some(PieceType::Knight), Some(PieceType::Rook), - Some(PieceType::Pawn), Some(PieceType::Pawn), Some(PieceType::Pawn), Some(PieceType::Pawn), - Some(PieceType::Pawn), Some(PieceType::Pawn), Some(PieceType::Pawn), Some(PieceType::Pawn), + let expected: [Option<(PieceType, Color)>; 64] = [ + Some((PieceType::Rook, Color::White)), Some((PieceType::Knight, Color::White)), Some((PieceType::Bishop, Color::White)), Some((PieceType::Queen, Color::White)), + Some((PieceType::King, Color::White)), Some((PieceType::Bishop, Color::White)), Some((PieceType::Knight, Color::White)), Some((PieceType::Rook, Color::White)), + Some((PieceType::Pawn, Color::White)), Some((PieceType::Pawn, Color::White)), Some((PieceType::Pawn, Color::White)), Some((PieceType::Pawn, Color::White)), + Some((PieceType::Pawn, Color::White)), Some((PieceType::Pawn, Color::White)), Some((PieceType::Pawn, Color::White)), Some((PieceType::Pawn, Color::White)), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - Some(PieceType::Pawn), Some(PieceType::Pawn), Some(PieceType::Pawn), Some(PieceType::Pawn), - Some(PieceType::Pawn), Some(PieceType::Pawn), Some(PieceType::Pawn), Some(PieceType::Pawn), - Some(PieceType::Rook), Some(PieceType::Knight), Some(PieceType::Bishop), Some(PieceType::Queen), - Some(PieceType::King), Some(PieceType::Bishop), Some(PieceType::Knight), Some(PieceType::Rook), + Some((PieceType::Pawn, Color::Black)), Some((PieceType::Pawn, Color::Black)), Some((PieceType::Pawn, Color::Black)), Some((PieceType::Pawn, Color::Black)), + Some((PieceType::Pawn, Color::Black)), Some((PieceType::Pawn, Color::Black)), Some((PieceType::Pawn, Color::Black)), Some((PieceType::Pawn, Color::Black)), + Some((PieceType::Rook, Color::Black)), Some((PieceType::Knight, Color::Black)), Some((PieceType::Bishop, Color::Black)), Some((PieceType::Queen, Color::Black)), + Some((PieceType::King, Color::Black)), Some((PieceType::Bishop, Color::Black)), Some((PieceType::Knight, Color::Black)), Some((PieceType::Rook, Color::Black)), ]; assert_eq!(expected, mailbox.mailbox); @@ -88,7 +78,10 @@ mod tests { let game = from_fen(FEN_MATE_IN_1)?; let mailbox = Mailbox::from_board(&game.board); - assert_eq!(PieceType::King, mailbox.piece_at(Square::F1).unwrap()); + assert_eq!( + (PieceType::King, Color::White), + mailbox.piece_at(Square::F1).unwrap() + ); Ok(()) } diff --git a/src/board/state.rs b/src/board/state.rs index eb1659a..1bc4905 100644 --- a/src/board/state.rs +++ b/src/board/state.rs @@ -143,6 +143,13 @@ impl State { pub const fn current_player(&self) -> Color { self.side_to_move } + + pub const fn next_player(&self) -> Color { + match self.side_to_move { + Color::White => Color::Black, + Color::Black => Color::White, + } + } } impl Default for State { diff --git a/src/board/zobrist.rs b/src/board/zobrist.rs index 4844f13..3800d88 100644 --- a/src/board/zobrist.rs +++ b/src/board/zobrist.rs @@ -8,6 +8,7 @@ use super::{ }; use rand::{rngs::SmallRng, RngCore, SeedableRng}; use std::sync::LazyLock; +use strum::IntoEnumIterator; static ZOBRIST_KEYS: LazyLock = LazyLock::new(ZobristKeys::new); @@ -44,39 +45,29 @@ impl ZobristKeys { } keys.side_to_move = state.next_u64(); - - Self { - piece_square_color: keys.piece_square_color, - en_passant: keys.en_passant, - castling_ability: keys.castling_ability, - side_to_move: keys.side_to_move, - } + keys } pub fn calculate_hash(&self, board: &Board) -> ZobristHash { let mut hash = 0; - let white_pieces = &board.white_pieces; - let black_pieces = &board.black_pieces; - for piece in white_pieces.iter() { - let mut bb = piece.bitboard; + PieceType::iter().for_each(|piece_type| { + let mut bitboard = board.pieces[piece_type] & board.color[Color::White]; - while bb != 0 { - let square = lsb(bb); - hash ^= self.piece_square_color[square][piece.piece_type][piece.color]; - bb &= bb - 1; + while bitboard != 0 { + let square = lsb(bitboard); + hash ^= self.piece_square_color[square][piece_type][Color::White]; + bitboard &= bitboard - 1; } - } - for piece in black_pieces.iter() { - let mut bb = piece.bitboard; + let mut bitboard = board.pieces[piece_type] & board.color[Color::Black]; - while bb != 0 { - let square = lsb(bb); - hash ^= self.piece_square_color[square][piece.piece_type][piece.color]; - bb &= bb - 1; + while bitboard != 0 { + let square = lsb(bitboard); + hash ^= self.piece_square_color[square][piece_type][Color::Black]; + bitboard &= bitboard - 1; } - } + }); if board.state.current_player().eq(&Color::Black) { hash ^= self.side_to_move diff --git a/src/evaluation/evaluation.rs b/src/evaluation/evaluation.rs index c019690..562ee79 100644 --- a/src/evaluation/evaluation.rs +++ b/src/evaluation/evaluation.rs @@ -1,4 +1,4 @@ -use u64 as Bitboard; +use strum::IntoEnumIterator; use crate::{ board::{ @@ -22,45 +22,34 @@ pub const fn material_score(piece_type: PieceType) -> i32 { } fn is_end_game(board: &Board) -> bool { - let white_pieces = board.white_pieces.iter().fold(0, |acc, p| { - acc + bitboard::bit_count(p.bitboard) * material_score(p.piece_type) as usize - }); - - let black_pieces = board.black_pieces.iter().fold(0, |acc, p| { - acc + bitboard::bit_count(p.bitboard) * material_score(p.piece_type) as usize - }); - - (white_pieces + black_pieces) < 2000 + PieceType::iter().fold(0, |acc, p| { + acc + bitboard::bit_count(board.pieces[p]) * material_score(p) as usize + }) < 2000 } fn evaluate_side_for(board: &Board, color: Color) -> i32 { - let mut total_score = 0; - let pieces = match color { - Color::White => &board.white_pieces, - Color::Black => &board.black_pieces, - }; - let psqt = if is_end_game(board) { piece_square_score_endgame } else { piece_square_score }; - for piece in pieces { - let (piece_type, mut bitboard): (PieceType, Bitboard) = (piece.piece_type, piece.bitboard); - let mut score = 0; + let mut total_score = 0; + + PieceType::iter().for_each(|piece_type| { + let mut bitboard = board.pieces[piece_type] & board.color[color]; while bitboard != 0 { let psqt_index = match color { Color::White => lsb(bitboard), Color::Black => mirror_index(lsb(bitboard)), }; - score += material_score(piece_type); - score += psqt(piece_type, psqt_index); + total_score += material_score(piece_type); + total_score += psqt(piece_type, psqt_index); bitboard &= bitboard - 1; } - total_score += score; - } + }); + total_score } diff --git a/src/movegen/attack_generator.rs b/src/movegen/attack_generator.rs index 78cd403..e5beae5 100644 --- a/src/movegen/attack_generator.rs +++ b/src/movegen/attack_generator.rs @@ -325,118 +325,100 @@ pub fn init_attacks() { #[cfg(test)] mod tests { use super::*; - use crate::board::board::{Color, Piece, PieceType}; + use crate::board::{board::Color, square::Square}; #[test] fn test_pawn_attacks() -> Result<(), String> { - let white_pawn_a_file = Piece::new(0x100, PieceType::Pawn, Color::White); - let attacks = pawn_attacks(white_pawn_a_file.bitboard, white_pawn_a_file.color); - assert_eq!(attacks, 0x20000); - - let white_pawn_b_file = Piece::new(0x200, PieceType::Pawn, Color::White); - let attacks = pawn_attacks(white_pawn_b_file.bitboard, white_pawn_b_file.color); - assert_eq!(attacks, 0x50000); - - let white_pawn_h_file = Piece::new(0x8000, PieceType::Pawn, Color::White); - let attacks = pawn_attacks(white_pawn_h_file.bitboard, white_pawn_h_file.color); - assert_eq!(attacks, 0x400000); - - let black_pawn_a_file = Piece::new(0x1000000000000, PieceType::Pawn, Color::Black); - let attacks = pawn_attacks(black_pawn_a_file.bitboard, black_pawn_a_file.color); - assert_eq!(attacks, 0x20000000000); - - let black_pawn_b_file = Piece::new(0x2000000000000, PieceType::Pawn, Color::Black); - let attacks = pawn_attacks(black_pawn_b_file.bitboard, black_pawn_b_file.color); - assert_eq!(attacks, 0x50000000000); - - let black_pawn_h_file = Piece::new(0x80000000000000, PieceType::Pawn, Color::Black); - let attacks = pawn_attacks(black_pawn_h_file.bitboard, black_pawn_h_file.color); - assert_eq!(attacks, 0x400000000000); + assert_eq!( + 0x20000, + pawn_attacks(square_to_bitboard(Square::A2), Color::White) + ); + assert_eq!( + 0x50000, + pawn_attacks(square_to_bitboard(Square::B2), Color::White) + ); + assert_eq!( + 0x400000, + pawn_attacks(square_to_bitboard(Square::H2), Color::White) + ); + assert_eq!( + 0x20000000000, + pawn_attacks(square_to_bitboard(Square::A7), Color::Black) + ); + assert_eq!( + 0x50000000000, + pawn_attacks(square_to_bitboard(Square::B7), Color::Black) + ); + assert_eq!( + 0x400000000000, + pawn_attacks(square_to_bitboard(Square::H7), Color::Black) + ); Ok(()) } #[test] fn test_knight_attacks() -> Result<(), String> { - let knight_two_attacks = Piece::new(0x1, PieceType::Knight, Color::White); - let attacks = knight_attacks(knight_two_attacks.bitboard); - assert_eq!(attacks, 0x20400); - - let knight_three_attacks = Piece::new(0x2, PieceType::Knight, Color::White); - let attacks = knight_attacks(knight_three_attacks.bitboard); - assert_eq!(attacks, 0x50800); - - let knight_three_attacks = Piece::new(0x4, PieceType::Knight, Color::White); - let attacks = knight_attacks(knight_three_attacks.bitboard); - assert_eq!(attacks, 0xa1100); - - let knight_six_attacks = Piece::new(0x400, PieceType::Knight, Color::White); - let attacks = knight_attacks(knight_six_attacks.bitboard); - assert_eq!(attacks, 0xa110011); - - let knight_eight_attacks = Piece::new(0x40000, PieceType::Knight, Color::White); - let attacks = knight_attacks(knight_eight_attacks.bitboard); - assert_eq!(attacks, 0xa1100110a); + assert_eq!(0x20400, knight_attacks(square_to_bitboard(Square::A1))); + assert_eq!(0x50800, knight_attacks(square_to_bitboard(Square::B1))); + assert_eq!(0xa1100, knight_attacks(square_to_bitboard(Square::C1))); + assert_eq!(0xa110011, knight_attacks(square_to_bitboard(Square::C2))); + assert_eq!(0xa1100110a, knight_attacks(square_to_bitboard(Square::C3))); Ok(()) } #[test] fn test_king_attacks() -> Result<(), String> { - let king_three_attacks = Piece::new(0x1, PieceType::King, Color::White); - let attacks = king_attacks(king_three_attacks.bitboard); - assert_eq!(attacks, 0x302); - - let king_five_attacks = Piece::new(0x2, PieceType::King, Color::White); - let attacks = king_attacks(king_five_attacks.bitboard); - assert_eq!(attacks, 0x705); - - let king_eight_attacks = Piece::new(0x200, PieceType::King, Color::White); - let attacks = king_attacks(king_eight_attacks.bitboard); - assert_eq!(attacks, 0x70507); + assert_eq!(0x302, king_attacks(square_to_bitboard(Square::A1))); + assert_eq!(0x705, king_attacks(square_to_bitboard(Square::B1))); + assert_eq!(0x70507, king_attacks(square_to_bitboard(Square::B2))); Ok(()) } #[test] fn test_mask_bishop_attacks() -> Result<(), String> { - let bishop_d4 = Piece::new(0x8000000, PieceType::Bishop, Color::White); - let all_directions_mask = mask_bishop_attacks(bishop_d4.bitboard); - assert_eq!(all_directions_mask, 0x40221400142200); + assert_eq!( + 0x40221400142200, + mask_bishop_attacks(square_to_bitboard(Square::D4)) + ); Ok(()) } #[test] fn test_bishop_attacks_on_the_fly() -> Result<(), String> { - let bishop_d4 = Piece::new(0x8000000, PieceType::Bishop, Color::White); - let blocker_c5 = 0x400000000_u64; - let attacks = bishop_attacks_on_the_fly(bishop_d4.bitboard, blocker_c5); - assert_eq!(attacks, 0x8040201400142241); - - let bishop_a1 = Piece::new(0x0, PieceType::Bishop, Color::White); - let blocker_none = 0x0_u64; - let attacks = bishop_attacks_on_the_fly(bishop_a1.bitboard, blocker_none); - assert_eq!(attacks, 0x8040201008040200); + assert_eq!( + 0x8040201400142241, + bishop_attacks_on_the_fly( + square_to_bitboard(Square::D4), + square_to_bitboard(Square::C5) + ) + ); Ok(()) } #[test] fn test_mask_rook_attacks() -> Result<(), String> { - let rook_d4 = Piece::new(0x8000000, PieceType::Rook, Color::White); - let all_directions_mask = mask_rook_attacks(rook_d4.bitboard); - assert_eq!(all_directions_mask, 0x8080876080800); + assert_eq!( + 0x8080876080800, + mask_rook_attacks(square_to_bitboard(Square::D4)) + ); Ok(()) } #[test] fn test_rook_attacks_on_the_fly() -> Result<(), String> { - let rook_d4 = Piece::new(0x8000000, PieceType::Rook, Color::White); - let blocker_c4 = 0x4000000_u64; - let attacks = rook_attacks_on_the_fly(rook_d4.bitboard, blocker_c4); - assert_eq!(attacks, 0x8080808f4080808); + assert_eq!( + 0x8080808f4080808, + rook_attacks_on_the_fly( + square_to_bitboard(Square::D4), + square_to_bitboard(Square::C4) + ) + ); Ok(()) } diff --git a/src/movegen/move.rs b/src/movegen/move.rs index 4d050f1..ecc52a8 100644 --- a/src/movegen/move.rs +++ b/src/movegen/move.rs @@ -116,7 +116,7 @@ impl Move { Ok(Self::build_with_type(&game.mailbox, src, dst, promote_into)) } - fn build_with_type( + const fn build_with_type( mailbox: &Mailbox, src: usize, dst: usize, @@ -136,7 +136,7 @@ impl Move { _ => (), } - if moving == Some(PieceType::Pawn) { + if let Some((PieceType::Pawn, _)) = moving { if src.abs_diff(dst) == 16 { return Self::with_type(src, dst, MoveType::DoublePush); } @@ -146,8 +146,10 @@ impl Move { } } - if moving == Some(PieceType::King) && src.abs_diff(dst) == 2 { - return Self::with_type(src, dst, MoveType::Castle); + if let Some((PieceType::King, _)) = moving { + if src.abs_diff(dst) == 2 { + return Self::with_type(src, dst, MoveType::Castle); + } } Self::new(src, dst) diff --git a/src/search/move_ordering.rs b/src/search/move_ordering.rs index 3fb0793..ba9dd1a 100644 --- a/src/search/move_ordering.rs +++ b/src/search/move_ordering.rs @@ -17,7 +17,7 @@ pub fn sort_moves(mut moves: Vec, mailbox: &Mailbox, tt_move: Option const fn mvv_lva(mailbox: &Mailbox, mv: Move) -> i32 { match (mailbox.piece_at(mv.src), mailbox.piece_at(mv.dst)) { - (Some(aggressor), Some(victim)) => material_score(victim) - material_score(aggressor), + (Some(aggressor), Some(victim)) => material_score(victim.0) - material_score(aggressor.0), _ => -1000, } }