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;
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<Square> 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<usize> 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<u64> 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<usize> for Bitboard {
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 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<T> Index<PieceType> 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(())

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;
};
}
@@ -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;

View File

@@ -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))
});
}

View File

@@ -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<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 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;
}

View File

@@ -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<Vec<Vec<Bitboard>>> = LazyLock::new(init_bishop_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;
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<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);
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<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);
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));
}
}

View File

@@ -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),
];

View File

@@ -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<Move> {
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<Move> {
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<Move> {
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<Move> {
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<Move> {
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<Move> {
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,