From 13c3eb2eb1048cf2cdb98505f4351064d10fc2c6 Mon Sep 17 00:00:00 2001 From: stefiosif Date: Sat, 6 Jul 2024 21:51:20 +0300 Subject: [PATCH] Refactor duplicate code and make it more readable --- src/attack.rs | 259 ++++++++++++++++++++++-------------------------- src/bitboard.rs | 29 ++++++ src/board.rs | 34 +++---- src/game.rs | 2 +- src/magic.rs | 197 ++++++++++++++++++------------------ src/main.rs | 2 + src/movegen.rs | 4 +- src/perft.rs | 4 +- src/square.rs | 87 ++++++++++++++++ 9 files changed, 358 insertions(+), 260 deletions(-) create mode 100644 src/bitboard.rs create mode 100644 src/square.rs diff --git a/src/attack.rs b/src/attack.rs index 5271ea1..6a98689 100644 --- a/src/attack.rs +++ b/src/attack.rs @@ -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); } } } diff --git a/src/bitboard.rs b/src/bitboard.rs new file mode 100644 index 0000000..75cefac --- /dev/null +++ b/src/bitboard.rs @@ -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 +} diff --git a/src/board.rs b/src/board.rs index 5c69b2b..5e5e7fe 100644 --- a/src/board.rs +++ b/src/board.rs @@ -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, } } } diff --git a/src/game.rs b/src/game.rs index f95b678..7854a76 100644 --- a/src/game.rs +++ b/src/game.rs @@ -118,7 +118,7 @@ impl State { } } - pub fn next_turn(&self) -> Color { + pub const fn next_turn(&self) -> Color { self.side_to_move } diff --git a/src/magic.rs b/src/magic.rs index a51451b..c8f2304 100644 --- a/src/magic.rs +++ b/src/magic.rs @@ -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( + 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]); } } diff --git a/src/main.rs b/src/main.rs index 1e22d35..cc0cfbf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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(); diff --git a/src/movegen.rs b/src/movegen.rs index 0e4b42c..8f06140 100644 --- a/src/movegen.rs +++ b/src/movegen.rs @@ -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, diff --git a/src/perft.rs b/src/perft.rs index 1ceb9e0..376b8ff 100644 --- a/src/perft.rs +++ b/src/perft.rs @@ -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)] diff --git a/src/square.rs b/src/square.rs new file mode 100644 index 0000000..a30d6f6 --- /dev/null +++ b/src/square.rs @@ -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 + } +}