Replace u64 bitboards with tuple struct

This commit is contained in:
stefiosif
2025-03-29 23:28:17 +02:00
parent 6653d8034f
commit 6b009383fe
9 changed files with 440 additions and 329 deletions

View File

@@ -1,41 +1,139 @@
use u64 as Bitboard; use std::ops;
pub const EMPTY: Bitboard = 0x0; use super::square::Square;
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;
pub const fn square_to_bitboard(square: usize) -> Bitboard { #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
1_u64 << square pub struct Bitboard(pub u64);
}
pub const fn square_to_bitboard_wrapping(square: usize) -> Bitboard { pub const EMPTY: Bitboard = Bitboard(0x0);
1_u64.wrapping_shl(square as u32) 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) { impl From<Square> for Bitboard {
match bitboard { #[inline(always)]
0 => (0, 0), fn from(square: Square) -> Self {
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8), Self(1_u64 << (square.0 as u32))
} }
} }
pub const fn lsb(bitboard: Bitboard) -> usize { impl From<usize> for Bitboard {
bitboard.trailing_zeros() as usize #[inline(always)]
fn from(square: usize) -> Self {
Self(1_u64 << (square as u32))
}
} }
#[allow(dead_code)] impl From<u64> for Bitboard {
pub const fn bit_count(bitboard: Bitboard) -> usize { #[inline(always)]
bitboard.count_ones() as usize fn from(square: u64) -> Self {
Self(1_u64 << (square as u32))
}
} }
pub const fn have_common_bit(bitboard_a: Bitboard, bitboard_b: Bitboard) -> bool { impl ops::Add<usize> for Bitboard {
bitboard_a & bitboard_b != 0 type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs as u64)
}
}
impl ops::Sub<usize> for Bitboard {
type Output = Self;
fn sub(self, rhs: usize) -> Self::Output {
Self(self.0 - rhs as u64)
}
}
impl ops::BitAnd<Self> for Bitboard {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl ops::BitOr<Self> for Bitboard {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl ops::BitAndAssign<Self> for Bitboard {
fn bitand_assign(&mut self, rhs: Self) {
self.0 = self.0 & rhs.0
}
}
impl ops::BitOrAssign<Self> 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<usize> for Bitboard {
type Output = Self;
fn shl(self, rhs: usize) -> Self::Output {
Self(self.0 << rhs)
}
}
impl ops::Shr<usize> 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<Self> 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))
}
} }

View File

@@ -1,7 +1,5 @@
use strum_macros::EnumIter; 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::board::state::State;
use crate::movegen::attack_generator::{ use crate::movegen::attack_generator::{
fetch_bishop_attacks, fetch_king_attacks, fetch_knight_attacks, fetch_pawn_attacks, fetch_bishop_attacks, fetch_king_attacks, fetch_knight_attacks, fetch_pawn_attacks,
@@ -25,22 +23,22 @@ impl Board {
pub const fn startpos() -> Self { pub const fn startpos() -> Self {
Self { Self {
pieces: [ pieces: [
0xff00000000ff00, Bitboard(0xff00000000ff00),
0x4200000000000042, Bitboard(0x4200000000000042),
0x2400000000000024, Bitboard(0x2400000000000024),
0x8100000000000081, Bitboard(0x8100000000000081),
0x800000000000008, Bitboard(0x800000000000008),
0x1000000000000010, Bitboard(0x1000000000000010),
], ],
color: [0xffff, 0xffff000000000000], color: [Bitboard(0xffff), Bitboard(0xffff000000000000)],
state: State::new(), state: State::new(),
} }
} }
pub const fn new_empty() -> Self { pub const fn new_empty() -> Self {
Self { Self {
pieces: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0], pieces: [EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
color: [0x0, 0x0], color: [EMPTY, EMPTY],
state: State::new(), state: State::new(),
} }
} }
@@ -48,7 +46,7 @@ impl Board {
pub fn is_attacked(&self, square: usize, opponent_color: Color) -> bool { pub fn is_attacked(&self, square: usize, opponent_color: Color) -> bool {
let all_occupancies = self.color[Color::White] | self.color[Color::Black]; let all_occupancies = self.color[Color::White] | self.color[Color::Black];
let own_color = opponent_color.opponent(); 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 pawns = self.pieces[PieceType::Pawn] & opponent_color_bb;
let knights = self.pieces[PieceType::Knight] & 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 queens = self.pieces[PieceType::Queen] & opponent_color_bb;
let king = self.pieces[PieceType::King] & opponent_color_bb; let king = self.pieces[PieceType::King] & opponent_color_bb;
have_common_bit(pawns, fetch_pawn_attacks(square, own_color)) pawns.intersects(fetch_pawn_attacks(square, own_color))
|| have_common_bit(knights, fetch_knight_attacks(square)) || knights.intersects(fetch_knight_attacks(square))
|| have_common_bit(bishops, fetch_bishop_attacks(all_occupancies, square)) || bishops.intersects(fetch_bishop_attacks(all_occupancies, square))
|| have_common_bit(rooks, fetch_rook_attacks(all_occupancies, square)) || rooks.intersects(fetch_rook_attacks(all_occupancies, square))
|| have_common_bit(queens, fetch_queen_attacks(all_occupancies, square)) || queens.intersects(fetch_queen_attacks(all_occupancies, square))
|| have_common_bit(king, fetch_king_attacks(square)) || king.intersects(fetch_king_attacks(square))
} }
pub fn king_under_check(&self, color: Color) -> bool { 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()) self.is_attacked(own_king_square, color.opponent())
} }
@@ -115,10 +113,11 @@ impl Board {
} }
pub fn non_pawn_materials(&self) -> u64 { pub fn non_pawn_materials(&self) -> u64 {
self.pieces[PieceType::Knight] (self.pieces[PieceType::Knight]
| self.pieces[PieceType::Bishop] | self.pieces[PieceType::Bishop]
| self.pieces[PieceType::Rook] | 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) { 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 { pub fn is_pawn_move(&self, square: usize) -> bool {
let side = self.state.current_player(); let side = self.state.current_player();
let own_pawns = self.pieces[PieceType::Pawn] & self.color[side]; 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) { 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] &= !Bitboard::from(src);
self.pieces[piece_type] |= square_to_bitboard(dst); self.pieces[piece_type] |= Bitboard::from(dst);
self.color[self.state.current_player()] &= !square_to_bitboard(src); self.color[self.state.current_player()] &= !Bitboard::from(src);
self.color[self.state.current_player()] |= square_to_bitboard(dst); self.color[self.state.current_player()] |= Bitboard::from(dst);
} }
pub fn insert_own_piece(&mut self, square: usize, piece_type: PieceType) { pub fn insert_own_piece(&mut self, square: usize, piece_type: PieceType) {
self.pieces[piece_type] |= square_to_bitboard(square); self.pieces[piece_type] |= Bitboard::from(square);
self.color[self.state.current_player()] |= square_to_bitboard(square); self.color[self.state.current_player()] |= Bitboard::from(square);
} }
pub fn insert_opponent_piece(&mut self, square: usize, piece_type: PieceType) { pub fn insert_opponent_piece(&mut self, square: usize, piece_type: PieceType) {
self.pieces[piece_type] |= square_to_bitboard(square); self.pieces[piece_type] |= Bitboard::from(square);
self.color[self.state.next_player()] |= square_to_bitboard(square); self.color[self.state.next_player()] |= Bitboard::from(square);
} }
pub fn remove_own_piece(&mut self, square: usize, piece_type: PieceType) { pub fn remove_own_piece(&mut self, square: usize, piece_type: PieceType) {
self.pieces[piece_type] &= !square_to_bitboard(square); self.pieces[piece_type] &= !Bitboard::from(square);
self.color[self.state.current_player()] &= !square_to_bitboard(square); self.color[self.state.current_player()] &= !Bitboard::from(square);
} }
pub fn remove_opponent_piece(&mut self, square: usize, piece_type: PieceType) { pub fn remove_opponent_piece(&mut self, square: usize, piece_type: PieceType) {
self.pieces[piece_type] &= !square_to_bitboard(square); self.pieces[piece_type] &= !Bitboard::from(square);
self.color[self.state.next_player()] &= !square_to_bitboard(square); self.color[self.state.next_player()] &= !Bitboard::from(square);
} }
pub fn promote_piece(&mut self, square: usize, promote: Promote) { pub fn promote_piece(&mut self, square: usize, promote: Promote) {
match promote { match promote {
Promote::Knight => self.pieces[PieceType::Knight] |= square_to_bitboard(square), Promote::Knight => self.pieces[PieceType::Knight] |= Bitboard::from(square),
Promote::Bishop => self.pieces[PieceType::Bishop] |= square_to_bitboard(square), Promote::Bishop => self.pieces[PieceType::Bishop] |= Bitboard::from(square),
Promote::Rook => self.pieces[PieceType::Rook] |= square_to_bitboard(square), Promote::Rook => self.pieces[PieceType::Rook] |= Bitboard::from(square),
Promote::Queen => self.pieces[PieceType::Queen] |= square_to_bitboard(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 std::ops::{Index, IndexMut};
use super::bitboard::{Bitboard, EMPTY};
impl<T> Index<PieceType> for [T] { impl<T> Index<PieceType> for [T] {
type Output = T; type Output = T;
@@ -256,11 +257,14 @@ mod tests {
fn test_occupancies() -> Result<(), String> { fn test_occupancies() -> Result<(), String> {
let new_game = from_fen(FEN_EXAMPLE[0])?; let new_game = from_fen(FEN_EXAMPLE[0])?;
assert_eq!(new_game.board.color[Color::White], 0x40000002000000); assert_eq!(
assert_eq!(new_game.board.color[Color::Black], 0x900204401002); new_game.board.color[Color::White],
Bitboard(0x40000002000000)
);
assert_eq!(new_game.board.color[Color::Black], Bitboard(0x900204401002));
assert_eq!( assert_eq!(
new_game.board.color[Color::White] | new_game.board.color[Color::Black], new_game.board.color[Color::White] | new_game.board.color[Color::Black],
0x40900206401002 Bitboard(0x40900206401002)
); );
Ok(()) Ok(())

View File

@@ -76,7 +76,7 @@ pub fn piece_placement(pieces: &str) -> Result<Board, FenError> {
)) ))
} }
} { } {
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; file += 1;
}; };
} }
@@ -137,7 +137,7 @@ fn castling_ability(castling: &str) -> Result<[Castle; 2], FenError> {
use std::collections::HashMap; use std::collections::HashMap;
use super::bitboard::square_to_bitboard; use super::bitboard::Bitboard;
use super::mailbox::Mailbox; use super::mailbox::Mailbox;
use super::zobrist::zobrist_keys; use super::zobrist::zobrist_keys;

View File

@@ -1,7 +1,7 @@
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use super::{ use super::{
bitboard::{have_common_bit, square_to_bitboard}, bitboard::Bitboard,
board::{Board, Color, PieceType}, board::{Board, Color, PieceType},
}; };
@@ -16,7 +16,7 @@ impl Mailbox {
*m = PieceType::iter() *m = PieceType::iter()
.flat_map(|p| Color::iter().map(move |c| (p, c))) .flat_map(|p| Color::iter().map(move |c| (p, c)))
.find(|&(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))
}); });
} }

View File

@@ -1,4 +1,6 @@
pub struct Square {} use super::bitboard::Bitboard;
pub struct Square(pub u8);
#[allow(dead_code)] #[allow(dead_code)]
impl Square { impl Square {
@@ -89,3 +91,16 @@ pub fn to_algebraic(square: usize) -> String {
format!("{}{}", (file + b'a') as char, (rank + b'1') as char) 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<Bitboard> for Coords {
fn from(bitboard: Bitboard) -> Self {
if bitboard.is_empty() {
return Self(0, 0);
}
Self(bitboard.lsb() / 8, bitboard.lsb() % 8)
}
}

View File

@@ -1,7 +1,6 @@
use crate::movegen::r#move::Promote; use crate::movegen::r#move::Promote;
use super::{ use super::{
bitboard::lsb,
board::{Board, Color, PieceType}, board::{Board, Color, PieceType},
square::{self, Square}, square::{self, Square},
state::Castle, state::Castle,
@@ -54,16 +53,16 @@ impl ZobristKeys {
PieceType::iter().for_each(|piece_type| { PieceType::iter().for_each(|piece_type| {
let mut bitboard = board.pieces[piece_type] & board.color[Color::White]; let mut bitboard = board.pieces[piece_type] & board.color[Color::White];
while bitboard != 0 { while !bitboard.is_empty() {
let square = lsb(bitboard); let square = bitboard.lsb();
hash ^= self.square_piece_color[square][piece_type][Color::White]; hash ^= self.square_piece_color[square][piece_type][Color::White];
bitboard &= bitboard - 1; bitboard &= bitboard - 1;
} }
let mut bitboard = board.pieces[piece_type] & board.color[Color::Black]; let mut bitboard = board.pieces[piece_type] & board.color[Color::Black];
while bitboard != 0 { while !bitboard.is_empty() {
let square = lsb(bitboard); let square = bitboard.lsb();
hash ^= self.square_piece_color[square][piece_type][Color::Black]; hash ^= self.square_piece_color[square][piece_type][Color::Black];
bitboard &= bitboard - 1; bitboard &= bitboard - 1;
} }

View File

@@ -1,15 +1,13 @@
use crate::board::{ use crate::board::{
bitboard::{ bitboard::{Bitboard, EMPTY, NOT_FILE_A, NOT_FILE_AB, NOT_FILE_GH, NOT_FILE_H},
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,
},
board::Color, board::Color,
square::from_coords, square::{from_coords, Coords},
}; };
use u64 as Bitboard;
use std::{array, sync::LazyLock};
#[rustfmt::skip] #[rustfmt::skip]
pub const ROOK_RELEVANT_BITS: [usize; 64] = [ const ROOK_RELEVANT_BITS: [usize; 64] = [
12, 11, 11, 11, 11, 11, 11, 12, 12, 11, 11, 11, 11, 11, 11, 12,
11, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11,
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] #[rustfmt::skip]
pub const BISHOP_RELEVANT_BITS: [usize; 64] = [ const BISHOP_RELEVANT_BITS: [usize; 64] = [
6, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 6,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 7, 7, 7, 7, 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<Vec<Vec<Bitboard>>> = LazyLock::new(init_bishop_attacks); static BISHOP_ATTACKS: LazyLock<Vec<Vec<Bitboard>>> = LazyLock::new(init_bishop_attacks);
static ROOK_ATTACKS: LazyLock<Vec<Vec<Bitboard>>> = LazyLock::new(init_rook_attacks); static ROOK_ATTACKS: LazyLock<Vec<Vec<Bitboard>>> = 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; let mut attacks = EMPTY;
match color { match color {
@@ -55,7 +53,7 @@ const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard {
attacks attacks
} }
const fn knight_attacks(bitboard: Bitboard) -> Bitboard { fn knight_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = EMPTY; let mut attacks = EMPTY;
attacks |= (bitboard & NOT_FILE_AB) << 6; attacks |= (bitboard & NOT_FILE_AB) << 6;
@@ -70,7 +68,7 @@ const fn knight_attacks(bitboard: Bitboard) -> Bitboard {
attacks attacks
} }
const fn king_attacks(bitboard: Bitboard) -> Bitboard { fn king_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = EMPTY; let mut attacks = EMPTY;
attacks |= (bitboard & NOT_FILE_H) << 1; attacks |= (bitboard & NOT_FILE_H) << 1;
@@ -87,16 +85,16 @@ const fn king_attacks(bitboard: Bitboard) -> Bitboard {
fn mask_bishop_attacks(bitboard: Bitboard) -> Bitboard { fn mask_bishop_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = EMPTY; 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 add_attacks_in_direction = |rank_step: isize, file_step: isize| {
let mut rank = rank_dst as isize; let mut rank = coords.0 as isize;
let mut file = file_dst as isize; let mut file = coords.1 as isize;
while (1..=6).contains(&(rank + rank_step)) && (1..=6).contains(&(file + file_step)) { while (1..=6).contains(&(rank + rank_step)) && (1..=6).contains(&(file + file_step)) {
rank += rank_step; rank += rank_step;
file += file_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 { fn mask_rook_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = EMPTY; 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| { let mut add_attacks_in_direction = |mut coord: usize, step: isize, is_vertical: bool| {
while (1..=6).contains(&(coord as isize + step)) { while (1..=6).contains(&(coord as isize + step)) {
coord = (coord as isize + step) as usize; coord = (coord as isize + step) as usize;
let attack_square = if is_vertical { let attack_square = if is_vertical {
square_to_bitboard(from_coords(coord, file_dst)) Bitboard::from(from_coords(coord, coords.1))
} else { } else {
square_to_bitboard(from_coords(rank_dst, coord)) Bitboard::from(from_coords(coords.0, coord))
}; };
attacks |= attack_square; attacks |= attack_square;
} }
}; };
add_attacks_in_direction(rank_dst, 1, true); add_attacks_in_direction(coords.0, 1, true);
add_attacks_in_direction(rank_dst, -1, true); add_attacks_in_direction(coords.0, -1, true);
add_attacks_in_direction(file_dst, 1, false); add_attacks_in_direction(coords.1, 1, false);
add_attacks_in_direction(file_dst, -1, false); add_attacks_in_direction(coords.1, -1, false);
attacks attacks
} }
fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard { fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
let mut attacks = EMPTY; 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 add_attacks_in_direction = |rank_step: isize, file_step: isize| {
let mut rank = rank_dst as isize; let mut rank = coords.0 as isize;
let mut file = file_dst as isize; let mut file = coords.1 as isize;
while (0..=7).contains(&(rank + rank_step)) && (0..=7).contains(&(file + file_step)) { while (0..=7).contains(&(rank + rank_step)) && (0..=7).contains(&(file + file_step)) {
rank += rank_step; rank += rank_step;
file += file_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; attacks |= attack_square;
if have_common_bit(attack_square, blocker) { if attack_square.intersects(blocker) {
break; 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 { fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
let mut attacks = EMPTY; 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| { let mut add_attacks_in_direction = |mut coord: usize, step: isize, is_vertical: bool| {
while (0..=7).contains(&(coord as isize + step)) { while (0..=7).contains(&(coord as isize + step)) {
coord = (coord as isize + step) as usize; coord = (coord as isize + step) as usize;
let attack_square = if is_vertical { let attack_square = if is_vertical {
square_to_bitboard(from_coords(coord, file_dst)) Bitboard::from(from_coords(coord, coords.1))
} else { } else {
square_to_bitboard(from_coords(rank_dst, coord)) Bitboard::from(from_coords(coords.0, coord))
}; };
attacks |= attack_square; attacks |= attack_square;
if have_common_bit(attack_square, blocker) { if attack_square.intersects(blocker) {
break; break;
} }
} }
}; };
add_attacks_in_direction(rank_dst, 1, true); add_attacks_in_direction(coords.0, 1, true);
add_attacks_in_direction(rank_dst, -1, true); add_attacks_in_direction(coords.0, -1, true);
add_attacks_in_direction(file_dst, 1, false); add_attacks_in_direction(coords.1, 1, false);
add_attacks_in_direction(file_dst, -1, false); add_attacks_in_direction(coords.1, -1, false);
attacks attacks
} }
@@ -197,10 +195,11 @@ fn set_occupancy(idx: u64, relevant_bits: usize, mut attack_mask: Bitboard) -> B
let mut occupancy = EMPTY; let mut occupancy = EMPTY;
for bit in 0..relevant_bits { for bit in 0..relevant_bits {
let square = lsb(attack_mask); let square = attack_mask.lsb();
attack_mask &= !square_to_bitboard(square); attack_mask &= !Bitboard::from(square);
if have_common_bit(idx, square_to_bitboard(bit)) {
occupancy |= square_to_bitboard(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)] #[inline(always)]
pub fn fetch_bishop_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard { 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 = occupancy.wrapping_mul(BISHOP_MAGIC[square]);
occupancy >>= 64 - BISHOP_RELEVANT_BITS[square]; occupancy >>= Bitboard(64 - BISHOP_RELEVANT_BITS[square] as u64);
BISHOP_ATTACKS[square][occupancy as usize] BISHOP_ATTACKS[square][occupancy.0 as usize]
} }
#[inline(always)] #[inline(always)]
pub fn fetch_rook_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard { 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 = occupancy.wrapping_mul(ROOK_MAGIC[square]);
occupancy >>= 64 - ROOK_RELEVANT_BITS[square]; occupancy >>= Bitboard(64 - ROOK_RELEVANT_BITS[square] as u64);
ROOK_ATTACKS[square][occupancy as usize] ROOK_ATTACKS[square][occupancy.0 as usize]
} }
#[inline(always)] #[inline(always)]
@@ -251,68 +250,68 @@ pub fn fetch_queen_attacks(occupancy: Bitboard, square: usize) -> Bitboard {
fn init_pawn_attacks() -> [[Bitboard; 2]; 64] { fn init_pawn_attacks() -> [[Bitboard; 2]; 64] {
array::from_fn(|square| { array::from_fn(|square| {
[ [
pawn_attacks(square_to_bitboard(square), Color::White), pawn_attacks(Bitboard::from(square), Color::White),
pawn_attacks(square_to_bitboard(square), Color::Black), pawn_attacks(Bitboard::from(square), Color::Black),
] ]
}) })
} }
fn init_knight_attacks() -> [Bitboard; 64] { 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] { 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<Vec<Bitboard>> { fn init_bishop_attacks() -> Vec<Vec<Bitboard>> {
let mut bishop_masks = [0; 64]; let mut bishop_masks = [Bitboard(0); 64];
let mut bishop_atks: Vec<Vec<Bitboard>> = Vec::with_capacity(64); let mut bishop_atks: Vec<Vec<Bitboard>> = Vec::with_capacity(64);
for square in 0..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 attack_mask = bishop_masks[square];
let occupancy_indices = square_to_bitboard(BISHOP_RELEVANT_BITS[square]); let occupancy_indices = Bitboard::from(BISHOP_RELEVANT_BITS[square]);
let mut square_attacks = vec![0; occupancy_indices as usize]; 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 occupancy = set_occupancy(idx, BISHOP_RELEVANT_BITS[square], attack_mask);
let magic_idx = let magic_idx =
occupancy.wrapping_mul(BISHOP_MAGIC[square]) >> (64 - BISHOP_RELEVANT_BITS[square]); occupancy.wrapping_mul(BISHOP_MAGIC[square]) >> (64 - BISHOP_RELEVANT_BITS[square]);
square_attacks[magic_idx as usize] = square_attacks[magic_idx.0 as usize] =
bishop_attacks_on_the_fly(square_to_bitboard(square), occupancy); bishop_attacks_on_the_fly(Bitboard::from(square), occupancy);
} }
bishop_atks.push(square_attacks); bishop_atks.push(square_attacks);
} }
bishop_atks bishop_atks
} }
fn init_rook_attacks() -> Vec<Vec<Bitboard>> { fn init_rook_attacks() -> Vec<Vec<Bitboard>> {
let mut rook_masks: Vec<Bitboard> = vec![0; 64]; let mut rook_masks = [Bitboard(0); 64];
let mut rook_atks: Vec<Vec<Bitboard>> = Vec::with_capacity(64); let mut rook_atks: Vec<Vec<Bitboard>> = Vec::with_capacity(64);
for square in 0..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 attack_mask = rook_masks[square];
let occupancy_indices = square_to_bitboard(ROOK_RELEVANT_BITS[square]); let occupancy_indices = Bitboard::from(ROOK_RELEVANT_BITS[square]);
let mut square_attacks = vec![0; occupancy_indices as usize]; 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 as u64, ROOK_RELEVANT_BITS[square], attack_mask); let occupancy = set_occupancy(idx, ROOK_RELEVANT_BITS[square], attack_mask);
let magic_idx = let magic_idx =
occupancy.wrapping_mul(ROOK_MAGIC[square]) >> (64 - ROOK_RELEVANT_BITS[square]); occupancy.wrapping_mul(ROOK_MAGIC[square]) >> (64 - ROOK_RELEVANT_BITS[square]);
square_attacks[magic_idx as usize] = square_attacks[magic_idx.0 as usize] =
rook_attacks_on_the_fly(square_to_bitboard(square), occupancy); rook_attacks_on_the_fly(Bitboard::from(square), occupancy);
} }
rook_atks.push(square_attacks); rook_atks.push(square_attacks);
} }
rook_atks rook_atks
} }
use std::{array, sync::LazyLock};
pub fn init_attacks() { pub fn init_attacks() {
let _ = &*PAWN_ATTACKS; let _ = &*PAWN_ATTACKS;
let _ = &*KNIGHT_ATTACKS; let _ = &*KNIGHT_ATTACKS;
@@ -327,133 +326,118 @@ mod tests {
use crate::board::{board::Color, square::Square}; use crate::board::{board::Color, square::Square};
#[test] #[test]
fn test_pawn_attacks() -> Result<(), String> { fn test_pawn_attacks() {
assert_eq!( assert_eq!(
0x20000, Bitboard(0x20000),
pawn_attacks(square_to_bitboard(Square::A2), Color::White) pawn_attacks(Bitboard::from(Square::A2), Color::White)
); );
assert_eq!( assert_eq!(
0x50000, Bitboard(0x50000),
pawn_attacks(square_to_bitboard(Square::B2), Color::White) pawn_attacks(Bitboard::from(Square::B2), Color::White)
); );
assert_eq!( assert_eq!(
0x400000, Bitboard(0x400000),
pawn_attacks(square_to_bitboard(Square::H2), Color::White) pawn_attacks(Bitboard::from(Square::H2), Color::White)
); );
assert_eq!( assert_eq!(
0x20000000000, Bitboard(0x20000000000),
pawn_attacks(square_to_bitboard(Square::A7), Color::Black) pawn_attacks(Bitboard::from(Square::A7), Color::Black)
); );
assert_eq!( assert_eq!(
0x50000000000, Bitboard(0x50000000000),
pawn_attacks(square_to_bitboard(Square::B7), Color::Black) pawn_attacks(Bitboard::from(Square::B7), Color::Black)
); );
assert_eq!( assert_eq!(
0x400000000000, Bitboard(0x400000000000),
pawn_attacks(square_to_bitboard(Square::H7), Color::Black) pawn_attacks(Bitboard::from(Square::H7), Color::Black)
); );
Ok(())
} }
#[test] #[test]
fn test_knight_attacks() -> Result<(), String> { fn test_knight_attacks() {
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> {
assert_eq!( assert_eq!(
0x40221400142200, Bitboard(0x20400),
mask_bishop_attacks(square_to_bitboard(Square::D4)) knight_attacks(Bitboard::from(Square::A1))
); );
Ok(())
}
#[test]
fn test_bishop_attacks_on_the_fly() -> Result<(), String> {
assert_eq!( assert_eq!(
0x8040201400142241, Bitboard(0x50800),
bishop_attacks_on_the_fly( knight_attacks(Bitboard::from(Square::B1))
square_to_bitboard(Square::D4),
square_to_bitboard(Square::C5)
)
); );
Ok(())
}
#[test]
fn test_mask_rook_attacks() -> Result<(), String> {
assert_eq!( assert_eq!(
0x8080876080800, Bitboard(0xa1100),
mask_rook_attacks(square_to_bitboard(Square::D4)) knight_attacks(Bitboard::from(Square::C1))
); );
Ok(())
}
#[test]
fn test_rook_attacks_on_the_fly() -> Result<(), String> {
assert_eq!( assert_eq!(
0x8080808f4080808, Bitboard(0xa110011),
rook_attacks_on_the_fly( knight_attacks(Bitboard::from(Square::C2))
square_to_bitboard(Square::D4), );
square_to_bitboard(Square::C4) assert_eq!(
) Bitboard(0xa1100110a),
knight_attacks(Bitboard::from(Square::C3))
); );
Ok(())
} }
#[test] #[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(); init_bishop_attacks();
let bishop_d3_square = 0x80000_u64.trailing_zeros() as usize; let blockers = Bitboard(0x602000020);
let blockers = 0x602000020; let attacks = fetch_bishop_attacks(blockers, Square::D3);
let attacks = fetch_bishop_attacks(blockers, bishop_d3_square); assert_eq!(attacks, Bitboard(0x80402214001422));
assert_eq!(attacks, 0x80402214001422);
Ok(())
} }
#[test] #[test]
fn test_rook_attacks() -> Result<(), String> { fn test_rook_attacks() {
init_rook_attacks(); init_rook_attacks();
let rook_d3_square = 0x80000_u64.trailing_zeros() as usize; let blockers = Bitboard(0x800000000600800);
let blockers = 0x800000000600800; let attacks = fetch_rook_attacks(blockers, Square::D3);
let attacks = fetch_rook_attacks(blockers, rook_d3_square); assert_eq!(attacks, Bitboard(0x808080808370800));
assert_eq!(attacks, 0x808080808370800);
Ok(())
} }
#[test] #[test]
fn test_queen_attacks() -> Result<(), String> { fn test_queen_attacks() {
init_rook_attacks(); init_rook_attacks();
init_bishop_attacks(); init_bishop_attacks();
let blockers = Bitboard(0x800000602600820);
let queen_d3_square = 0x80000_u64.trailing_zeros() as usize; let attacks = fetch_queen_attacks(blockers, Square::D3);
let blockers = 0x800000602600820; assert_eq!(attacks, Bitboard(0x888482a1c371c22));
let queen_attacks = fetch_queen_attacks(blockers, queen_d3_square);
assert_eq!(queen_attacks, 0x888482a1c371c22);
Ok(())
} }
} }

View File

@@ -1,39 +1,41 @@
use crate::board::bitboard::Bitboard;
#[rustfmt::skip] #[rustfmt::skip]
pub const ROOK_MAGIC: [u64; 64] = [ pub const ROOK_MAGIC: [Bitboard; 64] = [
0x80008010284000, 0x540024820001000, 0x1300092000124100, 0x480240800500080, Bitboard(0x80008010284000), Bitboard(0x540024820001000), Bitboard(0x1300092000124100), Bitboard(0x480240800500080),
0x1001004020081080, 0x500040021000208, 0x6280010000800200, 0x8006a100104180, Bitboard(0x1001004020081080), Bitboard(0x500040021000208), Bitboard(0x6280010000800200), Bitboard(0x8006a100104180),
0x420800080204000, 0x2802000804008, 0x101805000600080, 0x1000801000080082, Bitboard(0x420800080204000), Bitboard(0x2802000804008), Bitboard(0x101805000600080), Bitboard(0x1000801000080082),
0x102808044000800, 0x280800200040080, 0x1003000100040200, 0xd201000092005100, Bitboard(0x102808044000800), Bitboard(0x280800200040080), Bitboard(0x1003000100040200), Bitboard(0xd201000092005100),
0x80004000200041, 0x8400810040002100, 0x210048020008010, 0x220042001008, Bitboard(0x80004000200041), Bitboard(0x8400810040002100), Bitboard(0x210048020008010), Bitboard(0x220042001008),
0x20850011000800, 0x80808004000200, 0x100c440008424110, 0x2020010885904, Bitboard(0x20850011000800), Bitboard(0x80808004000200), Bitboard(0x100c440008424110), Bitboard(0x2020010885904),
0x120400080002080, 0x40400100210888, 0x4860080040100041, 0x10100080080084, Bitboard(0x120400080002080), Bitboard(0x40400100210888), Bitboard(0x4860080040100041), Bitboard(0x10100080080084),
0x8008080080400, 0x124008080020004, 0x100010400021008, 0x820008200004401, Bitboard(0x8008080080400), Bitboard(0x124008080020004), Bitboard(0x100010400021008), Bitboard(0x820008200004401),
0x404000a2800080, 0x802000804008, 0x8040809002802004, 0x1000801000800800, Bitboard(0x404000a2800080), Bitboard(0x802000804008), Bitboard(0x8040809002802004), Bitboard(0x1000801000800800),
0x40080800800, 0x202800200800400, 0x100204000108, 0x48010c44020000b1, Bitboard(0x40080800800), Bitboard(0x202800200800400), Bitboard(0x100204000108), Bitboard(0x48010c44020000b1),
0x800040008024, 0x402010004000, 0x4209002000110040, 0x12002040aa0050, Bitboard(0x800040008024), Bitboard(0x402010004000), Bitboard(0x4209002000110040), Bitboard(0x12002040aa0050),
0x2002008260010, 0x80a001008020004, 0x701000a00050004, 0x241a040080420001, Bitboard(0x2002008260010), Bitboard(0x80a001008020004), Bitboard(0x701000a00050004), Bitboard(0x241a040080420001),
0x50043a0800100, 0x8021c002a0148080, 0x200010008080, 0x8080410008080, Bitboard(0x50043a0800100), Bitboard(0x8021c002a0148080), Bitboard(0x200010008080), Bitboard(0x8080410008080),
0x5180080004110100, 0x1164000402008080, 0x1002800100020080, 0x4100840200, Bitboard(0x5180080004110100), Bitboard(0x1164000402008080), Bitboard(0x1002800100020080), Bitboard(0x4100840200),
0x82829101412206, 0x2209004000801021, 0x84010802202, 0x1a208900100005, Bitboard(0x82829101412206), Bitboard(0x2209004000801021), Bitboard(0x84010802202), Bitboard(0x1a208900100005),
0x1001002880005, 0x9008204000841, 0x2c9508221014, 0x8010470400402092, Bitboard(0x1001002880005), Bitboard(0x9008204000841), Bitboard(0x2c9508221014), Bitboard(0x8010470400402092),
]; ];
#[rustfmt::skip] #[rustfmt::skip]
pub const BISHOP_MAGIC: [u64; 64] = [ pub const BISHOP_MAGIC: [Bitboard; 64] = [
0x10041000802d00, 0x3410820e4100b0, 0x1008080120200000, 0x4080a0020104000, Bitboard(0x10041000802d00), Bitboard(0x3410820e4100b0), Bitboard(0x1008080120200000), Bitboard(0x4080a0020104000),
0x14152000000050, 0x1010841202844, 0x80480210100208, 0x8042410050022000, Bitboard(0x14152000000050), Bitboard(0x1010841202844), Bitboard(0x80480210100208), Bitboard(0x8042410050022000),
0x2400204810a00, 0x1003020204010e, 0xa012044104010800, 0x804280a042108c2, Bitboard(0x2400204810a00), Bitboard(0x1003020204010e), Bitboard(0xa012044104010800), Bitboard(0x804280a042108c2),
0x11040010810, 0x2010228820298000, 0x21440100c210400c, 0x910002104108414, Bitboard(0x11040010810), Bitboard(0x2010228820298000), Bitboard(0x21440100c210400c), Bitboard(0x910002104108414),
0x249012002102220, 0x210000421082108, 0xa001001083040100, 0x880c007801441100, Bitboard(0x249012002102220), Bitboard(0x210000421082108), Bitboard(0xa001001083040100), Bitboard(0x880c007801441100),
0xe00082ac00a01800, 0x40020004a2032001, 0x82aa016048040580, 0x40205301291000, Bitboard(0xe00082ac00a01800), Bitboard(0x40020004a2032001), Bitboard(0x82aa016048040580), Bitboard(0x40205301291000),
0x1060110004100200, 0x8002035010040800, 0x8401001a080508, 0xc0044004050020, Bitboard(0x1060110004100200), Bitboard(0x8002035010040800), Bitboard(0x8401001a080508), Bitboard(0xc0044004050020),
0x409010080104008, 0x28029232004400, 0x4008888002121000, 0x8041220041004118, Bitboard(0x409010080104008), Bitboard(0x28029232004400), Bitboard(0x4008888002121000), Bitboard(0x8041220041004118),
0x8018241008404200, 0x108021820348e, 0x108c0104100040, 0x8320080800460a00, Bitboard(0x8018241008404200), Bitboard(0x108021820348e), Bitboard(0x108c0104100040), Bitboard(0x8320080800460a00),
0x10008200802200, 0x284208080980800, 0x42020402122080, 0x9702004040410400, Bitboard(0x10008200802200), Bitboard(0x284208080980800), Bitboard(0x42020402122080), Bitboard(0x9702004040410400),
0x1092070012122, 0x2214024619001121, 0x491420140208d000, 0x7460009414003802, Bitboard(0x1092070012122), Bitboard(0x2214024619001121), Bitboard(0x491420140208d000), Bitboard(0x7460009414003802),
0x4022084104000040, 0x1084248085020200, 0x8808102400602480, 0x120410a400428508, Bitboard(0x4022084104000040), Bitboard(0x1084248085020200), Bitboard(0x8808102400602480), Bitboard(0x120410a400428508),
0x4014808050804, 0x408406804101008, 0x41081044d500031, 0x421080284044020, Bitboard(0x4014808050804), Bitboard(0x408406804101008), Bitboard(0x41081044d500031), Bitboard(0x421080284044020),
0x4060188813041110, 0x40202041024080, 0xa0212002108100a4, 0x3020202020480, Bitboard(0x4060188813041110), Bitboard(0x40202041024080), Bitboard(0xa0212002108100a4), Bitboard(0x3020202020480),
0x1a802808062882, 0x80110101012100, 0xa40100200420890, 0x48025100208801, Bitboard(0x1a802808062882), Bitboard(0x80110101012100), Bitboard(0xa40100200420890), Bitboard(0x48025100208801),
0x4000000004208200, 0x804a10011602, 0x200a24c410041500, 0x8408080088061020, Bitboard(0x4000000004208200), Bitboard(0x804a10011602), Bitboard(0x200a24c410041500), Bitboard(0x8408080088061020),
]; ];

View File

@@ -1,6 +1,5 @@
use crate::board::bitboard::{ use crate::board::bitboard::{
have_common_bit, lsb, square_to_bitboard, NOT_FILE_A, NOT_FILE_H, RANK_1, RANK_2, RANK_4, Bitboard, NOT_FILE_A, NOT_FILE_H, RANK_1, RANK_2, RANK_4, RANK_5, RANK_7, RANK_8,
RANK_5, RANK_7, RANK_8,
}; };
use crate::board::board::{Board, Color}; use crate::board::board::{Board, Color};
use crate::board::state::Castle; use crate::board::state::Castle;
@@ -9,7 +8,6 @@ use crate::movegen::attack_generator::{
fetch_queen_attacks, fetch_rook_attacks, fetch_queen_attacks, fetch_rook_attacks,
}; };
use crate::movegen::r#move::{Move, MoveType}; use crate::movegen::r#move::{Move, MoveType};
use u64 as Bitboard;
pub fn pawn_pseudo_moves( pub fn pawn_pseudo_moves(
pawns: Bitboard, pawns: Bitboard,
@@ -63,11 +61,11 @@ fn white_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec<Move> {
let empty = !occupancies; let empty = !occupancies;
let mut single_push = (pawns << 8) & empty; let mut single_push = (pawns << 8) & empty;
while single_push != 0 { while !single_push.is_empty() {
let dst = lsb(single_push); let dst = single_push.lsb();
let src = dst - 8; 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); add_promotion_moves(&mut moves, src, dst, false);
} else { } else {
moves.push(Move::new(src, dst)); moves.push(Move::new(src, dst));
@@ -77,8 +75,9 @@ fn white_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec<Move> {
let single_push = (pawns << 8) & empty; let single_push = (pawns << 8) & empty;
let mut double_push = (single_push << 8) & empty & RANK_4; 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; let src = dst - 16;
moves.push(Move::new_with_type(src, dst, MoveType::DoublePush)); moves.push(Move::new_with_type(src, dst, MoveType::DoublePush));
double_push &= double_push - 1; double_push &= double_push - 1;
@@ -91,11 +90,11 @@ fn black_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec<Move> {
let empty = !occupancies; let empty = !occupancies;
let mut single_push = (pawns >> 8) & empty; let mut single_push = (pawns >> 8) & empty;
while single_push != 0 { while !single_push.is_empty() {
let dst = lsb(single_push); let dst = single_push.lsb();
let src = dst + 8; 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); add_promotion_moves(&mut moves, src, dst, false);
} else { } else {
moves.push(Move::new(src, dst)); moves.push(Move::new(src, dst));
@@ -105,8 +104,9 @@ fn black_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec<Move> {
let single_push = (pawns >> 8) & empty; let single_push = (pawns >> 8) & empty;
let mut double_push = (single_push >> 8) & empty & RANK_5; 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; let src = dst + 16;
moves.push(Move::new_with_type(src, dst, MoveType::DoublePush)); moves.push(Move::new_with_type(src, dst, MoveType::DoublePush));
double_push &= double_push - 1; 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_east = pawns & ((opponent_occupancies >> 9) & NOT_FILE_H);
let mut w_pawns_capture_west = pawns & ((opponent_occupancies >> 7) & NOT_FILE_A); let mut w_pawns_capture_west = pawns & ((opponent_occupancies >> 7) & NOT_FILE_A);
while w_pawns_capture_east != 0 { while !w_pawns_capture_east.is_empty() {
let src = lsb(w_pawns_capture_east); let src = w_pawns_capture_east.lsb();
let dst = src + 9; 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); add_promotion_moves(&mut moves, src, dst, true);
} else { } else {
moves.push(Move::new_with_type(src, dst, MoveType::Capture)); moves.push(Move::new_with_type(src, dst, MoveType::Capture));
} }
w_pawns_capture_east &= w_pawns_capture_east - 1; 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; 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); add_promotion_moves(&mut moves, src, dst, true);
} else { } else {
moves.push(Move::new_with_type(src, dst, MoveType::Capture)); 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 { if let Some(en_passant_sq) = en_passant_square {
let attacked_src = fetch_pawn_attacks(en_passant_sq, Color::Black); let attacked_src = fetch_pawn_attacks(en_passant_sq, Color::Black);
let mut result = attacked_src & pawns; let mut result = attacked_src & pawns;
while result != 0 {
while !result.is_empty() {
moves.push(Move::new_with_type( moves.push(Move::new_with_type(
lsb(result), result.lsb(),
en_passant_sq, en_passant_sq,
MoveType::EnPassant, 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_east = pawns & ((opponent_occupancies << 7) & NOT_FILE_H);
let mut b_pawns_capture_west = pawns & ((opponent_occupancies << 9) & NOT_FILE_A); let mut b_pawns_capture_west = pawns & ((opponent_occupancies << 9) & NOT_FILE_A);
while b_pawns_capture_east != 0 { while !b_pawns_capture_east.is_empty() {
let src = lsb(b_pawns_capture_east); let src = b_pawns_capture_east.lsb();
let dst = src - 7; 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); add_promotion_moves(&mut moves, src, dst, true);
} else { } else {
moves.push(Move::new_with_type(src, dst, MoveType::Capture)); 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; b_pawns_capture_east &= b_pawns_capture_east - 1;
} }
while b_pawns_capture_west != 0 { while !b_pawns_capture_west.is_empty() {
let src = lsb(b_pawns_capture_west); let src = b_pawns_capture_west.lsb();
let dst = src - 9; 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); add_promotion_moves(&mut moves, src, dst, true);
} else { } else {
moves.push(Move::new_with_type(src, dst, MoveType::Capture)); 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 { if let Some(en_passant_square) = en_passant_square {
let attacked_src = fetch_pawn_attacks(en_passant_square, Color::White); let attacked_src = fetch_pawn_attacks(en_passant_square, Color::White);
let mut result = attacked_src & pawns; let mut result = attacked_src & pawns;
while result != 0 {
while !result.is_empty() {
moves.push(Move::new_with_type( moves.push(Move::new_with_type(
lsb(result), result.lsb(),
en_passant_square, en_passant_square,
MoveType::EnPassant, MoveType::EnPassant,
)); ));
@@ -216,14 +219,14 @@ pub fn knight_pseudo_moves(
let mut moves = vec![]; let mut moves = vec![];
let opponent_occupancies = all_occupancies ^ own_occupancies; let opponent_occupancies = all_occupancies ^ own_occupancies;
while knights != 0 { while !knights.is_empty() {
let knight_square = lsb(knights); let knight_square = knights.lsb();
let src = knight_square; let src = knight_square;
let mut attacks = fetch_knight_attacks(knight_square) & !own_occupancies; let mut attacks = fetch_knight_attacks(knight_square) & !own_occupancies;
while attacks != 0 { while !attacks.is_empty() {
let attack_sq = lsb(attacks); let attack_sq = attacks.lsb();
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { if Bitboard::from(attack_sq).intersects(opponent_occupancies) {
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else { } else {
moves.push(Move::new(src, attack_sq)); moves.push(Move::new(src, attack_sq));
@@ -243,14 +246,14 @@ pub fn bishop_pseudo_moves(
let mut moves = vec![]; let mut moves = vec![];
let opponent_occupancies = all_occupancies ^ own_occupancies; let opponent_occupancies = all_occupancies ^ own_occupancies;
while bishops != 0 { while !bishops.is_empty() {
let bishop_square = lsb(bishops); let bishop_square = bishops.lsb();
let src = bishop_square; let src = bishop_square;
let mut attacks = fetch_bishop_attacks(all_occupancies, bishop_square) & !own_occupancies; let mut attacks = fetch_bishop_attacks(all_occupancies, bishop_square) & !own_occupancies;
while attacks != 0 { while !attacks.is_empty() {
let attack_sq = lsb(attacks); let attack_sq = attacks.lsb();
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { if Bitboard::from(attack_sq).intersects(opponent_occupancies) {
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else { } else {
moves.push(Move::new(src, attack_sq)); moves.push(Move::new(src, attack_sq));
@@ -270,14 +273,14 @@ pub fn rook_pseudo_moves(
let mut moves = vec![]; let mut moves = vec![];
let opponent_occupancies = all_occupancies ^ own_occupancies; let opponent_occupancies = all_occupancies ^ own_occupancies;
while rooks != 0 { while !rooks.is_empty() {
let rook_square = lsb(rooks); let rook_square = rooks.lsb();
let src = rook_square; let src = rook_square;
let mut attacks = fetch_rook_attacks(all_occupancies, rook_square) & !own_occupancies; let mut attacks = fetch_rook_attacks(all_occupancies, rook_square) & !own_occupancies;
while attacks != 0 { while !attacks.is_empty() {
let attack_sq = lsb(attacks); let attack_sq = attacks.lsb();
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { if Bitboard::from(attack_sq).intersects(opponent_occupancies) {
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else { } else {
moves.push(Move::new(src, attack_sq)); moves.push(Move::new(src, attack_sq));
@@ -297,14 +300,14 @@ pub fn queen_pseudo_moves(
let mut moves = vec![]; let mut moves = vec![];
let opponent_occupancies = all_occupancies ^ own_occupancies; let opponent_occupancies = all_occupancies ^ own_occupancies;
while queens != 0 { while !queens.is_empty() {
let queen_square = lsb(queens); let queen_square = queens.lsb();
let src = queen_square; let src = queen_square;
let mut attacks = fetch_queen_attacks(all_occupancies, queen_square) & !own_occupancies; let mut attacks = fetch_queen_attacks(all_occupancies, queen_square) & !own_occupancies;
while attacks != 0 { while !attacks.is_empty() {
let attack_sq = lsb(attacks); let attack_sq = attacks.lsb();
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { if Bitboard::from(attack_sq).intersects(opponent_occupancies) {
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else { } else {
moves.push(Move::new(src, attack_sq)); moves.push(Move::new(src, attack_sq));
@@ -324,14 +327,14 @@ pub fn king_pseudo_moves(
color: Color, color: Color,
) -> Vec<Move> { ) -> Vec<Move> {
let mut moves = vec![]; let mut moves = vec![];
let king_square = lsb(king); let king_square = king.lsb();
let src = king_square; let src = king_square;
let mut attacks = fetch_king_attacks(king_square) & !own_occupancies; let mut attacks = fetch_king_attacks(king_square) & !own_occupancies;
let opponent_occupancies = all_occupancies ^ own_occupancies; let opponent_occupancies = all_occupancies ^ own_occupancies;
while attacks != 0 { while !attacks.is_empty() {
let attack_sq = lsb(attacks); let attack_sq = attacks.lsb();
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) { if Bitboard::from(attack_sq).intersects(opponent_occupancies) {
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture)); moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else { } else {
moves.push(Move::new(src, attack_sq)); 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<Move> { fn king_castling_moves(board: &Board, color: Color, all_occupancies: Bitboard) -> Vec<Move> {
let mut moves = vec![]; let mut moves = vec![];
let (king_src, king_dst_short, king_dst_long, path_short, path_long) = match color { 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::White => (4, 6, 2, Bitboard(0x60_u64), Bitboard(0xe_u64)),
Color::Black => (60, 62, 58, 0x6000000000000000_u64, 0xe00000000000000_u64), Color::Black => (
60,
62,
58,
Bitboard(0x6000000000000000_u64),
Bitboard(0xe00000000000000_u64),
),
}; };
let mut add_move_if_empty_path = |path_mask, king_dst| { 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 { if king_dst > king_src {
moves.push(Move::new_with_type( moves.push(Move::new_with_type(
king_src, king_src,