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;
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]
pub const ROOK_RELEVANT_BITS: [u8; 64] = [
pub 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,
@@ -19,7 +21,7 @@ pub const ROOK_RELEVANT_BITS: [u8; 64] = [
];
#[rustfmt::skip]
pub const BISHOP_RELEVANT_BITS: [u8; 64] = [
pub 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,
@@ -37,16 +39,16 @@ static mut BISHOP_ATTACKS: [[Bitboard; 512]; 64] = [[0; 512]; 64];
static mut ROOK_ATTACKS: [[Bitboard; 4096]; 64] = [[0; 4096]; 64];
const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard {
let mut attacks = 0_u64;
let mut attacks = EMPTY;
match color {
Color::Black => {
attacks |= (bitboard & NOT_H_FILE) >> (7);
attacks |= (bitboard & NOT_A_FILE) >> (9);
attacks |= (bitboard & NOT_FILE_H) >> 7;
attacks |= (bitboard & NOT_FILE_A) >> 9;
}
Color::White => {
attacks |= (bitboard & NOT_A_FILE) << (7);
attacks |= (bitboard & NOT_H_FILE) << (9);
attacks |= (bitboard & NOT_FILE_A) << 7;
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 {
let mut attacks = 0_u64;
let mut attacks = EMPTY;
attacks |= (bitboard & NOT_AB_FILE) << (6);
attacks |= (bitboard & NOT_GH_FILE) << (10);
attacks |= (bitboard & NOT_A_FILE) << (15);
attacks |= (bitboard & NOT_H_FILE) << (17);
attacks |= (bitboard & NOT_GH_FILE) >> (6);
attacks |= (bitboard & NOT_AB_FILE) >> (10);
attacks |= (bitboard & NOT_H_FILE) >> (15);
attacks |= (bitboard & NOT_A_FILE) >> (17);
attacks |= (bitboard & NOT_FILE_AB) << 6;
attacks |= (bitboard & NOT_FILE_GH) << 10;
attacks |= (bitboard & NOT_FILE_A) << 15;
attacks |= (bitboard & NOT_FILE_H) << 17;
attacks |= (bitboard & NOT_FILE_GH) >> 6;
attacks |= (bitboard & NOT_FILE_AB) >> 10;
attacks |= (bitboard & NOT_FILE_H) >> 15;
attacks |= (bitboard & NOT_FILE_A) >> 17;
attacks
}
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_A_FILE) << (7);
attacks |= bitboard << (8);
attacks |= (bitboard & NOT_H_FILE) << (9);
attacks |= (bitboard & NOT_A_FILE) >> (1);
attacks |= (bitboard & NOT_H_FILE) >> (7);
attacks |= bitboard >> (8);
attacks |= (bitboard & NOT_A_FILE) >> (9);
attacks |= (bitboard & NOT_FILE_H) << 1;
attacks |= (bitboard & NOT_FILE_A) << 7;
attacks |= bitboard << 8;
attacks |= (bitboard & NOT_FILE_H) << 9;
attacks |= (bitboard & NOT_FILE_A) >> 1;
attacks |= (bitboard & NOT_FILE_H) >> 7;
attacks |= bitboard >> 8;
attacks |= (bitboard & NOT_FILE_A) >> 9;
attacks
}
pub fn mask_bishop_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64;
let target_rank = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() / 8
};
let target_file = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() % 8
let mut attacks = EMPTY;
let (target_rank, target_file) = match bitboard {
0 => (0, 0),
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
};
let (mut rank, mut file) = (target_rank + 1, target_file + 1);
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);
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));
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));
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
}
pub fn mask_rook_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64;
let target_rank = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() / 8
};
let target_file = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() % 8
let mut attacks = EMPTY;
let (target_rank, target_file) = match bitboard {
0 => (0, 0),
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
};
let (mut rank, mut file) = (target_rank + 1, target_file + 1);
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 {
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));
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() {
attacks |= 1_u64 << (target_rank * 8 + file);
attacks |= square_to_bitboard(coords_to_square(target_rank, file));
}
attacks
}
pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
let mut attacks = 0_u64;
let target_rank = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() / 8
};
let target_file = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() % 8
let mut attacks = EMPTY;
let (target_rank, target_file) = match bitboard {
0 => (0, 0),
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
};
let (mut rank, mut file) = (target_rank + 1, target_file + 1);
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;
if attack_square & blocker != 0 {
break;
@@ -175,7 +159,7 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
file = target_file + 1;
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;
if attack_square & blocker != 0 {
break;
@@ -184,7 +168,7 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
rank = target_rank + 1;
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;
if attack_square & blocker != 0 {
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()) {
let attack_square = 1_u64 << (rank * 8 + file);
let attack_square = square_to_bitboard(coords_to_square(rank, file));
attacks |= attack_square;
if attack_square & blocker != 0 {
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 {
let mut attacks = 0_u64;
let target_rank = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() / 8
};
let target_file = if bitboard == 0 {
0
} else {
bitboard.trailing_zeros() % 8
let mut attacks = EMPTY;
let (target_rank, target_file) = match bitboard {
0 => (0, 0),
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
};
let (mut rank, mut file) = (target_rank + 1, target_file + 1);
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;
if attack_square & blocker != 0 {
break;
}
}
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;
if attack_square & blocker != 0 {
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));
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;
if attack_square & blocker != 0 {
break;
}
}
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;
if attack_square & blocker != 0 {
break;
@@ -250,96 +228,97 @@ pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboar
attacks
}
pub fn set_occupancy(index: u64, bits_in_mask: u8, mut attack_mask: Bitboard) -> Bitboard {
let mut occupancy = 0_u64;
pub fn set_occupancy(index: u64, relevant_bits: usize, mut attack_mask: Bitboard) -> Bitboard {
let mut occupancy = EMPTY;
for count in 0..bits_in_mask {
let square = attack_mask.trailing_zeros() as u8;
attack_mask &= !(1_u64 << square);
if index & (1_u64 << count) != 0 {
occupancy |= 1_u64 << square;
for bit in 0..relevant_bits {
let square = lsb(attack_mask);
attack_mask &= !square_to_bitboard(square);
if intersect(index, square_to_bitboard(bit)) {
occupancy |= square_to_bitboard(square);
}
}
occupancy
}
pub fn get_pawn_attacks(sq: usize, color: Color) -> Bitboard {
pub fn get_pawn_attacks(square: usize, color: Color) -> Bitboard {
unsafe {
match color {
Color::White => PAWN_ATTACKS[sq][0],
Color::Black => PAWN_ATTACKS[sq][1],
Color::White => PAWN_ATTACKS[square][0],
Color::Black => PAWN_ATTACKS[square][1],
}
}
}
pub fn get_knight_attacks(sq: usize) -> Bitboard {
unsafe { KNIGHT_ATTACKS[sq] }
pub fn get_knight_attacks(square: usize) -> Bitboard {
unsafe { KNIGHT_ATTACKS[square] }
}
pub fn get_king_attacks(sq: usize) -> Bitboard {
unsafe { KING_ATTACKS[sq] }
pub fn get_king_attacks(square: usize) -> Bitboard {
unsafe { KING_ATTACKS[square] }
}
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 {
occupancy &= mask_bishop_attacks(1_u64.wrapping_shl(sq as u32));
occupancy = occupancy.wrapping_mul(BISHOP_MAGIC[sq]);
occupancy >>= 64 - BISHOP_RELEVANT_BITS[sq];
BISHOP_ATTACKS[sq][occupancy as usize]
occupancy &= mask_bishop_attacks(square_to_bitboard_wrapping(square));
occupancy = occupancy.wrapping_mul(BISHOP_MAGIC[square]);
occupancy >>= 64 - BISHOP_RELEVANT_BITS[square];
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 {
occupancy &= mask_rook_attacks(1_u64.wrapping_shl(sq as u32));
occupancy = occupancy.wrapping_mul(ROOK_MAGIC[sq]);
occupancy >>= 64 - ROOK_RELEVANT_BITS[sq];
ROOK_ATTACKS[sq][occupancy as usize]
occupancy &= mask_rook_attacks(square_to_bitboard_wrapping(square));
occupancy = occupancy.wrapping_mul(ROOK_MAGIC[square]);
occupancy >>= 64 - ROOK_RELEVANT_BITS[square];
ROOK_ATTACKS[square][occupancy as usize]
}
}
pub fn get_queen_attacks(occupancy: Bitboard, sq: usize) -> Bitboard {
get_rook_attacks(occupancy, sq) | get_bishop_attacks(occupancy, sq)
pub fn get_queen_attacks(occupancy: Bitboard, square: usize) -> Bitboard {
get_rook_attacks(occupancy, square) | get_bishop_attacks(occupancy, square)
}
pub fn init_pawn_attacks() {
(0..64).for_each(|sq| unsafe {
PAWN_ATTACKS[sq][0] = pawn_attacks(1_u64 << (sq), Color::White);
PAWN_ATTACKS[sq][1] = pawn_attacks(1_u64 << (sq), Color::Black);
(0..64).for_each(|square| unsafe {
PAWN_ATTACKS[square][0] = pawn_attacks(square_to_bitboard(square), Color::White);
PAWN_ATTACKS[square][1] = pawn_attacks(square_to_bitboard(square), Color::Black);
});
}
pub fn init_knight_attacks() {
(0..64).for_each(|sq| unsafe {
KNIGHT_ATTACKS[sq] = knight_attacks(1_u64 << (sq));
(0..64).for_each(|square| unsafe {
KNIGHT_ATTACKS[square] = knight_attacks(square_to_bitboard(square));
});
}
pub fn init_king_attacks() {
(0..64).for_each(|sq| unsafe {
KING_ATTACKS[sq] = king_attacks(1_u64 << (sq));
(0..64).for_each(|square| unsafe {
KING_ATTACKS[square] = king_attacks(square_to_bitboard(square));
});
}
pub fn init_bishop_attacks() {
let mut bishop_masks = [0; 64];
for sq in 0..64 {
bishop_masks[sq] = mask_bishop_attacks(1_u64 << (sq));
let attack_mask = bishop_masks[sq];
let occupancy_indices = 1_u64 << BISHOP_RELEVANT_BITS[sq];
for square in 0..64 {
bishop_masks[square] = mask_bishop_attacks(square_to_bitboard(square));
let attack_mask = bishop_masks[square];
let occupancy_indices = square_to_bitboard(BISHOP_RELEVANT_BITS[square]);
for idx in 0..occupancy_indices {
let occupancy = set_occupancy(idx, BISHOP_RELEVANT_BITS[sq], attack_mask);
for index in 0..occupancy_indices {
let occupancy = set_occupancy(index, BISHOP_RELEVANT_BITS[square], attack_mask);
unsafe {
let magic_index =
occupancy.wrapping_mul(BISHOP_MAGIC[sq]) >> (64 - BISHOP_RELEVANT_BITS[sq]);
let magic_index = (occupancy.wrapping_mul(BISHOP_MAGIC[square])
>> (64 - BISHOP_RELEVANT_BITS[square]))
as usize;
BISHOP_ATTACKS[sq][magic_index as usize] =
bishop_attacks_on_the_fly(1_u64 << (sq), occupancy);
BISHOP_ATTACKS[square][magic_index] =
bishop_attacks_on_the_fly(square_to_bitboard(square), occupancy);
}
}
}
@@ -348,20 +327,20 @@ pub fn init_bishop_attacks() {
pub fn init_rook_attacks() {
let mut rook_masks = [0; 64];
for sq in 0..64 {
rook_masks[sq] = mask_rook_attacks(1_u64 << (sq));
let attack_mask = rook_masks[sq];
let relevant_bits_count = attack_mask.count_ones() as u8;
let occupancy_indices = 1_u64 << (relevant_bits_count);
for square in 0..64 {
rook_masks[square] = mask_rook_attacks(square_to_bitboard(square));
let attack_mask = rook_masks[square];
let occupancy_indices = square_to_bitboard(ROOK_RELEVANT_BITS[square]);
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 {
let magic_index =
occupancy.wrapping_mul(ROOK_MAGIC[sq]) >> (64 - ROOK_RELEVANT_BITS[sq]);
let magic_index = (occupancy.wrapping_mul(ROOK_MAGIC[square])
>> (64 - ROOK_RELEVANT_BITS[square]))
as usize;
ROOK_ATTACKS[sq][magic_index as usize] =
rook_attacks_on_the_fly(1_u64 << (sq), occupancy);
ROOK_ATTACKS[square][magic_index] =
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 {
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);
}
MoveType::Capture => {
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
Board::remove_piece(mv.target as u8, opponent_pieces);
Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
Self::remove_piece(mv.target as u8, opponent_pieces);
self.state.set_en_passant_target_square(None);
}
MoveType::EnPassant => {
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
Board::remove_pawn_enpassant(mv.target as u8, opponent_pieces, color);
Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
Self::remove_pawn_enpassant(mv.target as u8, opponent_pieces, color);
self.state.set_en_passant_target_square(None);
}
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 {
Color::White => 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);
}
MoveType::Promotion(promote) => {
Board::remove_piece(mv.source as u8, own_pieces);
Board::promote_piece(mv.target as u8, own_pieces, *promote);
Self::remove_piece(mv.source as u8, own_pieces);
Self::promote_piece(mv.target as u8, own_pieces, *promote);
self.state.set_en_passant_target_square(None);
}
MoveType::PromotionCapture(promote) => {
Board::remove_piece(mv.source as u8, own_pieces);
Board::remove_piece(mv.target as u8, opponent_pieces);
Board::promote_piece(mv.target as u8, own_pieces, *promote);
Self::remove_piece(mv.source as u8, own_pieces);
Self::remove_piece(mv.target as u8, opponent_pieces);
Self::promote_piece(mv.target as u8, own_pieces, *promote);
self.state.set_en_passant_target_square(None);
}
MoveType::Castle => {
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
Board::move_rook_castle(mv.target as u8, own_pieces);
Self::move_piece(mv.source as u8, 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_castling_ability(color, Castle::None)
}
@@ -238,7 +238,7 @@ impl Board {
_ => 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) {
@@ -320,10 +320,10 @@ pub enum Color {
}
impl Color {
pub const fn opponent_color(color: Color) -> Color {
pub const fn opponent_color(color: Self) -> Self {
match color {
Color::White => Color::Black,
Color::Black => Color::White,
Self::White => Self::Black,
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
}

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,
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::{RngCore, SeedableRng};
use u64 as Bitboard;
@@ -50,10 +52,10 @@ pub const BISHOP_MAGIC: [u64; 64] = [
];
pub fn random_uint64(state: &mut SmallRng) -> u64 {
let n1 = state.next_u32() as u64 & 0xFFFF;
let n2 = state.next_u32() as u64 & 0xFFFF;
let n3 = state.next_u32() as u64 & 0xFFFF;
let n4 = state.next_u32() as u64 & 0xFFFF;
let n1 = state.next_u32() as u64 & 0xffff;
let n2 = state.next_u32() as u64 & 0xffff;
let n3 = state.next_u32() as u64 & 0xffff;
let n4 = state.next_u32() as u64 & 0xffff;
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)
}
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() {
let mut state = SmallRng::seed_from_u64(1804289383);
for idx in 0..64 {
let rook_magic_number =
find_rook_magic_numbers(ROOK_RELEVANT_BITS[idx], 1_u64 << (idx), &mut state);
for square in Square::A1..=Square::H8 {
let rook_magic_number = find_magic_numbers(
ROOK_RELEVANT_BITS[square],
square_to_bitboard(square),
&mut state,
mask_rook_attacks,
rook_attacks_on_the_fly,
);
unsafe {
ROOK_MAGIC_INIT[idx] = rook_magic_number;
ROOK_MAGIC_INIT[square] = rook_magic_number;
}
}
for idx in 0..64 {
let bishop_magic_number =
find_bishop_magic_numbers(BISHOP_RELEVANT_BITS[idx], 1_u64 << (idx), &mut state);
for square in Square::A1..=Square::H8 {
let bishop_magic_number = find_magic_numbers(
BISHOP_RELEVANT_BITS[square],
square_to_bitboard(square),
&mut state,
mask_bishop_attacks,
bishop_attacks_on_the_fly,
);
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)]
mod tests {
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]
fn test_init_magic_arrays() -> Result<(), String> {
init_magic_arrays();
for idx in 0..64 {
for index in Square::A1..=Square::H8 {
unsafe {
assert_eq!(ROOK_MAGIC[idx], ROOK_MAGIC_INIT[idx]);
assert_eq!(BISHOP_MAGIC[idx], BISHOP_MAGIC_INIT[idx]);
assert_eq!(ROOK_MAGIC[index], ROOK_MAGIC_INIT[index]);
assert_eq!(BISHOP_MAGIC[index], BISHOP_MAGIC_INIT[index]);
}
}

View File

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

View File

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

View File

@@ -24,7 +24,7 @@ const MAX_DEPTH: u8 = 3;
pub fn perftree_script() {
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);
driver(&mut game, &mut nodes, depth);
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(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.get(&square).unwrap()
map.get(&square).expect("Invalid square")
}
#[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
}
}