Refactor duplicate code and make it more readable

This commit is contained in:
2024-07-06 21:51:20 +03:00
parent 57af0c3f27
commit 13c3eb2eb1
9 changed files with 358 additions and 260 deletions

View File

@@ -1,13 +1,15 @@
use crate::board::Color; use crate::{
bitboard::{
intersect, lsb, square_to_bitboard, square_to_bitboard_wrapping, EMPTY, NOT_FILE_A,
NOT_FILE_AB, NOT_FILE_GH, NOT_FILE_H,
},
board::Color,
square::coords_to_square,
};
use u64 as Bitboard; use u64 as Bitboard;
const NOT_A_FILE: Bitboard = 0xfefefefefefefefe;
const NOT_AB_FILE: Bitboard = 0xfcfcfcfcfcfcfcfc;
const NOT_H_FILE: Bitboard = 0x7f7f7f7f7f7f7f7f;
const NOT_GH_FILE: Bitboard = 0x3f3f3f3f3f3f3f3f;
#[rustfmt::skip] #[rustfmt::skip]
pub const ROOK_RELEVANT_BITS: [u8; 64] = [ pub 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,
@@ -19,7 +21,7 @@ pub const ROOK_RELEVANT_BITS: [u8; 64] = [
]; ];
#[rustfmt::skip] #[rustfmt::skip]
pub const BISHOP_RELEVANT_BITS: [u8; 64] = [ pub 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,
@@ -37,16 +39,16 @@ static mut BISHOP_ATTACKS: [[Bitboard; 512]; 64] = [[0; 512]; 64];
static mut ROOK_ATTACKS: [[Bitboard; 4096]; 64] = [[0; 4096]; 64]; static mut ROOK_ATTACKS: [[Bitboard; 4096]; 64] = [[0; 4096]; 64];
const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard { const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard {
let mut attacks = 0_u64; let mut attacks = EMPTY;
match color { match color {
Color::Black => { Color::Black => {
attacks |= (bitboard & NOT_H_FILE) >> (7); attacks |= (bitboard & NOT_FILE_H) >> 7;
attacks |= (bitboard & NOT_A_FILE) >> (9); attacks |= (bitboard & NOT_FILE_A) >> 9;
} }
Color::White => { Color::White => {
attacks |= (bitboard & NOT_A_FILE) << (7); attacks |= (bitboard & NOT_FILE_A) << 7;
attacks |= (bitboard & NOT_H_FILE) << (9); attacks |= (bitboard & NOT_FILE_H) << 9;
} }
}; };
@@ -54,119 +56,101 @@ const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard {
} }
const fn knight_attacks(bitboard: Bitboard) -> Bitboard { const fn knight_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64; let mut attacks = EMPTY;
attacks |= (bitboard & NOT_AB_FILE) << (6); attacks |= (bitboard & NOT_FILE_AB) << 6;
attacks |= (bitboard & NOT_GH_FILE) << (10); attacks |= (bitboard & NOT_FILE_GH) << 10;
attacks |= (bitboard & NOT_A_FILE) << (15); attacks |= (bitboard & NOT_FILE_A) << 15;
attacks |= (bitboard & NOT_H_FILE) << (17); attacks |= (bitboard & NOT_FILE_H) << 17;
attacks |= (bitboard & NOT_GH_FILE) >> (6); attacks |= (bitboard & NOT_FILE_GH) >> 6;
attacks |= (bitboard & NOT_AB_FILE) >> (10); attacks |= (bitboard & NOT_FILE_AB) >> 10;
attacks |= (bitboard & NOT_H_FILE) >> (15); attacks |= (bitboard & NOT_FILE_H) >> 15;
attacks |= (bitboard & NOT_A_FILE) >> (17); attacks |= (bitboard & NOT_FILE_A) >> 17;
attacks attacks
} }
const fn king_attacks(bitboard: Bitboard) -> Bitboard { const fn king_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64; let mut attacks = EMPTY;
attacks |= (bitboard & NOT_H_FILE) << (1); attacks |= (bitboard & NOT_FILE_H) << 1;
attacks |= (bitboard & NOT_A_FILE) << (7); attacks |= (bitboard & NOT_FILE_A) << 7;
attacks |= bitboard << (8); attacks |= bitboard << 8;
attacks |= (bitboard & NOT_H_FILE) << (9); attacks |= (bitboard & NOT_FILE_H) << 9;
attacks |= (bitboard & NOT_A_FILE) >> (1); attacks |= (bitboard & NOT_FILE_A) >> 1;
attacks |= (bitboard & NOT_H_FILE) >> (7); attacks |= (bitboard & NOT_FILE_H) >> 7;
attacks |= bitboard >> (8); attacks |= bitboard >> 8;
attacks |= (bitboard & NOT_A_FILE) >> (9); attacks |= (bitboard & NOT_FILE_A) >> 9;
attacks attacks
} }
pub fn mask_bishop_attacks(bitboard: Bitboard) -> Bitboard { pub fn mask_bishop_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64; let mut attacks = EMPTY;
let target_rank = if bitboard == 0 { let (target_rank, target_file) = match bitboard {
0 0 => (0, 0),
} else { _ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
bitboard.trailing_zeros() / 8
};
let target_file = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() % 8
}; };
let (mut rank, mut file) = (target_rank + 1, target_file + 1); let (mut rank, mut file) = (target_rank + 1, target_file + 1);
for (rank, file) in (rank..=6).zip(file..=6) { for (rank, file) in (rank..=6).zip(file..=6) {
attacks |= 1_u64 << (rank * 8 + file); attacks |= square_to_bitboard(coords_to_square(rank, file));
} }
(rank, file) = (target_rank.saturating_sub(1), target_file + 1); (rank, file) = (target_rank.saturating_sub(1), target_file + 1);
for (rank, file) in (1..=rank).rev().zip(file..=6) { for (rank, file) in (1..=rank).rev().zip(file..=6) {
attacks |= 1_u64 << (rank * 8 + file); attacks |= square_to_bitboard(coords_to_square(rank, file));
} }
(rank, file) = (target_rank + 1, target_file.saturating_sub(1)); (rank, file) = (target_rank + 1, target_file.saturating_sub(1));
for (rank, file) in (rank..=6).zip((1..=file).rev()) { for (rank, file) in (rank..=6).zip((1..=file).rev()) {
attacks |= 1_u64 << (rank * 8 + file); attacks |= square_to_bitboard(coords_to_square(rank, file));
} }
(rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1)); (rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1));
for (rank, file) in (1..=rank).rev().zip((1..=file).rev()) { for (rank, file) in (1..=rank).rev().zip((1..=file).rev()) {
attacks |= 1_u64 << (rank * 8 + file); attacks |= square_to_bitboard(coords_to_square(rank, file));
} }
attacks attacks
} }
pub fn mask_rook_attacks(bitboard: Bitboard) -> Bitboard { pub fn mask_rook_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64; let mut attacks = EMPTY;
let target_rank = if bitboard == 0 { let (target_rank, target_file) = match bitboard {
0 0 => (0, 0),
} else { _ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
bitboard.trailing_zeros() / 8
};
let target_file = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() % 8
}; };
let (mut rank, mut file) = (target_rank + 1, target_file + 1); let (mut rank, mut file) = (target_rank + 1, target_file + 1);
for rank in rank..=6 { for rank in rank..=6 {
attacks |= 1_u64 << (rank * 8 + target_file); attacks |= square_to_bitboard(coords_to_square(rank, target_file));
} }
for file in file..=6 { for file in file..=6 {
attacks |= 1_u64 << (target_rank * 8 + file); attacks |= square_to_bitboard(coords_to_square(target_rank, file));
} }
(rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1)); (rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1));
for rank in (1..=rank).rev() { for rank in (1..=rank).rev() {
attacks |= 1_u64 << (rank * 8 + target_file); attacks |= square_to_bitboard(coords_to_square(rank, target_file));
} }
for file in (1..=file).rev() { for file in (1..=file).rev() {
attacks |= 1_u64 << (target_rank * 8 + file); attacks |= square_to_bitboard(coords_to_square(target_rank, file));
} }
attacks attacks
} }
pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard { pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
let mut attacks = 0_u64; let mut attacks = EMPTY;
let target_rank = if bitboard == 0 { let (target_rank, target_file) = match bitboard {
0 0 => (0, 0),
} else { _ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
bitboard.trailing_zeros() / 8
};
let target_file = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() % 8
}; };
let (mut rank, mut file) = (target_rank + 1, target_file + 1); let (mut rank, mut file) = (target_rank + 1, target_file + 1);
for (rank, file) in (rank..=7).zip(file..=7) { for (rank, file) in (rank..=7).zip(file..=7) {
let attack_square = 1_u64 << (rank * 8 + file); let attack_square = square_to_bitboard(coords_to_square(rank, file));
attacks |= attack_square; attacks |= attack_square;
if attack_square & blocker != 0 { if attack_square & blocker != 0 {
break; break;
@@ -175,7 +159,7 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
file = target_file + 1; file = target_file + 1;
for (rank, file) in (0..target_rank).rev().zip(file..=7) { for (rank, file) in (0..target_rank).rev().zip(file..=7) {
let attack_square = 1_u64 << (rank * 8 + file); let attack_square = square_to_bitboard(coords_to_square(rank, file));
attacks |= attack_square; attacks |= attack_square;
if attack_square & blocker != 0 { if attack_square & blocker != 0 {
break; break;
@@ -184,7 +168,7 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
rank = target_rank + 1; rank = target_rank + 1;
for (rank, file) in (rank..=7).zip((0..target_file).rev()) { for (rank, file) in (rank..=7).zip((0..target_file).rev()) {
let attack_square = 1_u64 << (rank * 8 + file); let attack_square = square_to_bitboard(coords_to_square(rank, file));
attacks |= attack_square; attacks |= attack_square;
if attack_square & blocker != 0 { if attack_square & blocker != 0 {
break; break;
@@ -192,7 +176,7 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
} }
for (rank, file) in (0..target_rank).rev().zip((0..target_file).rev()) { for (rank, file) in (0..target_rank).rev().zip((0..target_file).rev()) {
let attack_square = 1_u64 << (rank * 8 + file); let attack_square = square_to_bitboard(coords_to_square(rank, file));
attacks |= attack_square; attacks |= attack_square;
if attack_square & blocker != 0 { if attack_square & blocker != 0 {
break; break;
@@ -203,28 +187,22 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
} }
pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard { pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
let mut attacks = 0_u64; let mut attacks = EMPTY;
let target_rank = if bitboard == 0 { let (target_rank, target_file) = match bitboard {
0 0 => (0, 0),
} else { _ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
bitboard.trailing_zeros() / 8
};
let target_file = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() % 8
}; };
let (mut rank, mut file) = (target_rank + 1, target_file + 1); let (mut rank, mut file) = (target_rank + 1, target_file + 1);
for rank in rank..=7 { for rank in rank..=7 {
let attack_square = 1_u64 << (rank * 8 + target_file); let attack_square = square_to_bitboard(coords_to_square(rank, target_file));
attacks |= attack_square; attacks |= attack_square;
if attack_square & blocker != 0 { if attack_square & blocker != 0 {
break; break;
} }
} }
for file in file..=7 { for file in file..=7 {
let attack_square = 1_u64 << (target_rank * 8 + file); let attack_square = square_to_bitboard(coords_to_square(target_rank, file));
attacks |= attack_square; attacks |= attack_square;
if attack_square & blocker != 0 { if attack_square & blocker != 0 {
break; break;
@@ -233,14 +211,14 @@ pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboar
(rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1)); (rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1));
for rank in (0..=rank).rev() { for rank in (0..=rank).rev() {
let attack_square = 1_u64 << (rank * 8 + target_file); let attack_square = square_to_bitboard(coords_to_square(rank, target_file));
attacks |= attack_square; attacks |= attack_square;
if attack_square & blocker != 0 { if attack_square & blocker != 0 {
break; break;
} }
} }
for file in (0..=file).rev() { for file in (0..=file).rev() {
let attack_square = 1_u64 << (target_rank * 8 + file); let attack_square = square_to_bitboard(coords_to_square(target_rank, file));
attacks |= attack_square; attacks |= attack_square;
if attack_square & blocker != 0 { if attack_square & blocker != 0 {
break; break;
@@ -250,96 +228,97 @@ pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboar
attacks attacks
} }
pub fn set_occupancy(index: u64, bits_in_mask: u8, mut attack_mask: Bitboard) -> Bitboard { pub fn set_occupancy(index: u64, relevant_bits: usize, mut attack_mask: Bitboard) -> Bitboard {
let mut occupancy = 0_u64; let mut occupancy = EMPTY;
for count in 0..bits_in_mask { for bit in 0..relevant_bits {
let square = attack_mask.trailing_zeros() as u8; let square = lsb(attack_mask);
attack_mask &= !(1_u64 << square); attack_mask &= !square_to_bitboard(square);
if index & (1_u64 << count) != 0 { if intersect(index, square_to_bitboard(bit)) {
occupancy |= 1_u64 << square; occupancy |= square_to_bitboard(square);
} }
} }
occupancy occupancy
} }
pub fn get_pawn_attacks(sq: usize, color: Color) -> Bitboard { pub fn get_pawn_attacks(square: usize, color: Color) -> Bitboard {
unsafe { unsafe {
match color { match color {
Color::White => PAWN_ATTACKS[sq][0], Color::White => PAWN_ATTACKS[square][0],
Color::Black => PAWN_ATTACKS[sq][1], Color::Black => PAWN_ATTACKS[square][1],
} }
} }
} }
pub fn get_knight_attacks(sq: usize) -> Bitboard { pub fn get_knight_attacks(square: usize) -> Bitboard {
unsafe { KNIGHT_ATTACKS[sq] } unsafe { KNIGHT_ATTACKS[square] }
} }
pub fn get_king_attacks(sq: usize) -> Bitboard { pub fn get_king_attacks(square: usize) -> Bitboard {
unsafe { KING_ATTACKS[sq] } unsafe { KING_ATTACKS[square] }
} }
use crate::magic::{BISHOP_MAGIC, ROOK_MAGIC}; use crate::magic::{BISHOP_MAGIC, ROOK_MAGIC};
pub fn get_bishop_attacks(mut occupancy: Bitboard, sq: usize) -> Bitboard { pub fn get_bishop_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
unsafe { unsafe {
occupancy &= mask_bishop_attacks(1_u64.wrapping_shl(sq as u32)); occupancy &= mask_bishop_attacks(square_to_bitboard_wrapping(square));
occupancy = occupancy.wrapping_mul(BISHOP_MAGIC[sq]); occupancy = occupancy.wrapping_mul(BISHOP_MAGIC[square]);
occupancy >>= 64 - BISHOP_RELEVANT_BITS[sq]; occupancy >>= 64 - BISHOP_RELEVANT_BITS[square];
BISHOP_ATTACKS[sq][occupancy as usize] BISHOP_ATTACKS[square][occupancy as usize]
} }
} }
pub fn get_rook_attacks(mut occupancy: Bitboard, sq: usize) -> Bitboard { pub fn get_rook_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
unsafe { unsafe {
occupancy &= mask_rook_attacks(1_u64.wrapping_shl(sq as u32)); occupancy &= mask_rook_attacks(square_to_bitboard_wrapping(square));
occupancy = occupancy.wrapping_mul(ROOK_MAGIC[sq]); occupancy = occupancy.wrapping_mul(ROOK_MAGIC[square]);
occupancy >>= 64 - ROOK_RELEVANT_BITS[sq]; occupancy >>= 64 - ROOK_RELEVANT_BITS[square];
ROOK_ATTACKS[sq][occupancy as usize] ROOK_ATTACKS[square][occupancy as usize]
} }
} }
pub fn get_queen_attacks(occupancy: Bitboard, sq: usize) -> Bitboard { pub fn get_queen_attacks(occupancy: Bitboard, square: usize) -> Bitboard {
get_rook_attacks(occupancy, sq) | get_bishop_attacks(occupancy, sq) get_rook_attacks(occupancy, square) | get_bishop_attacks(occupancy, square)
} }
pub fn init_pawn_attacks() { pub fn init_pawn_attacks() {
(0..64).for_each(|sq| unsafe { (0..64).for_each(|square| unsafe {
PAWN_ATTACKS[sq][0] = pawn_attacks(1_u64 << (sq), Color::White); PAWN_ATTACKS[square][0] = pawn_attacks(square_to_bitboard(square), Color::White);
PAWN_ATTACKS[sq][1] = pawn_attacks(1_u64 << (sq), Color::Black); PAWN_ATTACKS[square][1] = pawn_attacks(square_to_bitboard(square), Color::Black);
}); });
} }
pub fn init_knight_attacks() { pub fn init_knight_attacks() {
(0..64).for_each(|sq| unsafe { (0..64).for_each(|square| unsafe {
KNIGHT_ATTACKS[sq] = knight_attacks(1_u64 << (sq)); KNIGHT_ATTACKS[square] = knight_attacks(square_to_bitboard(square));
}); });
} }
pub fn init_king_attacks() { pub fn init_king_attacks() {
(0..64).for_each(|sq| unsafe { (0..64).for_each(|square| unsafe {
KING_ATTACKS[sq] = king_attacks(1_u64 << (sq)); KING_ATTACKS[square] = king_attacks(square_to_bitboard(square));
}); });
} }
pub fn init_bishop_attacks() { pub fn init_bishop_attacks() {
let mut bishop_masks = [0; 64]; let mut bishop_masks = [0; 64];
for sq in 0..64 { for square in 0..64 {
bishop_masks[sq] = mask_bishop_attacks(1_u64 << (sq)); bishop_masks[square] = mask_bishop_attacks(square_to_bitboard(square));
let attack_mask = bishop_masks[sq]; let attack_mask = bishop_masks[square];
let occupancy_indices = 1_u64 << BISHOP_RELEVANT_BITS[sq]; let occupancy_indices = square_to_bitboard(BISHOP_RELEVANT_BITS[square]);
for idx in 0..occupancy_indices { for index in 0..occupancy_indices {
let occupancy = set_occupancy(idx, BISHOP_RELEVANT_BITS[sq], attack_mask); let occupancy = set_occupancy(index, BISHOP_RELEVANT_BITS[square], attack_mask);
unsafe { unsafe {
let magic_index = let magic_index = (occupancy.wrapping_mul(BISHOP_MAGIC[square])
occupancy.wrapping_mul(BISHOP_MAGIC[sq]) >> (64 - BISHOP_RELEVANT_BITS[sq]); >> (64 - BISHOP_RELEVANT_BITS[square]))
as usize;
BISHOP_ATTACKS[sq][magic_index as usize] = BISHOP_ATTACKS[square][magic_index] =
bishop_attacks_on_the_fly(1_u64 << (sq), occupancy); bishop_attacks_on_the_fly(square_to_bitboard(square), occupancy);
} }
} }
} }
@@ -348,20 +327,20 @@ pub fn init_bishop_attacks() {
pub fn init_rook_attacks() { pub fn init_rook_attacks() {
let mut rook_masks = [0; 64]; let mut rook_masks = [0; 64];
for sq in 0..64 { for square in 0..64 {
rook_masks[sq] = mask_rook_attacks(1_u64 << (sq)); rook_masks[square] = mask_rook_attacks(square_to_bitboard(square));
let attack_mask = rook_masks[sq]; let attack_mask = rook_masks[square];
let relevant_bits_count = attack_mask.count_ones() as u8; let occupancy_indices = square_to_bitboard(ROOK_RELEVANT_BITS[square]);
let occupancy_indices = 1_u64 << (relevant_bits_count);
for idx in 0..occupancy_indices { for idx in 0..occupancy_indices {
let occupancy = set_occupancy(idx, relevant_bits_count, attack_mask); let occupancy = set_occupancy(idx, ROOK_RELEVANT_BITS[square], attack_mask);
unsafe { unsafe {
let magic_index = let magic_index = (occupancy.wrapping_mul(ROOK_MAGIC[square])
occupancy.wrapping_mul(ROOK_MAGIC[sq]) >> (64 - ROOK_RELEVANT_BITS[sq]); >> (64 - ROOK_RELEVANT_BITS[square]))
as usize;
ROOK_ATTACKS[sq][magic_index as usize] = ROOK_ATTACKS[square][magic_index] =
rook_attacks_on_the_fly(1_u64 << (sq), occupancy); rook_attacks_on_the_fly(square_to_bitboard(square), occupancy);
} }
} }
} }

29
src/bitboard.rs Normal file
View File

@@ -0,0 +1,29 @@
use u64 as Bitboard;
pub const EMPTY: Bitboard = 0x0;
pub const FULL: Bitboard = 0xffffffffffffffff;
pub const NOT_FILE_A: Bitboard = 0xfefefefefefefefe;
pub const NOT_FILE_AB: Bitboard = 0xfcfcfcfcfcfcfcfc;
pub const NOT_FILE_H: Bitboard = 0x7f7f7f7f7f7f7f7f;
pub const NOT_FILE_GH: Bitboard = 0x3f3f3f3f3f3f3f3f;
pub const RANK_8: Bitboard = 0x0ff00000000000000;
pub const fn square_to_bitboard(square: usize) -> Bitboard {
1_u64 << square
}
pub const fn square_to_bitboard_wrapping(square: usize) -> Bitboard {
1_u64.wrapping_shl(square as u32)
}
pub const fn lsb(bitboard: Bitboard) -> usize {
bitboard.trailing_zeros() as usize
}
pub const fn bit_count(bitboard: Bitboard) -> usize {
bitboard.count_ones() as usize
}
pub const fn intersect(bitboard_a: Bitboard, bitboard_b: Bitboard) -> bool {
bitboard_a & bitboard_b != 0
}

View File

@@ -180,21 +180,21 @@ impl Board {
match &mv.move_type { match &mv.move_type {
MoveType::Quiet => { MoveType::Quiet => {
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces); Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
self.state.set_en_passant_target_square(None); self.state.set_en_passant_target_square(None);
} }
MoveType::Capture => { MoveType::Capture => {
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces); Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
Board::remove_piece(mv.target as u8, opponent_pieces); Self::remove_piece(mv.target as u8, opponent_pieces);
self.state.set_en_passant_target_square(None); self.state.set_en_passant_target_square(None);
} }
MoveType::EnPassant => { MoveType::EnPassant => {
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces); Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
Board::remove_pawn_enpassant(mv.target as u8, opponent_pieces, color); Self::remove_pawn_enpassant(mv.target as u8, opponent_pieces, color);
self.state.set_en_passant_target_square(None); self.state.set_en_passant_target_square(None);
} }
MoveType::DoublePush => { MoveType::DoublePush => {
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces); Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
let en_passant = match color { let en_passant = match color {
Color::White => Some((mv.source + 8) as u8), Color::White => Some((mv.source + 8) as u8),
Color::Black => Some((mv.source - 8) as u8), Color::Black => Some((mv.source - 8) as u8),
@@ -202,19 +202,19 @@ impl Board {
self.state.set_en_passant_target_square(en_passant); self.state.set_en_passant_target_square(en_passant);
} }
MoveType::Promotion(promote) => { MoveType::Promotion(promote) => {
Board::remove_piece(mv.source as u8, own_pieces); Self::remove_piece(mv.source as u8, own_pieces);
Board::promote_piece(mv.target as u8, own_pieces, *promote); Self::promote_piece(mv.target as u8, own_pieces, *promote);
self.state.set_en_passant_target_square(None); self.state.set_en_passant_target_square(None);
} }
MoveType::PromotionCapture(promote) => { MoveType::PromotionCapture(promote) => {
Board::remove_piece(mv.source as u8, own_pieces); Self::remove_piece(mv.source as u8, own_pieces);
Board::remove_piece(mv.target as u8, opponent_pieces); Self::remove_piece(mv.target as u8, opponent_pieces);
Board::promote_piece(mv.target as u8, own_pieces, *promote); Self::promote_piece(mv.target as u8, own_pieces, *promote);
self.state.set_en_passant_target_square(None); self.state.set_en_passant_target_square(None);
} }
MoveType::Castle => { MoveType::Castle => {
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces); Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
Board::move_rook_castle(mv.target as u8, own_pieces); Self::move_rook_castle(mv.target as u8, own_pieces);
self.state.set_en_passant_target_square(None); self.state.set_en_passant_target_square(None);
self.state.set_castling_ability(color, Castle::None) self.state.set_castling_ability(color, Castle::None)
} }
@@ -238,7 +238,7 @@ impl Board {
_ => return, _ => return,
}; };
Board::move_piece(rook_source, rook_target, pieces); Self::move_piece(rook_source, rook_target, pieces);
} }
fn promote_piece(square: u8, pieces: &mut [Piece; 6], promote: Promote) { fn promote_piece(square: u8, pieces: &mut [Piece; 6], promote: Promote) {
@@ -320,10 +320,10 @@ pub enum Color {
} }
impl Color { impl Color {
pub const fn opponent_color(color: Color) -> Color { pub const fn opponent_color(color: Self) -> Self {
match color { match color {
Color::White => Color::Black, Self::White => Self::Black,
Color::Black => Color::White, Self::Black => Self::White,
} }
} }
} }

View File

@@ -118,7 +118,7 @@ impl State {
} }
} }
pub fn next_turn(&self) -> Color { pub const fn next_turn(&self) -> Color {
self.side_to_move self.side_to_move
} }

View File

@@ -2,6 +2,8 @@ use crate::attack::{
bishop_attacks_on_the_fly, mask_bishop_attacks, mask_rook_attacks, rook_attacks_on_the_fly, bishop_attacks_on_the_fly, mask_bishop_attacks, mask_rook_attacks, rook_attacks_on_the_fly,
set_occupancy, BISHOP_RELEVANT_BITS, ROOK_RELEVANT_BITS, set_occupancy, BISHOP_RELEVANT_BITS, ROOK_RELEVANT_BITS,
}; };
use crate::bitboard::{bit_count, square_to_bitboard, EMPTY, RANK_8};
use crate::square::Square;
use rand::rngs::SmallRng; use rand::rngs::SmallRng;
use rand::{RngCore, SeedableRng}; use rand::{RngCore, SeedableRng};
use u64 as Bitboard; use u64 as Bitboard;
@@ -50,10 +52,10 @@ pub const BISHOP_MAGIC: [u64; 64] = [
]; ];
pub fn random_uint64(state: &mut SmallRng) -> u64 { pub fn random_uint64(state: &mut SmallRng) -> u64 {
let n1 = state.next_u32() as u64 & 0xFFFF; let n1 = state.next_u32() as u64 & 0xffff;
let n2 = state.next_u32() as u64 & 0xFFFF; let n2 = state.next_u32() as u64 & 0xffff;
let n3 = state.next_u32() as u64 & 0xFFFF; let n3 = state.next_u32() as u64 & 0xffff;
let n4 = state.next_u32() as u64 & 0xFFFF; let n4 = state.next_u32() as u64 & 0xffff;
n1 | (n2 << (16)) | (n3 << (32)) | (n4 << (48)) n1 | (n2 << (16)) | (n3 << (32)) | (n4 << (48))
} }
@@ -62,120 +64,119 @@ pub fn generate_magic_number_candidate(state: &mut SmallRng) -> u64 {
random_uint64(state) & random_uint64(state) & random_uint64(state) random_uint64(state) & random_uint64(state) & random_uint64(state)
} }
pub fn find_rook_magic_numbers(relevant_bits: u8, square: Bitboard, state: &mut SmallRng) -> u64 {
let mut occupancies: [u64; 4096] = [0; 4096];
let mut attacks: [u64; 4096] = [0; 4096];
let attack_mask = mask_rook_attacks(square);
let occupancy_indices = 1_u64 << (relevant_bits);
for index in 0..occupancy_indices {
occupancies[index as usize] =
set_occupancy(index, attack_mask.count_ones() as u8, attack_mask);
attacks[index as usize] = rook_attacks_on_the_fly(square, occupancies[index as usize]);
}
for _ in 0..100_000_000 {
let magic_number = generate_magic_number_candidate(state);
if 6 > (attack_mask.wrapping_mul(magic_number) & 0xFF00000000000000_u64).count_ones() {
continue;
}
let mut used_attacks = [0; 4096];
let mut fail = false;
for idx in 0..occupancy_indices {
let magic_idx = (occupancies[idx as usize].wrapping_mul(magic_number)
>> ((64 - relevant_bits) as u32)) as usize;
if used_attacks[magic_idx] == 0_u64 {
used_attacks[magic_idx] = attacks[idx as usize];
} else if used_attacks[magic_idx] != attacks[idx as usize] {
fail = true;
break;
}
}
if !fail {
return magic_number;
}
}
0_u64
}
pub fn find_bishop_magic_numbers(relevant_bits: u8, square: Bitboard, state: &mut SmallRng) -> u64 {
let mut occupancies: [u64; 4096] = [0; 4096];
let mut attacks: [u64; 4096] = [0; 4096];
let attack_mask = mask_bishop_attacks(square);
let occupancy_indices = 1_u64 << (relevant_bits);
for index in 0..occupancy_indices {
occupancies[index as usize] =
set_occupancy(index, attack_mask.count_ones() as u8, attack_mask);
attacks[index as usize] = bishop_attacks_on_the_fly(square, occupancies[index as usize]);
}
for _ in 0..100_000_000 {
let magic_number = generate_magic_number_candidate(state);
if 6 > (attack_mask.wrapping_mul(magic_number) & 0xFF00000000000000_u64).count_ones() {
continue;
}
let mut used_attacks = [0; 4096];
let mut fail = false;
for idx in 0..occupancy_indices {
let magic_idx = (occupancies[idx as usize].wrapping_mul(magic_number)
>> ((64 - relevant_bits) as u32)) as usize;
if used_attacks[magic_idx] == 0_u64 {
used_attacks[magic_idx] = attacks[idx as usize];
} else if used_attacks[magic_idx] != attacks[idx as usize] {
fail = true;
break;
}
}
if !fail {
return magic_number;
}
}
0_u64
}
pub fn init_magic_arrays() { pub fn init_magic_arrays() {
let mut state = SmallRng::seed_from_u64(1804289383); let mut state = SmallRng::seed_from_u64(1804289383);
for idx in 0..64 { for square in Square::A1..=Square::H8 {
let rook_magic_number = let rook_magic_number = find_magic_numbers(
find_rook_magic_numbers(ROOK_RELEVANT_BITS[idx], 1_u64 << (idx), &mut state); ROOK_RELEVANT_BITS[square],
square_to_bitboard(square),
&mut state,
mask_rook_attacks,
rook_attacks_on_the_fly,
);
unsafe { unsafe {
ROOK_MAGIC_INIT[idx] = rook_magic_number; ROOK_MAGIC_INIT[square] = rook_magic_number;
} }
} }
for idx in 0..64 { for square in Square::A1..=Square::H8 {
let bishop_magic_number = let bishop_magic_number = find_magic_numbers(
find_bishop_magic_numbers(BISHOP_RELEVANT_BITS[idx], 1_u64 << (idx), &mut state); BISHOP_RELEVANT_BITS[square],
square_to_bitboard(square),
&mut state,
mask_bishop_attacks,
bishop_attacks_on_the_fly,
);
unsafe { unsafe {
BISHOP_MAGIC_INIT[idx] = bishop_magic_number; BISHOP_MAGIC_INIT[square] = bishop_magic_number;
} }
} }
} }
pub fn find_magic_numbers<F1, F2>(
relevant_bits: usize,
bitboard: Bitboard,
state: &mut SmallRng,
mask_attacks: F1,
attacks_on_the_fly: F2,
) -> u64
where
F1: Fn(Bitboard) -> Bitboard,
F2: Fn(Bitboard, Bitboard) -> Bitboard,
{
let mut occupancies: [u64; 4096] = [0; 4096];
let mut attacks: [u64; 4096] = [0; 4096];
let attack_mask = mask_attacks(bitboard);
let occupancy_indices = square_to_bitboard(relevant_bits);
for index in 0..occupancy_indices {
occupancies[index as usize] = set_occupancy(index, bit_count(attack_mask), attack_mask);
attacks[index as usize] = attacks_on_the_fly(bitboard, occupancies[index as usize]);
}
generate_magic_number(
relevant_bits,
state,
attack_mask,
occupancy_indices,
occupancies,
attacks,
)
}
pub fn generate_magic_number(
relevant_bits: usize,
state: &mut SmallRng,
attack_mask: u64,
occupancy_indices: u64,
occupancies: [u64; 4096],
attacks: [u64; 4096],
) -> u64 {
for _ in 0..100000000 {
let magic_number = generate_magic_number_candidate(state);
if 6 > bit_count(attack_mask.wrapping_mul(magic_number) & RANK_8) {
continue;
}
let mut used_attacks = [0; 4096];
let mut fail = false;
for index in 0..occupancy_indices {
let magic = (occupancies[index as usize].wrapping_mul(magic_number)
>> ((64 - relevant_bits) as u32)) as usize;
if used_attacks[magic] == EMPTY {
used_attacks[magic] = attacks[index as usize];
} else if used_attacks[magic] != attacks[index as usize] {
fail = true;
break;
}
}
if !fail {
return magic_number;
}
}
EMPTY
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::init_magic_arrays; use super::init_magic_arrays;
use crate::magic::{BISHOP_MAGIC, BISHOP_MAGIC_INIT, ROOK_MAGIC, ROOK_MAGIC_INIT}; use crate::{
magic::{BISHOP_MAGIC, BISHOP_MAGIC_INIT, ROOK_MAGIC, ROOK_MAGIC_INIT},
square::Square,
};
#[test] #[test]
fn test_init_magic_arrays() -> Result<(), String> { fn test_init_magic_arrays() -> Result<(), String> {
init_magic_arrays(); init_magic_arrays();
for idx in 0..64 { for index in Square::A1..=Square::H8 {
unsafe { unsafe {
assert_eq!(ROOK_MAGIC[idx], ROOK_MAGIC_INIT[idx]); assert_eq!(ROOK_MAGIC[index], ROOK_MAGIC_INIT[index]);
assert_eq!(BISHOP_MAGIC[idx], BISHOP_MAGIC_INIT[idx]); assert_eq!(BISHOP_MAGIC[index], BISHOP_MAGIC_INIT[index]);
} }
} }

View File

@@ -1,4 +1,5 @@
pub mod attack; pub mod attack;
pub mod bitboard;
pub mod board; pub mod board;
pub mod fen; pub mod fen;
pub mod game; pub mod game;
@@ -6,6 +7,7 @@ pub mod magic;
pub mod r#move; pub mod r#move;
pub mod movegen; pub mod movegen;
pub mod perft; pub mod perft;
pub mod square;
fn main() { fn main() {
attack::init_attacks(); attack::init_attacks();

View File

@@ -9,8 +9,8 @@ use u64 as Bitboard;
const NOT_A_FILE: Bitboard = 0xfefefefefefefefe; const NOT_A_FILE: Bitboard = 0xfefefefefefefefe;
const NOT_H_FILE: Bitboard = 0x7f7f7f7f7f7f7f7f; const NOT_H_FILE: Bitboard = 0x7f7f7f7f7f7f7f7f;
const RANK_4: Bitboard = 0x00000000FF000000; const RANK_4: Bitboard = 0x00000000ff000000;
const RANK_5: Bitboard = 0x000000FF00000000; const RANK_5: Bitboard = 0x000000ff00000000;
pub fn pawn_pseudo_moves( pub fn pawn_pseudo_moves(
pawns: Bitboard, pawns: Bitboard,

View File

@@ -24,7 +24,7 @@ const MAX_DEPTH: u8 = 3;
pub fn perftree_script() { pub fn perftree_script() {
let fen = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"; let fen = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1";
let mut game = crate::fen::from_fen(fen).unwrap(); let mut game = crate::fen::from_fen(fen).expect("Invalid FEN string");
let (mut nodes, depth): (u64, u8) = (0, MAX_DEPTH); let (mut nodes, depth): (u64, u8) = (0, MAX_DEPTH);
driver(&mut game, &mut nodes, depth); driver(&mut game, &mut nodes, depth);
println!(); println!();
@@ -52,7 +52,7 @@ pub fn square_to_notation(square: u8) -> &'static str {
map.insert(52, "e7"); map.insert(53, "f7"); map.insert(54, "g7"); map.insert(55, "h7"); map.insert(52, "e7"); map.insert(53, "f7"); map.insert(54, "g7"); map.insert(55, "h7");
map.insert(56, "a8"); map.insert(57, "b8"); map.insert(58, "c8"); map.insert(59, "d8"); map.insert(56, "a8"); map.insert(57, "b8"); map.insert(58, "c8"); map.insert(59, "d8");
map.insert(60, "e8"); map.insert(61, "f8"); map.insert(62, "g8"); map.insert(63, "h8"); map.insert(60, "e8"); map.insert(61, "f8"); map.insert(62, "g8"); map.insert(63, "h8");
map.get(&square).unwrap() map.get(&square).expect("Invalid square")
} }
#[cfg(test)] #[cfg(test)]

87
src/square.rs Normal file
View File

@@ -0,0 +1,87 @@
use u64 as Bitboard;
pub struct Square {}
impl Square {
pub const A1: usize = 0;
pub const B1: usize = 1;
pub const C1: usize = 2;
pub const D1: usize = 3;
pub const E1: usize = 4;
pub const F1: usize = 5;
pub const G1: usize = 6;
pub const H1: usize = 7;
pub const A2: usize = 8;
pub const B2: usize = 9;
pub const C2: usize = 10;
pub const D2: usize = 11;
pub const E2: usize = 12;
pub const F2: usize = 13;
pub const G2: usize = 14;
pub const H2: usize = 15;
pub const A3: usize = 16;
pub const B3: usize = 17;
pub const C3: usize = 18;
pub const D3: usize = 19;
pub const E3: usize = 20;
pub const F3: usize = 21;
pub const G3: usize = 22;
pub const H3: usize = 23;
pub const A4: usize = 24;
pub const B4: usize = 25;
pub const C4: usize = 26;
pub const D4: usize = 27;
pub const E4: usize = 28;
pub const F4: usize = 29;
pub const G4: usize = 30;
pub const H4: usize = 31;
pub const A5: usize = 32;
pub const B5: usize = 33;
pub const C5: usize = 34;
pub const D5: usize = 35;
pub const E5: usize = 36;
pub const F5: usize = 37;
pub const G5: usize = 38;
pub const H5: usize = 39;
pub const A6: usize = 40;
pub const B6: usize = 41;
pub const C6: usize = 42;
pub const D6: usize = 43;
pub const E6: usize = 44;
pub const F6: usize = 45;
pub const G6: usize = 46;
pub const H6: usize = 47;
pub const A7: usize = 48;
pub const B7: usize = 49;
pub const C7: usize = 50;
pub const D7: usize = 51;
pub const E7: usize = 52;
pub const F7: usize = 53;
pub const G7: usize = 54;
pub const H7: usize = 55;
pub const A8: usize = 56;
pub const B8: usize = 57;
pub const C8: usize = 58;
pub const D8: usize = 59;
pub const E8: usize = 60;
pub const F8: usize = 61;
pub const G8: usize = 62;
pub const H8: usize = 63;
}
pub const fn coords_to_square(rank: usize, file: usize) -> usize {
rank * 8 + file
}
impl Square {
pub const fn to_bitboard(sq: usize) -> Bitboard {
1_u64 << sq
}
}