diff --git a/src/board/bitboard.rs b/src/board/bitboard.rs index 64a1f36..7f7ae8a 100644 --- a/src/board/bitboard.rs +++ b/src/board/bitboard.rs @@ -1,41 +1,139 @@ -use u64 as Bitboard; +use std::ops; -pub const EMPTY: Bitboard = 0x0; -pub const NOT_FILE_A: Bitboard = 0xfefefefefefefefe; -pub const NOT_FILE_H: Bitboard = 0x7f7f7f7f7f7f7f7f; -pub const NOT_FILE_AB: Bitboard = 0xfcfcfcfcfcfcfcfc; -pub const NOT_FILE_GH: Bitboard = 0x3f3f3f3f3f3f3f3f; -pub const RANK_1: Bitboard = 0xff; -pub const RANK_2: Bitboard = 0xff00; -pub const RANK_4: Bitboard = 0xff000000; -pub const RANK_5: Bitboard = 0xff00000000; -pub const RANK_7: Bitboard = 0xff000000000000; -pub const RANK_8: Bitboard = 0xff00000000000000; +use super::square::Square; -pub const fn square_to_bitboard(square: usize) -> Bitboard { - 1_u64 << square -} +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)] +pub struct Bitboard(pub u64); -pub const fn square_to_bitboard_wrapping(square: usize) -> Bitboard { - 1_u64.wrapping_shl(square as u32) -} +pub const EMPTY: Bitboard = Bitboard(0x0); +pub const NOT_FILE_A: Bitboard = Bitboard(0xfefefefefefefefe); +pub const NOT_FILE_H: Bitboard = Bitboard(0x7f7f7f7f7f7f7f7f); +pub const NOT_FILE_AB: Bitboard = Bitboard(0xfcfcfcfcfcfcfcfc); +pub const NOT_FILE_GH: Bitboard = Bitboard(0x3f3f3f3f3f3f3f3f); +pub const RANK_1: Bitboard = Bitboard(0xff); +pub const RANK_2: Bitboard = Bitboard(0xff00); +pub const RANK_4: Bitboard = Bitboard(0xff000000); +pub const RANK_5: Bitboard = Bitboard(0xff00000000); +pub const RANK_7: Bitboard = Bitboard(0xff000000000000); +pub const RANK_8: Bitboard = Bitboard(0xff00000000000000); -pub const fn bitboard_to_coords(bitboard: Bitboard) -> (usize, usize) { - match bitboard { - 0 => (0, 0), - _ => (lsb(bitboard) / 8, lsb(bitboard) % 8), +impl From for Bitboard { + #[inline(always)] + fn from(square: Square) -> Self { + Self(1_u64 << (square.0 as u32)) } } -pub const fn lsb(bitboard: Bitboard) -> usize { - bitboard.trailing_zeros() as usize +impl From for Bitboard { + #[inline(always)] + fn from(square: usize) -> Self { + Self(1_u64 << (square as u32)) + } } -#[allow(dead_code)] -pub const fn bit_count(bitboard: Bitboard) -> usize { - bitboard.count_ones() as usize +impl From for Bitboard { + #[inline(always)] + fn from(square: u64) -> Self { + Self(1_u64 << (square as u32)) + } } -pub const fn have_common_bit(bitboard_a: Bitboard, bitboard_b: Bitboard) -> bool { - bitboard_a & bitboard_b != 0 +impl ops::Add for Bitboard { + type Output = Self; + + fn add(self, rhs: usize) -> Self::Output { + Self(self.0 + rhs as u64) + } +} + +impl ops::Sub for Bitboard { + type Output = Self; + + fn sub(self, rhs: usize) -> Self::Output { + Self(self.0 - rhs as u64) + } +} + +impl ops::BitAnd for Bitboard { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl ops::BitOr for Bitboard { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl ops::BitAndAssign for Bitboard { + fn bitand_assign(&mut self, rhs: Self) { + self.0 = self.0 & rhs.0 + } +} + +impl ops::BitOrAssign for Bitboard { + fn bitor_assign(&mut self, rhs: Self) { + self.0 = self.0 | rhs.0 + } +} + +impl ops::Not for Bitboard { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl ops::Shl for Bitboard { + type Output = Self; + + fn shl(self, rhs: usize) -> Self::Output { + Self(self.0 << rhs) + } +} + +impl ops::Shr for Bitboard { + type Output = Self; + + fn shr(self, rhs: usize) -> Self::Output { + Self(self.0 >> rhs) + } +} + +impl ops::ShrAssign for Bitboard { + fn shr_assign(&mut self, rhs: Self) { + self.0 = self.0 >> rhs.0 + } +} + +impl ops::BitXor for Bitboard { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self::Output { + Self(self.0 ^ rhs.0) + } +} + +impl Bitboard { + pub const fn lsb(self) -> usize { + self.0.trailing_zeros() as usize + } + + pub const fn intersects(self, other: Self) -> bool { + self.0 & other.0 != 0 + } + + pub const fn is_empty(self) -> bool { + self.0 == 0 + } + + pub const fn wrapping_mul(self, rhs: Self) -> Self { + Self(self.0.wrapping_mul(rhs.0)) + } } diff --git a/src/board/board.rs b/src/board/board.rs index b003522..881fa70 100644 --- a/src/board/board.rs +++ b/src/board/board.rs @@ -1,7 +1,5 @@ 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, @@ -25,22 +23,22 @@ impl Board { pub const fn startpos() -> Self { Self { pieces: [ - 0xff00000000ff00, - 0x4200000000000042, - 0x2400000000000024, - 0x8100000000000081, - 0x800000000000008, - 0x1000000000000010, + Bitboard(0xff00000000ff00), + Bitboard(0x4200000000000042), + Bitboard(0x2400000000000024), + Bitboard(0x8100000000000081), + Bitboard(0x800000000000008), + Bitboard(0x1000000000000010), ], - color: [0xffff, 0xffff000000000000], + color: [Bitboard(0xffff), Bitboard(0xffff000000000000)], state: State::new(), } } pub const fn new_empty() -> Self { Self { - pieces: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0], - color: [0x0, 0x0], + pieces: [EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY], + color: [EMPTY, EMPTY], state: State::new(), } } @@ -48,7 +46,7 @@ impl Board { 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 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; @@ -57,16 +55,16 @@ impl Board { 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)) + pawns.intersects(fetch_pawn_attacks(square, own_color)) + || knights.intersects(fetch_knight_attacks(square)) + || bishops.intersects(fetch_bishop_attacks(all_occupancies, square)) + || rooks.intersects(fetch_rook_attacks(all_occupancies, square)) + || queens.intersects(fetch_queen_attacks(all_occupancies, square)) + || king.intersects(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]); + let own_king_square = (self.pieces[PieceType::King] & self.color[color]).lsb(); self.is_attacked(own_king_square, color.opponent()) } @@ -115,10 +113,11 @@ impl Board { } pub fn non_pawn_materials(&self) -> u64 { - self.pieces[PieceType::Knight] + (self.pieces[PieceType::Knight] | self.pieces[PieceType::Bishop] | self.pieces[PieceType::Rook] - | self.pieces[PieceType::Queen] + | self.pieces[PieceType::Queen]) + .0 } pub fn set_piece(&mut self, bb: Bitboard, piece_type: PieceType, color: Color) { @@ -133,44 +132,44 @@ impl Board { 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) + Bitboard::from(square).intersects(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); + self.pieces[piece_type] &= !Bitboard::from(src); + self.pieces[piece_type] |= Bitboard::from(dst); + self.color[self.state.current_player()] &= !Bitboard::from(src); + self.color[self.state.current_player()] |= Bitboard::from(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); + self.pieces[piece_type] |= Bitboard::from(square); + self.color[self.state.current_player()] |= Bitboard::from(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); + self.pieces[piece_type] |= Bitboard::from(square); + self.color[self.state.next_player()] |= Bitboard::from(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); + self.pieces[piece_type] &= !Bitboard::from(square); + self.color[self.state.current_player()] &= !Bitboard::from(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); + self.pieces[piece_type] &= !Bitboard::from(square); + self.color[self.state.next_player()] &= !Bitboard::from(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), + Promote::Knight => self.pieces[PieceType::Knight] |= Bitboard::from(square), + Promote::Bishop => self.pieces[PieceType::Bishop] |= Bitboard::from(square), + Promote::Rook => self.pieces[PieceType::Rook] |= Bitboard::from(square), + Promote::Queen => self.pieces[PieceType::Queen] |= Bitboard::from(square), }; - self.color[self.state.current_player()] |= square_to_bitboard(square); + self.color[self.state.current_player()] |= Bitboard::from(square); } } @@ -197,6 +196,8 @@ impl PieceType { } use std::ops::{Index, IndexMut}; +use super::bitboard::{Bitboard, EMPTY}; + impl Index for [T] { type Output = T; @@ -256,11 +257,14 @@ mod tests { 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], + Bitboard(0x40000002000000) + ); + assert_eq!(new_game.board.color[Color::Black], Bitboard(0x900204401002)); assert_eq!( new_game.board.color[Color::White] | new_game.board.color[Color::Black], - 0x40900206401002 + Bitboard(0x40900206401002) ); Ok(()) diff --git a/src/board/fen.rs b/src/board/fen.rs index d227e4b..4d6630a 100644 --- a/src/board/fen.rs +++ b/src/board/fen.rs @@ -76,7 +76,7 @@ pub fn piece_placement(pieces: &str) -> Result { )) } } { - board.set_piece(square_to_bitboard(square as usize), piece_type, color); + board.set_piece(Bitboard::from(square as usize), piece_type, color); file += 1; }; } @@ -137,7 +137,7 @@ fn castling_ability(castling: &str) -> Result<[Castle; 2], FenError> { use std::collections::HashMap; -use super::bitboard::square_to_bitboard; +use super::bitboard::Bitboard; use super::mailbox::Mailbox; use super::zobrist::zobrist_keys; diff --git a/src/board/mailbox.rs b/src/board/mailbox.rs index b1096c1..86de4ea 100644 --- a/src/board/mailbox.rs +++ b/src/board/mailbox.rs @@ -1,7 +1,7 @@ use strum::IntoEnumIterator; use super::{ - bitboard::{have_common_bit, square_to_bitboard}, + bitboard::Bitboard, board::{Board, Color, PieceType}, }; @@ -16,7 +16,7 @@ impl Mailbox { *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)) + (board.pieces[p] & board.color[c]).intersects(Bitboard::from(square)) }); } diff --git a/src/board/square.rs b/src/board/square.rs index 74e0a82..bfdb27d 100644 --- a/src/board/square.rs +++ b/src/board/square.rs @@ -1,4 +1,6 @@ -pub struct Square {} +use super::bitboard::Bitboard; + +pub struct Square(pub u8); #[allow(dead_code)] impl Square { @@ -89,3 +91,16 @@ pub fn to_algebraic(square: usize) -> String { format!("{}{}", (file + b'a') as char, (rank + b'1') as char) } + +// coords.0 is rank, coords.1 is file +pub struct Coords(pub usize, pub usize); + +impl From for Coords { + fn from(bitboard: Bitboard) -> Self { + if bitboard.is_empty() { + return Self(0, 0); + } + + Self(bitboard.lsb() / 8, bitboard.lsb() % 8) + } +} diff --git a/src/board/zobrist.rs b/src/board/zobrist.rs index 622066a..18c1835 100644 --- a/src/board/zobrist.rs +++ b/src/board/zobrist.rs @@ -1,7 +1,6 @@ use crate::movegen::r#move::Promote; use super::{ - bitboard::lsb, board::{Board, Color, PieceType}, square::{self, Square}, state::Castle, @@ -54,16 +53,16 @@ impl ZobristKeys { PieceType::iter().for_each(|piece_type| { let mut bitboard = board.pieces[piece_type] & board.color[Color::White]; - while bitboard != 0 { - let square = lsb(bitboard); + while !bitboard.is_empty() { + let square = bitboard.lsb(); hash ^= self.square_piece_color[square][piece_type][Color::White]; bitboard &= bitboard - 1; } let mut bitboard = board.pieces[piece_type] & board.color[Color::Black]; - while bitboard != 0 { - let square = lsb(bitboard); + while !bitboard.is_empty() { + let square = bitboard.lsb(); hash ^= self.square_piece_color[square][piece_type][Color::Black]; bitboard &= bitboard - 1; } diff --git a/src/movegen/attack_generator.rs b/src/movegen/attack_generator.rs index e881fdb..10ec1fd 100644 --- a/src/movegen/attack_generator.rs +++ b/src/movegen/attack_generator.rs @@ -1,15 +1,13 @@ use crate::board::{ - bitboard::{ - bitboard_to_coords, have_common_bit, lsb, square_to_bitboard, square_to_bitboard_wrapping, - EMPTY, NOT_FILE_A, NOT_FILE_AB, NOT_FILE_GH, NOT_FILE_H, - }, + bitboard::{Bitboard, EMPTY, NOT_FILE_A, NOT_FILE_AB, NOT_FILE_GH, NOT_FILE_H}, board::Color, - square::from_coords, + square::{from_coords, Coords}, }; -use u64 as Bitboard; + +use std::{array, sync::LazyLock}; #[rustfmt::skip] -pub const ROOK_RELEVANT_BITS: [usize; 64] = [ +const ROOK_RELEVANT_BITS: [usize; 64] = [ 12, 11, 11, 11, 11, 11, 11, 12, 11, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11, @@ -21,7 +19,7 @@ pub const ROOK_RELEVANT_BITS: [usize; 64] = [ ]; #[rustfmt::skip] -pub const BISHOP_RELEVANT_BITS: [usize; 64] = [ +const BISHOP_RELEVANT_BITS: [usize; 64] = [ 6, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 5, 5, @@ -38,7 +36,7 @@ static KING_ATTACKS: LazyLock<[Bitboard; 64]> = LazyLock::new(init_king_attacks) static BISHOP_ATTACKS: LazyLock>> = LazyLock::new(init_bishop_attacks); static ROOK_ATTACKS: LazyLock>> = LazyLock::new(init_rook_attacks); -const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard { +fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard { let mut attacks = EMPTY; match color { @@ -55,7 +53,7 @@ const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard { attacks } -const fn knight_attacks(bitboard: Bitboard) -> Bitboard { +fn knight_attacks(bitboard: Bitboard) -> Bitboard { let mut attacks = EMPTY; attacks |= (bitboard & NOT_FILE_AB) << 6; @@ -70,7 +68,7 @@ const fn knight_attacks(bitboard: Bitboard) -> Bitboard { attacks } -const fn king_attacks(bitboard: Bitboard) -> Bitboard { +fn king_attacks(bitboard: Bitboard) -> Bitboard { let mut attacks = EMPTY; attacks |= (bitboard & NOT_FILE_H) << 1; @@ -87,16 +85,16 @@ const fn king_attacks(bitboard: Bitboard) -> Bitboard { fn mask_bishop_attacks(bitboard: Bitboard) -> Bitboard { let mut attacks = EMPTY; - let (rank_dst, file_dst) = bitboard_to_coords(bitboard); + let coords = Coords::from(bitboard); let mut add_attacks_in_direction = |rank_step: isize, file_step: isize| { - let mut rank = rank_dst as isize; - let mut file = file_dst as isize; + let mut rank = coords.0 as isize; + let mut file = coords.1 as isize; while (1..=6).contains(&(rank + rank_step)) && (1..=6).contains(&(file + file_step)) { rank += rank_step; file += file_step; - attacks |= square_to_bitboard(from_coords(rank as usize, file as usize)); + attacks |= Bitboard::from(from_coords(rank as usize, file as usize)); } }; @@ -110,46 +108,46 @@ fn mask_bishop_attacks(bitboard: Bitboard) -> Bitboard { fn mask_rook_attacks(bitboard: Bitboard) -> Bitboard { let mut attacks = EMPTY; - let (rank_dst, file_dst) = bitboard_to_coords(bitboard); + let coords = Coords::from(bitboard); let mut add_attacks_in_direction = |mut coord: usize, step: isize, is_vertical: bool| { while (1..=6).contains(&(coord as isize + step)) { coord = (coord as isize + step) as usize; let attack_square = if is_vertical { - square_to_bitboard(from_coords(coord, file_dst)) + Bitboard::from(from_coords(coord, coords.1)) } else { - square_to_bitboard(from_coords(rank_dst, coord)) + Bitboard::from(from_coords(coords.0, coord)) }; attacks |= attack_square; } }; - add_attacks_in_direction(rank_dst, 1, true); - add_attacks_in_direction(rank_dst, -1, true); - add_attacks_in_direction(file_dst, 1, false); - add_attacks_in_direction(file_dst, -1, false); + add_attacks_in_direction(coords.0, 1, true); + add_attacks_in_direction(coords.0, -1, true); + add_attacks_in_direction(coords.1, 1, false); + add_attacks_in_direction(coords.1, -1, false); attacks } fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard { let mut attacks = EMPTY; - let (rank_dst, file_dst) = bitboard_to_coords(bitboard); + let coords = Coords::from(bitboard); let mut add_attacks_in_direction = |rank_step: isize, file_step: isize| { - let mut rank = rank_dst as isize; - let mut file = file_dst as isize; + let mut rank = coords.0 as isize; + let mut file = coords.1 as isize; while (0..=7).contains(&(rank + rank_step)) && (0..=7).contains(&(file + file_step)) { rank += rank_step; file += file_step; - let attack_square = square_to_bitboard(from_coords(rank as usize, file as usize)); + let attack_square = Bitboard::from(from_coords(rank as usize, file as usize)); attacks |= attack_square; - if have_common_bit(attack_square, blocker) { + if attack_square.intersects(blocker) { break; } } @@ -165,30 +163,30 @@ fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard { let mut attacks = EMPTY; - let (rank_dst, file_dst) = bitboard_to_coords(bitboard); + let coords = Coords::from(bitboard); let mut add_attacks_in_direction = |mut coord: usize, step: isize, is_vertical: bool| { while (0..=7).contains(&(coord as isize + step)) { coord = (coord as isize + step) as usize; let attack_square = if is_vertical { - square_to_bitboard(from_coords(coord, file_dst)) + Bitboard::from(from_coords(coord, coords.1)) } else { - square_to_bitboard(from_coords(rank_dst, coord)) + Bitboard::from(from_coords(coords.0, coord)) }; attacks |= attack_square; - if have_common_bit(attack_square, blocker) { + if attack_square.intersects(blocker) { break; } } }; - add_attacks_in_direction(rank_dst, 1, true); - add_attacks_in_direction(rank_dst, -1, true); - add_attacks_in_direction(file_dst, 1, false); - add_attacks_in_direction(file_dst, -1, false); + add_attacks_in_direction(coords.0, 1, true); + add_attacks_in_direction(coords.0, -1, true); + add_attacks_in_direction(coords.1, 1, false); + add_attacks_in_direction(coords.1, -1, false); attacks } @@ -197,10 +195,11 @@ fn set_occupancy(idx: u64, relevant_bits: usize, mut attack_mask: Bitboard) -> B let mut occupancy = EMPTY; for bit in 0..relevant_bits { - let square = lsb(attack_mask); - attack_mask &= !square_to_bitboard(square); - if have_common_bit(idx, square_to_bitboard(bit)) { - occupancy |= square_to_bitboard(square); + let square = attack_mask.lsb(); + attack_mask &= !Bitboard::from(square); + + if Bitboard(idx).intersects(Bitboard::from(bit)) { + occupancy |= Bitboard::from(square); } } @@ -229,18 +228,18 @@ use crate::movegen::magic_bitboards::{BISHOP_MAGIC, ROOK_MAGIC}; #[inline(always)] pub fn fetch_bishop_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard { - occupancy &= mask_bishop_attacks(square_to_bitboard_wrapping(square)); + occupancy &= mask_bishop_attacks(Bitboard(1u64.wrapping_shl(square as u32))); occupancy = occupancy.wrapping_mul(BISHOP_MAGIC[square]); - occupancy >>= 64 - BISHOP_RELEVANT_BITS[square]; - BISHOP_ATTACKS[square][occupancy as usize] + occupancy >>= Bitboard(64 - BISHOP_RELEVANT_BITS[square] as u64); + BISHOP_ATTACKS[square][occupancy.0 as usize] } #[inline(always)] pub fn fetch_rook_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard { - occupancy &= mask_rook_attacks(square_to_bitboard_wrapping(square)); + occupancy &= mask_rook_attacks(Bitboard(1u64.wrapping_shl(square as u32))); occupancy = occupancy.wrapping_mul(ROOK_MAGIC[square]); - occupancy >>= 64 - ROOK_RELEVANT_BITS[square]; - ROOK_ATTACKS[square][occupancy as usize] + occupancy >>= Bitboard(64 - ROOK_RELEVANT_BITS[square] as u64); + ROOK_ATTACKS[square][occupancy.0 as usize] } #[inline(always)] @@ -251,68 +250,68 @@ pub fn fetch_queen_attacks(occupancy: Bitboard, square: usize) -> Bitboard { fn init_pawn_attacks() -> [[Bitboard; 2]; 64] { array::from_fn(|square| { [ - pawn_attacks(square_to_bitboard(square), Color::White), - pawn_attacks(square_to_bitboard(square), Color::Black), + pawn_attacks(Bitboard::from(square), Color::White), + pawn_attacks(Bitboard::from(square), Color::Black), ] }) } fn init_knight_attacks() -> [Bitboard; 64] { - array::from_fn(|square| knight_attacks(square_to_bitboard(square))) + array::from_fn(|square| knight_attacks(Bitboard::from(square))) } fn init_king_attacks() -> [Bitboard; 64] { - array::from_fn(|square| king_attacks(square_to_bitboard(square))) + array::from_fn(|square| king_attacks(Bitboard::from(square))) } fn init_bishop_attacks() -> Vec> { - let mut bishop_masks = [0; 64]; + let mut bishop_masks = [Bitboard(0); 64]; let mut bishop_atks: Vec> = Vec::with_capacity(64); for square in 0..64 { - bishop_masks[square] = mask_bishop_attacks(square_to_bitboard(square)); + bishop_masks[square] = mask_bishop_attacks(Bitboard::from(square)); let attack_mask = bishop_masks[square]; - let occupancy_indices = square_to_bitboard(BISHOP_RELEVANT_BITS[square]); - let mut square_attacks = vec![0; occupancy_indices as usize]; + let occupancy_indices = Bitboard::from(BISHOP_RELEVANT_BITS[square]); + let mut square_attacks = vec![Bitboard(0); occupancy_indices.0 as usize]; - for idx in 0..occupancy_indices { + for idx in 0..occupancy_indices.0 { let occupancy = set_occupancy(idx, BISHOP_RELEVANT_BITS[square], attack_mask); let magic_idx = occupancy.wrapping_mul(BISHOP_MAGIC[square]) >> (64 - BISHOP_RELEVANT_BITS[square]); - square_attacks[magic_idx as usize] = - bishop_attacks_on_the_fly(square_to_bitboard(square), occupancy); + square_attacks[magic_idx.0 as usize] = + bishop_attacks_on_the_fly(Bitboard::from(square), occupancy); } bishop_atks.push(square_attacks); } + bishop_atks } fn init_rook_attacks() -> Vec> { - let mut rook_masks: Vec = vec![0; 64]; + let mut rook_masks = [Bitboard(0); 64]; let mut rook_atks: Vec> = Vec::with_capacity(64); for square in 0..64 { - rook_masks[square] = mask_rook_attacks(square_to_bitboard(square)); + rook_masks[square] = mask_rook_attacks(Bitboard::from(square)); let attack_mask = rook_masks[square]; - let occupancy_indices = square_to_bitboard(ROOK_RELEVANT_BITS[square]); - let mut square_attacks = vec![0; occupancy_indices as usize]; + let occupancy_indices = Bitboard::from(ROOK_RELEVANT_BITS[square]); + let mut square_attacks = vec![Bitboard(0); occupancy_indices.0 as usize]; - for idx in 0..occupancy_indices { - let occupancy = set_occupancy(idx as u64, ROOK_RELEVANT_BITS[square], attack_mask); + for idx in 0..occupancy_indices.0 { + let occupancy = set_occupancy(idx, ROOK_RELEVANT_BITS[square], attack_mask); let magic_idx = occupancy.wrapping_mul(ROOK_MAGIC[square]) >> (64 - ROOK_RELEVANT_BITS[square]); - square_attacks[magic_idx as usize] = - rook_attacks_on_the_fly(square_to_bitboard(square), occupancy); + square_attacks[magic_idx.0 as usize] = + rook_attacks_on_the_fly(Bitboard::from(square), occupancy); } rook_atks.push(square_attacks); } + rook_atks } -use std::{array, sync::LazyLock}; - pub fn init_attacks() { let _ = &*PAWN_ATTACKS; let _ = &*KNIGHT_ATTACKS; @@ -327,133 +326,118 @@ mod tests { use crate::board::{board::Color, square::Square}; #[test] - fn test_pawn_attacks() -> Result<(), String> { + fn test_pawn_attacks() { assert_eq!( - 0x20000, - pawn_attacks(square_to_bitboard(Square::A2), Color::White) + Bitboard(0x20000), + pawn_attacks(Bitboard::from(Square::A2), Color::White) ); assert_eq!( - 0x50000, - pawn_attacks(square_to_bitboard(Square::B2), Color::White) + Bitboard(0x50000), + pawn_attacks(Bitboard::from(Square::B2), Color::White) ); assert_eq!( - 0x400000, - pawn_attacks(square_to_bitboard(Square::H2), Color::White) + Bitboard(0x400000), + pawn_attacks(Bitboard::from(Square::H2), Color::White) ); assert_eq!( - 0x20000000000, - pawn_attacks(square_to_bitboard(Square::A7), Color::Black) + Bitboard(0x20000000000), + pawn_attacks(Bitboard::from(Square::A7), Color::Black) ); assert_eq!( - 0x50000000000, - pawn_attacks(square_to_bitboard(Square::B7), Color::Black) + Bitboard(0x50000000000), + pawn_attacks(Bitboard::from(Square::B7), Color::Black) ); assert_eq!( - 0x400000000000, - pawn_attacks(square_to_bitboard(Square::H7), Color::Black) + Bitboard(0x400000000000), + pawn_attacks(Bitboard::from(Square::H7), Color::Black) ); - - Ok(()) } #[test] - fn test_knight_attacks() -> Result<(), String> { - 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> { - 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> { + fn test_knight_attacks() { assert_eq!( - 0x40221400142200, - mask_bishop_attacks(square_to_bitboard(Square::D4)) + Bitboard(0x20400), + knight_attacks(Bitboard::from(Square::A1)) ); - - Ok(()) - } - - #[test] - fn test_bishop_attacks_on_the_fly() -> Result<(), String> { assert_eq!( - 0x8040201400142241, - bishop_attacks_on_the_fly( - square_to_bitboard(Square::D4), - square_to_bitboard(Square::C5) - ) + Bitboard(0x50800), + knight_attacks(Bitboard::from(Square::B1)) ); - - Ok(()) - } - - #[test] - fn test_mask_rook_attacks() -> Result<(), String> { assert_eq!( - 0x8080876080800, - mask_rook_attacks(square_to_bitboard(Square::D4)) + Bitboard(0xa1100), + knight_attacks(Bitboard::from(Square::C1)) ); - - Ok(()) - } - - #[test] - fn test_rook_attacks_on_the_fly() -> Result<(), String> { assert_eq!( - 0x8080808f4080808, - rook_attacks_on_the_fly( - square_to_bitboard(Square::D4), - square_to_bitboard(Square::C4) - ) + Bitboard(0xa110011), + knight_attacks(Bitboard::from(Square::C2)) + ); + assert_eq!( + Bitboard(0xa1100110a), + knight_attacks(Bitboard::from(Square::C3)) ); - - Ok(()) } #[test] - fn test_bishop_attacks() -> Result<(), String> { + fn test_king_attacks() { + assert_eq!(Bitboard(0x302), king_attacks(Bitboard::from(Square::A1))); + assert_eq!(Bitboard(0x705), king_attacks(Bitboard::from(Square::B1))); + assert_eq!(Bitboard(0x70507), king_attacks(Bitboard::from(Square::B2))); + } + + #[test] + fn test_mask_bishop_attacks() { + assert_eq!( + Bitboard(0x40221400142200), + mask_bishop_attacks(Bitboard::from(Square::D4)) + ); + } + + #[test] + fn test_bishop_attacks_on_the_fly() { + assert_eq!( + Bitboard(0x8040201400142241), + bishop_attacks_on_the_fly(Bitboard::from(Square::D4), Bitboard::from(Square::C5)) + ); + } + + #[test] + fn test_mask_rook_attacks() { + assert_eq!( + Bitboard(0x8080876080800), + mask_rook_attacks(Bitboard::from(Square::D4)) + ); + } + + #[test] + fn test_rook_attacks_on_the_fly() { + assert_eq!( + Bitboard(0x8080808f4080808), + rook_attacks_on_the_fly(Bitboard::from(Square::D4), Bitboard::from(Square::C4)) + ); + } + + #[test] + fn test_bishop_attacks() { init_bishop_attacks(); - let bishop_d3_square = 0x80000_u64.trailing_zeros() as usize; - let blockers = 0x602000020; - let attacks = fetch_bishop_attacks(blockers, bishop_d3_square); - assert_eq!(attacks, 0x80402214001422); - - Ok(()) + let blockers = Bitboard(0x602000020); + let attacks = fetch_bishop_attacks(blockers, Square::D3); + assert_eq!(attacks, Bitboard(0x80402214001422)); } #[test] - fn test_rook_attacks() -> Result<(), String> { + fn test_rook_attacks() { init_rook_attacks(); - let rook_d3_square = 0x80000_u64.trailing_zeros() as usize; - let blockers = 0x800000000600800; - let attacks = fetch_rook_attacks(blockers, rook_d3_square); - assert_eq!(attacks, 0x808080808370800); - - Ok(()) + let blockers = Bitboard(0x800000000600800); + let attacks = fetch_rook_attacks(blockers, Square::D3); + assert_eq!(attacks, Bitboard(0x808080808370800)); } #[test] - fn test_queen_attacks() -> Result<(), String> { + fn test_queen_attacks() { init_rook_attacks(); init_bishop_attacks(); - - let queen_d3_square = 0x80000_u64.trailing_zeros() as usize; - let blockers = 0x800000602600820; - let queen_attacks = fetch_queen_attacks(blockers, queen_d3_square); - assert_eq!(queen_attacks, 0x888482a1c371c22); - - Ok(()) + let blockers = Bitboard(0x800000602600820); + let attacks = fetch_queen_attacks(blockers, Square::D3); + assert_eq!(attacks, Bitboard(0x888482a1c371c22)); } } diff --git a/src/movegen/magic_bitboards.rs b/src/movegen/magic_bitboards.rs index b3c964a..c22a64e 100644 --- a/src/movegen/magic_bitboards.rs +++ b/src/movegen/magic_bitboards.rs @@ -1,39 +1,41 @@ +use crate::board::bitboard::Bitboard; + #[rustfmt::skip] -pub const ROOK_MAGIC: [u64; 64] = [ - 0x80008010284000, 0x540024820001000, 0x1300092000124100, 0x480240800500080, - 0x1001004020081080, 0x500040021000208, 0x6280010000800200, 0x8006a100104180, - 0x420800080204000, 0x2802000804008, 0x101805000600080, 0x1000801000080082, - 0x102808044000800, 0x280800200040080, 0x1003000100040200, 0xd201000092005100, - 0x80004000200041, 0x8400810040002100, 0x210048020008010, 0x220042001008, - 0x20850011000800, 0x80808004000200, 0x100c440008424110, 0x2020010885904, - 0x120400080002080, 0x40400100210888, 0x4860080040100041, 0x10100080080084, - 0x8008080080400, 0x124008080020004, 0x100010400021008, 0x820008200004401, - 0x404000a2800080, 0x802000804008, 0x8040809002802004, 0x1000801000800800, - 0x40080800800, 0x202800200800400, 0x100204000108, 0x48010c44020000b1, - 0x800040008024, 0x402010004000, 0x4209002000110040, 0x12002040aa0050, - 0x2002008260010, 0x80a001008020004, 0x701000a00050004, 0x241a040080420001, - 0x50043a0800100, 0x8021c002a0148080, 0x200010008080, 0x8080410008080, - 0x5180080004110100, 0x1164000402008080, 0x1002800100020080, 0x4100840200, - 0x82829101412206, 0x2209004000801021, 0x84010802202, 0x1a208900100005, - 0x1001002880005, 0x9008204000841, 0x2c9508221014, 0x8010470400402092, +pub const ROOK_MAGIC: [Bitboard; 64] = [ + Bitboard(0x80008010284000), Bitboard(0x540024820001000), Bitboard(0x1300092000124100), Bitboard(0x480240800500080), + Bitboard(0x1001004020081080), Bitboard(0x500040021000208), Bitboard(0x6280010000800200), Bitboard(0x8006a100104180), + Bitboard(0x420800080204000), Bitboard(0x2802000804008), Bitboard(0x101805000600080), Bitboard(0x1000801000080082), + Bitboard(0x102808044000800), Bitboard(0x280800200040080), Bitboard(0x1003000100040200), Bitboard(0xd201000092005100), + Bitboard(0x80004000200041), Bitboard(0x8400810040002100), Bitboard(0x210048020008010), Bitboard(0x220042001008), + Bitboard(0x20850011000800), Bitboard(0x80808004000200), Bitboard(0x100c440008424110), Bitboard(0x2020010885904), + Bitboard(0x120400080002080), Bitboard(0x40400100210888), Bitboard(0x4860080040100041), Bitboard(0x10100080080084), + Bitboard(0x8008080080400), Bitboard(0x124008080020004), Bitboard(0x100010400021008), Bitboard(0x820008200004401), + Bitboard(0x404000a2800080), Bitboard(0x802000804008), Bitboard(0x8040809002802004), Bitboard(0x1000801000800800), + Bitboard(0x40080800800), Bitboard(0x202800200800400), Bitboard(0x100204000108), Bitboard(0x48010c44020000b1), + Bitboard(0x800040008024), Bitboard(0x402010004000), Bitboard(0x4209002000110040), Bitboard(0x12002040aa0050), + Bitboard(0x2002008260010), Bitboard(0x80a001008020004), Bitboard(0x701000a00050004), Bitboard(0x241a040080420001), + Bitboard(0x50043a0800100), Bitboard(0x8021c002a0148080), Bitboard(0x200010008080), Bitboard(0x8080410008080), + Bitboard(0x5180080004110100), Bitboard(0x1164000402008080), Bitboard(0x1002800100020080), Bitboard(0x4100840200), + Bitboard(0x82829101412206), Bitboard(0x2209004000801021), Bitboard(0x84010802202), Bitboard(0x1a208900100005), + Bitboard(0x1001002880005), Bitboard(0x9008204000841), Bitboard(0x2c9508221014), Bitboard(0x8010470400402092), ]; #[rustfmt::skip] -pub const BISHOP_MAGIC: [u64; 64] = [ - 0x10041000802d00, 0x3410820e4100b0, 0x1008080120200000, 0x4080a0020104000, - 0x14152000000050, 0x1010841202844, 0x80480210100208, 0x8042410050022000, - 0x2400204810a00, 0x1003020204010e, 0xa012044104010800, 0x804280a042108c2, - 0x11040010810, 0x2010228820298000, 0x21440100c210400c, 0x910002104108414, - 0x249012002102220, 0x210000421082108, 0xa001001083040100, 0x880c007801441100, - 0xe00082ac00a01800, 0x40020004a2032001, 0x82aa016048040580, 0x40205301291000, - 0x1060110004100200, 0x8002035010040800, 0x8401001a080508, 0xc0044004050020, - 0x409010080104008, 0x28029232004400, 0x4008888002121000, 0x8041220041004118, - 0x8018241008404200, 0x108021820348e, 0x108c0104100040, 0x8320080800460a00, - 0x10008200802200, 0x284208080980800, 0x42020402122080, 0x9702004040410400, - 0x1092070012122, 0x2214024619001121, 0x491420140208d000, 0x7460009414003802, - 0x4022084104000040, 0x1084248085020200, 0x8808102400602480, 0x120410a400428508, - 0x4014808050804, 0x408406804101008, 0x41081044d500031, 0x421080284044020, - 0x4060188813041110, 0x40202041024080, 0xa0212002108100a4, 0x3020202020480, - 0x1a802808062882, 0x80110101012100, 0xa40100200420890, 0x48025100208801, - 0x4000000004208200, 0x804a10011602, 0x200a24c410041500, 0x8408080088061020, +pub const BISHOP_MAGIC: [Bitboard; 64] = [ + Bitboard(0x10041000802d00), Bitboard(0x3410820e4100b0), Bitboard(0x1008080120200000), Bitboard(0x4080a0020104000), + Bitboard(0x14152000000050), Bitboard(0x1010841202844), Bitboard(0x80480210100208), Bitboard(0x8042410050022000), + Bitboard(0x2400204810a00), Bitboard(0x1003020204010e), Bitboard(0xa012044104010800), Bitboard(0x804280a042108c2), + Bitboard(0x11040010810), Bitboard(0x2010228820298000), Bitboard(0x21440100c210400c), Bitboard(0x910002104108414), + Bitboard(0x249012002102220), Bitboard(0x210000421082108), Bitboard(0xa001001083040100), Bitboard(0x880c007801441100), + Bitboard(0xe00082ac00a01800), Bitboard(0x40020004a2032001), Bitboard(0x82aa016048040580), Bitboard(0x40205301291000), + Bitboard(0x1060110004100200), Bitboard(0x8002035010040800), Bitboard(0x8401001a080508), Bitboard(0xc0044004050020), + Bitboard(0x409010080104008), Bitboard(0x28029232004400), Bitboard(0x4008888002121000), Bitboard(0x8041220041004118), + Bitboard(0x8018241008404200), Bitboard(0x108021820348e), Bitboard(0x108c0104100040), Bitboard(0x8320080800460a00), + Bitboard(0x10008200802200), Bitboard(0x284208080980800), Bitboard(0x42020402122080), Bitboard(0x9702004040410400), + Bitboard(0x1092070012122), Bitboard(0x2214024619001121), Bitboard(0x491420140208d000), Bitboard(0x7460009414003802), + Bitboard(0x4022084104000040), Bitboard(0x1084248085020200), Bitboard(0x8808102400602480), Bitboard(0x120410a400428508), + Bitboard(0x4014808050804), Bitboard(0x408406804101008), Bitboard(0x41081044d500031), Bitboard(0x421080284044020), + Bitboard(0x4060188813041110), Bitboard(0x40202041024080), Bitboard(0xa0212002108100a4), Bitboard(0x3020202020480), + Bitboard(0x1a802808062882), Bitboard(0x80110101012100), Bitboard(0xa40100200420890), Bitboard(0x48025100208801), + Bitboard(0x4000000004208200), Bitboard(0x804a10011602), Bitboard(0x200a24c410041500), Bitboard(0x8408080088061020), ]; diff --git a/src/movegen/move_generator.rs b/src/movegen/move_generator.rs index 66c9f26..e50cb04 100644 --- a/src/movegen/move_generator.rs +++ b/src/movegen/move_generator.rs @@ -1,6 +1,5 @@ use crate::board::bitboard::{ - have_common_bit, lsb, square_to_bitboard, NOT_FILE_A, NOT_FILE_H, RANK_1, RANK_2, RANK_4, - RANK_5, RANK_7, RANK_8, + Bitboard, NOT_FILE_A, NOT_FILE_H, RANK_1, RANK_2, RANK_4, RANK_5, RANK_7, RANK_8, }; use crate::board::board::{Board, Color}; use crate::board::state::Castle; @@ -9,7 +8,6 @@ use crate::movegen::attack_generator::{ fetch_queen_attacks, fetch_rook_attacks, }; use crate::movegen::r#move::{Move, MoveType}; -use u64 as Bitboard; pub fn pawn_pseudo_moves( pawns: Bitboard, @@ -63,11 +61,11 @@ fn white_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec { let empty = !occupancies; let mut single_push = (pawns << 8) & empty; - while single_push != 0 { - let dst = lsb(single_push); + while !single_push.is_empty() { + let dst = single_push.lsb(); let src = dst - 8; - if have_common_bit(square_to_bitboard(src), RANK_7) { + if Bitboard::from(src).intersects(RANK_7) { add_promotion_moves(&mut moves, src, dst, false); } else { moves.push(Move::new(src, dst)); @@ -77,8 +75,9 @@ fn white_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec { let single_push = (pawns << 8) & empty; let mut double_push = (single_push << 8) & empty & RANK_4; - while double_push != 0 { - let dst = lsb(double_push); + + while !double_push.is_empty() { + let dst = double_push.lsb(); let src = dst - 16; moves.push(Move::new_with_type(src, dst, MoveType::DoublePush)); double_push &= double_push - 1; @@ -91,11 +90,11 @@ fn black_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec { let empty = !occupancies; let mut single_push = (pawns >> 8) & empty; - while single_push != 0 { - let dst = lsb(single_push); + while !single_push.is_empty() { + let dst = single_push.lsb(); let src = dst + 8; - if have_common_bit(square_to_bitboard(src), RANK_2) { + if Bitboard::from(src).intersects(RANK_2) { add_promotion_moves(&mut moves, src, dst, false); } else { moves.push(Move::new(src, dst)); @@ -105,8 +104,9 @@ fn black_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec { let single_push = (pawns >> 8) & empty; let mut double_push = (single_push >> 8) & empty & RANK_5; - while double_push != 0 { - let dst = lsb(double_push); + + while !double_push.is_empty() { + let dst = double_push.lsb(); let src = dst + 16; moves.push(Move::new_with_type(src, dst, MoveType::DoublePush)); double_push &= double_push - 1; @@ -123,22 +123,23 @@ fn white_pawn_capture_moves( let mut w_pawns_capture_east = pawns & ((opponent_occupancies >> 9) & NOT_FILE_H); let mut w_pawns_capture_west = pawns & ((opponent_occupancies >> 7) & NOT_FILE_A); - while w_pawns_capture_east != 0 { - let src = lsb(w_pawns_capture_east); + while !w_pawns_capture_east.is_empty() { + let src = w_pawns_capture_east.lsb(); let dst = src + 9; - if have_common_bit(square_to_bitboard(dst), RANK_8) { + if Bitboard::from(dst).intersects(RANK_8) { add_promotion_moves(&mut moves, src, dst, true); } else { moves.push(Move::new_with_type(src, dst, MoveType::Capture)); } w_pawns_capture_east &= w_pawns_capture_east - 1; } - while w_pawns_capture_west != 0 { - let src = lsb(w_pawns_capture_west); + + while !w_pawns_capture_west.is_empty() { + let src = w_pawns_capture_west.lsb(); let dst = src + 7; - if have_common_bit(square_to_bitboard(dst), RANK_8) { + if Bitboard::from(dst).intersects(RANK_8) { add_promotion_moves(&mut moves, src, dst, true); } else { moves.push(Move::new_with_type(src, dst, MoveType::Capture)); @@ -149,9 +150,10 @@ fn white_pawn_capture_moves( if let Some(en_passant_sq) = en_passant_square { let attacked_src = fetch_pawn_attacks(en_passant_sq, Color::Black); let mut result = attacked_src & pawns; - while result != 0 { + + while !result.is_empty() { moves.push(Move::new_with_type( - lsb(result), + result.lsb(), en_passant_sq, MoveType::EnPassant, )); @@ -170,11 +172,11 @@ fn black_pawn_capture_moves( let mut b_pawns_capture_east = pawns & ((opponent_occupancies << 7) & NOT_FILE_H); let mut b_pawns_capture_west = pawns & ((opponent_occupancies << 9) & NOT_FILE_A); - while b_pawns_capture_east != 0 { - let src = lsb(b_pawns_capture_east); + while !b_pawns_capture_east.is_empty() { + let src = b_pawns_capture_east.lsb(); let dst = src - 7; - if have_common_bit(square_to_bitboard(dst), RANK_1) { + if Bitboard::from(dst).intersects(RANK_1) { add_promotion_moves(&mut moves, src, dst, true); } else { moves.push(Move::new_with_type(src, dst, MoveType::Capture)); @@ -182,10 +184,10 @@ fn black_pawn_capture_moves( b_pawns_capture_east &= b_pawns_capture_east - 1; } - while b_pawns_capture_west != 0 { - let src = lsb(b_pawns_capture_west); + while !b_pawns_capture_west.is_empty() { + let src = b_pawns_capture_west.lsb(); let dst = src - 9; - if have_common_bit(square_to_bitboard(dst), RANK_1) { + if Bitboard::from(dst).intersects(RANK_1) { add_promotion_moves(&mut moves, src, dst, true); } else { moves.push(Move::new_with_type(src, dst, MoveType::Capture)); @@ -196,9 +198,10 @@ fn black_pawn_capture_moves( if let Some(en_passant_square) = en_passant_square { let attacked_src = fetch_pawn_attacks(en_passant_square, Color::White); let mut result = attacked_src & pawns; - while result != 0 { + + while !result.is_empty() { moves.push(Move::new_with_type( - lsb(result), + result.lsb(), en_passant_square, MoveType::EnPassant, )); @@ -216,14 +219,14 @@ pub fn knight_pseudo_moves( let mut moves = vec![]; let opponent_occupancies = all_occupancies ^ own_occupancies; - while knights != 0 { - let knight_square = lsb(knights); + while !knights.is_empty() { + let knight_square = knights.lsb(); let src = knight_square; let mut attacks = fetch_knight_attacks(knight_square) & !own_occupancies; - while attacks != 0 { - let attack_sq = lsb(attacks); - if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { + while !attacks.is_empty() { + let attack_sq = attacks.lsb(); + if Bitboard::from(attack_sq).intersects(opponent_occupancies) { moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); } else { moves.push(Move::new(src, attack_sq)); @@ -243,14 +246,14 @@ pub fn bishop_pseudo_moves( let mut moves = vec![]; let opponent_occupancies = all_occupancies ^ own_occupancies; - while bishops != 0 { - let bishop_square = lsb(bishops); + while !bishops.is_empty() { + let bishop_square = bishops.lsb(); let src = bishop_square; let mut attacks = fetch_bishop_attacks(all_occupancies, bishop_square) & !own_occupancies; - while attacks != 0 { - let attack_sq = lsb(attacks); - if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { + while !attacks.is_empty() { + let attack_sq = attacks.lsb(); + if Bitboard::from(attack_sq).intersects(opponent_occupancies) { moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); } else { moves.push(Move::new(src, attack_sq)); @@ -270,14 +273,14 @@ pub fn rook_pseudo_moves( let mut moves = vec![]; let opponent_occupancies = all_occupancies ^ own_occupancies; - while rooks != 0 { - let rook_square = lsb(rooks); + while !rooks.is_empty() { + let rook_square = rooks.lsb(); let src = rook_square; let mut attacks = fetch_rook_attacks(all_occupancies, rook_square) & !own_occupancies; - while attacks != 0 { - let attack_sq = lsb(attacks); - if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { + while !attacks.is_empty() { + let attack_sq = attacks.lsb(); + if Bitboard::from(attack_sq).intersects(opponent_occupancies) { moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); } else { moves.push(Move::new(src, attack_sq)); @@ -297,14 +300,14 @@ pub fn queen_pseudo_moves( let mut moves = vec![]; let opponent_occupancies = all_occupancies ^ own_occupancies; - while queens != 0 { - let queen_square = lsb(queens); + while !queens.is_empty() { + let queen_square = queens.lsb(); let src = queen_square; let mut attacks = fetch_queen_attacks(all_occupancies, queen_square) & !own_occupancies; - while attacks != 0 { - let attack_sq = lsb(attacks); - if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { + while !attacks.is_empty() { + let attack_sq = attacks.lsb(); + if Bitboard::from(attack_sq).intersects(opponent_occupancies) { moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); } else { moves.push(Move::new(src, attack_sq)); @@ -324,14 +327,14 @@ pub fn king_pseudo_moves( color: Color, ) -> Vec { let mut moves = vec![]; - let king_square = lsb(king); + let king_square = king.lsb(); let src = king_square; let mut attacks = fetch_king_attacks(king_square) & !own_occupancies; let opponent_occupancies = all_occupancies ^ own_occupancies; - while attacks != 0 { - let attack_sq = lsb(attacks); - if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { + while !attacks.is_empty() { + let attack_sq = attacks.lsb(); + if Bitboard::from(attack_sq).intersects(opponent_occupancies) { moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); } else { moves.push(Move::new(src, attack_sq)); @@ -346,12 +349,18 @@ pub fn king_pseudo_moves( fn king_castling_moves(board: &Board, color: Color, all_occupancies: Bitboard) -> Vec { let mut moves = vec![]; let (king_src, king_dst_short, king_dst_long, path_short, path_long) = match color { - Color::White => (4, 6, 2, 0x60_u64, 0xe_u64), - Color::Black => (60, 62, 58, 0x6000000000000000_u64, 0xe00000000000000_u64), + Color::White => (4, 6, 2, Bitboard(0x60_u64), Bitboard(0xe_u64)), + Color::Black => ( + 60, + 62, + 58, + Bitboard(0x6000000000000000_u64), + Bitboard(0xe00000000000000_u64), + ), }; let mut add_move_if_empty_path = |path_mask, king_dst| { - if !have_common_bit(all_occupancies, path_mask) { + if !all_occupancies.intersects(path_mask) { if king_dst > king_src { moves.push(Move::new_with_type( king_src,