From c8d7c17711e6e23c44f9cb5aeb32cd44b3111ee5 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Mon, 25 Jul 2022 16:34:35 +0200 Subject: [PATCH 01/14] Fix pre-commit check in CI Now that we have actual dependencies, we need to run 'pre-commit run' outside of the build sandbox. --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 7037529..a6c1ce5 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,9 +4,9 @@ type: exec name: abacus checks steps: -- name: flake check +- name: pre commit check commands: - - nix flake check + - nix develop . --command pre-commit run --all - name: package check commands: From 72f74762d163ae996f24d3179419bc521d2de3e1 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Fri, 22 Jul 2022 18:41:09 +0200 Subject: [PATCH 02/14] Make 'Magic' fields 'pub(crate)' --- src/movegen/magic/magic.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/movegen/magic/magic.rs b/src/movegen/magic/magic.rs index 0f328d5..6fe0400 100644 --- a/src/movegen/magic/magic.rs +++ b/src/movegen/magic/magic.rs @@ -4,13 +4,13 @@ use crate::board::Bitboard; #[allow(unused)] // FIXME: remove once used pub struct Magic { /// Magic number. - magic: u64, + pub(crate) magic: u64, /// Base offset into the magic square table. - offset: usize, + pub(crate) offset: usize, /// Mask to apply to the blocker board before applying the magic. - mask: Bitboard, + pub(crate) mask: Bitboard, /// Length of the resulting mask after applying the magic. - shift: u8, + pub(crate) shift: u8, } impl Magic { From 9af18b4750b35fae8e1fab5f5bd5adc8234cca8c Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Fri, 22 Jul 2022 18:42:05 +0200 Subject: [PATCH 03/14] Add magic bitboard generation --- src/movegen/magic/magic.rs | 2 - src/movegen/wizardry/generation.rs | 68 ++++++++++++++++++++++++++++++ src/movegen/wizardry/mask.rs | 2 - src/movegen/wizardry/mod.rs | 1 + 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 src/movegen/wizardry/generation.rs diff --git a/src/movegen/magic/magic.rs b/src/movegen/magic/magic.rs index 6fe0400..f59bde2 100644 --- a/src/movegen/magic/magic.rs +++ b/src/movegen/magic/magic.rs @@ -1,7 +1,6 @@ use crate::board::Bitboard; /// A type representing the magic board indexing a given [Square]. -#[allow(unused)] // FIXME: remove once used pub struct Magic { /// Magic number. pub(crate) magic: u64, @@ -14,7 +13,6 @@ pub struct Magic { } impl Magic { - #[allow(unused)] // FIXME: remove once used pub fn get_index(&self, blockers: Bitboard) -> usize { let relevant_occupancy = (blockers & self.mask).0; let base_index = ((relevant_occupancy.wrapping_mul(self.magic)) >> self.shift) as usize; diff --git a/src/movegen/wizardry/generation.rs b/src/movegen/wizardry/generation.rs new file mode 100644 index 0000000..48e80e1 --- /dev/null +++ b/src/movegen/wizardry/generation.rs @@ -0,0 +1,68 @@ +use crate::board::{Bitboard, Square}; +use crate::movegen::bishop::bishop_moves; +use crate::movegen::rook::rook_moves; +use crate::movegen::Magic; + +use super::mask::{generate_bishop_mask, generate_rook_mask}; + +type MagicGenerationType = (Vec, Vec); + +#[allow(unused)] // FIXME: remove when used +pub fn generate_bishop_magics(rng: &mut dyn random::Source) -> MagicGenerationType { + generate_magics(rng, generate_bishop_mask, bishop_moves) +} + +#[allow(unused)] // FIXME: remove when used +pub fn generate_rook_magics(rng: &mut dyn random::Source) -> MagicGenerationType { + generate_magics(rng, generate_rook_mask, rook_moves) +} + +fn generate_magics( + rng: &mut dyn random::Source, + mask_fn: impl Fn(Square) -> Bitboard, + moves_fn: impl Fn(Square, Bitboard) -> Bitboard, +) -> MagicGenerationType { + let mut offset = 0; + + let mut magics = Vec::new(); + let mut boards = Vec::new(); + + for square in Square::iter() { + let mask = mask_fn(square); + let mut candidate: Magic; + let potential_occupancy: Vec<_> = mask.iter_power_set().collect(); + let moves_len = potential_occupancy.len(); + + 'candidate_search: loop { + candidate = Magic { + magic: magic_candidate(rng), + offset, + mask, + shift: (64 - mask.count()) as u8, + }; + let mut candidate_moves = Vec::new(); + candidate_moves.resize(moves_len, Bitboard::EMPTY); + + for occupancy in potential_occupancy.iter().cloned() { + let index = candidate.get_index(occupancy); + let moves = moves_fn(square, occupancy); + if candidate_moves[index] != Bitboard::EMPTY && candidate_moves[index] != moves { + continue 'candidate_search; + } + candidate_moves[index] = moves; + } + + boards.append(&mut candidate_moves); + break; + } + + magics.push(candidate); + offset += moves_len; + } + + (magics, boards) +} + +fn magic_candidate(rng: &mut dyn random::Source) -> u64 { + rng.read_u64() & rng.read_u64() & rng.read_u64() +} diff --git a/src/movegen/wizardry/mask.rs b/src/movegen/wizardry/mask.rs index 13d7cd6..0c0bbdf 100644 --- a/src/movegen/wizardry/mask.rs +++ b/src/movegen/wizardry/mask.rs @@ -2,7 +2,6 @@ use crate::board::{Bitboard, File, Rank, Square}; use crate::movegen::bishop::bishop_moves; use crate::movegen::rook::rook_moves; -#[allow(unused)] // FIXME: remove once used pub fn generate_bishop_mask(square: Square) -> Bitboard { let rays = bishop_moves(square, Bitboard::EMPTY); @@ -14,7 +13,6 @@ pub fn generate_bishop_mask(square: Square) -> Bitboard { rays - mask } -#[allow(unused)] // FIXME: remove once used pub fn generate_rook_mask(square: Square) -> Bitboard { let rays = rook_moves(square, Bitboard::EMPTY); diff --git a/src/movegen/wizardry/mod.rs b/src/movegen/wizardry/mod.rs index f6053c6..8b5ba4e 100644 --- a/src/movegen/wizardry/mod.rs +++ b/src/movegen/wizardry/mod.rs @@ -1 +1,2 @@ +mod generation; mod mask; From ce86301068289a1c6943dbfe93e6fac5072a9606 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Fri, 22 Jul 2022 18:46:39 +0200 Subject: [PATCH 04/14] Remove superfluous 'format!' call --- src/board/square.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/board/square.rs b/src/board/square.rs index b5a033c..0d58c2b 100644 --- a/src/board/square.rs +++ b/src/board/square.rs @@ -18,7 +18,7 @@ pub enum Square { impl std::fmt::Display for Square { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", format!("{:?}", self)) + write!(f, "{:?}", self) } } From b6f1e763006b98d068a11fc4f2f5ba9acdf7a6bf Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Fri, 22 Jul 2022 18:46:56 +0200 Subject: [PATCH 05/14] Allow some clippy warnings --- src/board/bitboard/mod.rs | 2 ++ src/board/square.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/board/bitboard/mod.rs b/src/board/bitboard/mod.rs index 91c3122..d5ca8eb 100644 --- a/src/board/bitboard/mod.rs +++ b/src/board/bitboard/mod.rs @@ -19,6 +19,7 @@ impl Bitboard { pub const ALL: Bitboard = Bitboard(u64::MAX); /// Array of bitboards representing the eight ranks, in order from rank 1 to rank 8. + #[allow(clippy::unusual_byte_groupings)] pub const RANKS: [Self; 8] = [ Bitboard(0b00000001_00000001_00000001_00000001_00000001_00000001_00000001_00000001), Bitboard(0b00000010_00000010_00000010_00000010_00000010_00000010_00000010_00000010), @@ -31,6 +32,7 @@ impl Bitboard { ]; /// Array of bitboards representing the eight files, in order from file A to file H. + #[allow(clippy::unusual_byte_groupings)] pub const FILES: [Self; 8] = [ Bitboard(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_11111111), Bitboard(0b00000000_00000000_00000000_00000000_00000000_00000000_11111111_00000000), diff --git a/src/board/square.rs b/src/board/square.rs index 0d58c2b..97d9bdc 100644 --- a/src/board/square.rs +++ b/src/board/square.rs @@ -109,6 +109,7 @@ impl std::ops::Shl for Square { #[inline(always)] fn shl(self, rhs: usize) -> Self::Output { + #[allow(clippy::suspicious_arithmetic_impl)] Square::from_index(self as usize + rhs) } } @@ -119,6 +120,7 @@ impl std::ops::Shr for Square { #[inline(always)] fn shr(self, rhs: usize) -> Self::Output { + #[allow(clippy::suspicious_arithmetic_impl)] Square::from_index(self as usize - rhs) } } From fa1bf74c44d5e51c93ba7e759d66e5a12029bbaa Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Fri, 22 Jul 2022 18:49:16 +0200 Subject: [PATCH 06/14] Add '# Safety' section to 'unsafe fn' doc --- src/board/castle_rights.rs | 4 ++++ src/board/color.rs | 4 ++++ src/board/file.rs | 4 ++++ src/board/piece.rs | 4 ++++ src/board/rank.rs | 4 ++++ src/board/square.rs | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/src/board/castle_rights.rs b/src/board/castle_rights.rs index 8ed8764..d727fd8 100644 --- a/src/board/castle_rights.rs +++ b/src/board/castle_rights.rs @@ -26,6 +26,10 @@ impl CastleRights { } /// Convert from a castle rights index into a [CastleRights] type, no bounds checking. + /// + /// # Safety + /// + /// Should only be called with values that can be output by [CastleRights::index()]. #[inline(always)] pub unsafe fn from_index_unchecked(index: usize) -> Self { std::mem::transmute(index as u8) diff --git a/src/board/color.rs b/src/board/color.rs index 09d017b..07024ee 100644 --- a/src/board/color.rs +++ b/src/board/color.rs @@ -20,6 +20,10 @@ impl Color { } /// Convert from a piece index into a [Color] type, no bounds checking. + /// + /// # Safety + /// + /// Should only be called with values that can be output by [Color::index()]. #[inline(always)] pub unsafe fn from_index_unchecked(index: usize) -> Self { std::mem::transmute(index as u8) diff --git a/src/board/file.rs b/src/board/file.rs index 55a138b..4c84f20 100644 --- a/src/board/file.rs +++ b/src/board/file.rs @@ -43,6 +43,10 @@ impl File { } /// Convert from a file index into a [File] type, no bounds checking. + /// + /// # Safety + /// + /// Should only be called with values that can be output by [File::index()]. #[inline(always)] pub unsafe fn from_index_unchecked(index: usize) -> Self { std::mem::transmute(index as u8) diff --git a/src/board/piece.rs b/src/board/piece.rs index 17ae913..58f989a 100644 --- a/src/board/piece.rs +++ b/src/board/piece.rs @@ -36,6 +36,10 @@ impl Piece { } /// Convert from a piece index into a [Piece] type, no bounds checking. + /// + /// # Safety + /// + /// Should only be called with values that can be output by [Piece::index()]. #[inline(always)] pub unsafe fn from_index_unchecked(index: usize) -> Self { std::mem::transmute(index as u8) diff --git a/src/board/rank.rs b/src/board/rank.rs index 70fad46..a0b04d3 100644 --- a/src/board/rank.rs +++ b/src/board/rank.rs @@ -43,6 +43,10 @@ impl Rank { } /// Convert from a rank index into a [Rank] type, no bounds checking. + /// + /// # Safety + /// + /// Should only be called with values that can be output by [Rank::index()]. #[inline(always)] pub unsafe fn from_index_unchecked(index: usize) -> Self { std::mem::transmute(index as u8) diff --git a/src/board/square.rs b/src/board/square.rs index 97d9bdc..9ffa824 100644 --- a/src/board/square.rs +++ b/src/board/square.rs @@ -59,6 +59,10 @@ impl Square { } /// Convert from a square index into a [Square] type, no bounds checking. + /// + /// # Safety + /// + /// Should only be called with values that can be output by [Square::index()]. #[inline(always)] pub unsafe fn from_index_unchecked(index: usize) -> Self { std::mem::transmute(index as u8) From eea6509643359038666120cd04ffc0676bac36df Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Fri, 22 Jul 2022 18:50:44 +0200 Subject: [PATCH 07/14] Move 'Magic' into 'seer::movegen::magic' --- src/movegen/magic/magic.rs | 21 --------------------- src/movegen/magic/mod.rs | 23 +++++++++++++++++++++-- src/movegen/wizardry/generation.rs | 24 ++++++++++++------------ 3 files changed, 33 insertions(+), 35 deletions(-) delete mode 100644 src/movegen/magic/magic.rs diff --git a/src/movegen/magic/magic.rs b/src/movegen/magic/magic.rs deleted file mode 100644 index f59bde2..0000000 --- a/src/movegen/magic/magic.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::board::Bitboard; - -/// A type representing the magic board indexing a given [Square]. -pub struct Magic { - /// Magic number. - pub(crate) magic: u64, - /// Base offset into the magic square table. - pub(crate) offset: usize, - /// Mask to apply to the blocker board before applying the magic. - pub(crate) mask: Bitboard, - /// Length of the resulting mask after applying the magic. - pub(crate) shift: u8, -} - -impl Magic { - pub fn get_index(&self, blockers: Bitboard) -> usize { - let relevant_occupancy = (blockers & self.mask).0; - let base_index = ((relevant_occupancy.wrapping_mul(self.magic)) >> self.shift) as usize; - base_index + self.offset - } -} diff --git a/src/movegen/magic/mod.rs b/src/movegen/magic/mod.rs index 068c6e7..f59bde2 100644 --- a/src/movegen/magic/mod.rs +++ b/src/movegen/magic/mod.rs @@ -1,2 +1,21 @@ -pub mod magic; -pub use magic::*; +use crate::board::Bitboard; + +/// A type representing the magic board indexing a given [Square]. +pub struct Magic { + /// Magic number. + pub(crate) magic: u64, + /// Base offset into the magic square table. + pub(crate) offset: usize, + /// Mask to apply to the blocker board before applying the magic. + pub(crate) mask: Bitboard, + /// Length of the resulting mask after applying the magic. + pub(crate) shift: u8, +} + +impl Magic { + pub fn get_index(&self, blockers: Bitboard) -> usize { + let relevant_occupancy = (blockers & self.mask).0; + let base_index = ((relevant_occupancy.wrapping_mul(self.magic)) >> self.shift) as usize; + base_index + self.offset + } +} diff --git a/src/movegen/wizardry/generation.rs b/src/movegen/wizardry/generation.rs index 48e80e1..ee75d57 100644 --- a/src/movegen/wizardry/generation.rs +++ b/src/movegen/wizardry/generation.rs @@ -22,42 +22,42 @@ fn generate_magics( mask_fn: impl Fn(Square) -> Bitboard, moves_fn: impl Fn(Square, Bitboard) -> Bitboard, ) -> MagicGenerationType { - let mut offset = 0; - let mut magics = Vec::new(); let mut boards = Vec::new(); for square in Square::iter() { let mask = mask_fn(square); let mut candidate: Magic; - let potential_occupancy: Vec<_> = mask.iter_power_set().collect(); - let moves_len = potential_occupancy.len(); + + let occupancy_to_moves: Vec<_> = mask + .iter_power_set() + .map(|occupancy| (occupancy, moves_fn(square, occupancy))) + .collect(); 'candidate_search: loop { candidate = Magic { magic: magic_candidate(rng), - offset, + offset: 0, mask, shift: (64 - mask.count()) as u8, }; - let mut candidate_moves = Vec::new(); - candidate_moves.resize(moves_len, Bitboard::EMPTY); + let mut candidate_moves = vec![Bitboard::EMPTY; occupancy_to_moves.len()]; - for occupancy in potential_occupancy.iter().cloned() { + for (occupancy, moves) in occupancy_to_moves.iter().cloned() { let index = candidate.get_index(occupancy); - let moves = moves_fn(square, occupancy); + // Non-constructive collision, try with another candidate if candidate_moves[index] != Bitboard::EMPTY && candidate_moves[index] != moves { continue 'candidate_search; } candidate_moves[index] = moves; } + // We have filled all candidate boards, record the correct offset and add the moves + candidate.offset = boards.len(); boards.append(&mut candidate_moves); + magics.push(candidate); break; } - - magics.push(candidate); - offset += moves_len; } (magics, boards) From 3002e41d80cb9dbe909fd5131bed6d9480f0b30f Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sat, 23 Jul 2022 15:32:56 +0200 Subject: [PATCH 08/14] Add 'Color::iter' --- src/board/color.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/board/color.rs b/src/board/color.rs index 07024ee..ca87ade 100644 --- a/src/board/color.rs +++ b/src/board/color.rs @@ -11,6 +11,13 @@ impl Color { /// The number of [Color] variants. pub const NUM_VARIANTS: usize = 2; + const ALL: [Self; Self::NUM_VARIANTS] = [Self::White, Self::Black]; + + /// Iterate over all colors in order. + pub fn iter() -> impl Iterator { + Self::ALL.iter().cloned() + } + /// Convert from a piece index into a [Color] type. #[inline(always)] pub fn from_index(index: usize) -> Self { From d562f39232b79f57c93159f6198ddb81ed1f327e Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sun, 24 Jul 2022 13:40:01 +0200 Subject: [PATCH 09/14] Make all modules at least 'pub(crate)' --- src/movegen/mod.rs | 12 ++++++------ src/movegen/wizardry/mod.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/movegen/mod.rs b/src/movegen/mod.rs index 3d22eb0..26b60a3 100644 --- a/src/movegen/mod.rs +++ b/src/movegen/mod.rs @@ -3,11 +3,11 @@ pub mod magic; pub use magic::*; // Move generation implementation details -mod bishop; -mod king; -mod knight; -mod pawn; -mod rook; +pub(crate) mod bishop; +pub(crate) mod king; +pub(crate) mod knight; +pub(crate) mod pawn; +pub(crate) mod rook; // Magic bitboard generation -mod wizardry; +pub(crate) mod wizardry; diff --git a/src/movegen/wizardry/mod.rs b/src/movegen/wizardry/mod.rs index 8b5ba4e..dfd732d 100644 --- a/src/movegen/wizardry/mod.rs +++ b/src/movegen/wizardry/mod.rs @@ -1,2 +1,2 @@ -mod generation; +pub(crate) mod generation; mod mask; From e79af35b7d18821befcbaf701d97e71614811414 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sun, 24 Jul 2022 13:40:01 +0200 Subject: [PATCH 10/14] Generate magic tables with build script --- Cargo.toml | 11 +++++ src/build.rs | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 src/build.rs diff --git a/Cargo.toml b/Cargo.toml index 52a2217..756e025 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,19 @@ name = "seer" version = "0.1.0" edition = "2021" +build = "src/build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] random = "0.12.2" + +[build-dependencies] +random = "0.12.2" + +# Optimize build scripts to shorten compile times. +[profile.dev.build-override] +opt-level = 3 + +[profile.release.build-override] +opt-level = 3 diff --git a/src/build.rs b/src/build.rs new file mode 100644 index 0000000..29721d1 --- /dev/null +++ b/src/build.rs @@ -0,0 +1,133 @@ +use std::io::{Result, Write}; + +pub mod board; +pub mod movegen; +pub mod utils; + +use crate::{ + board::{Bitboard, Color, File, Square}, + movegen::{ + king::king_moves, + knight::knight_moves, + pawn::{pawn_captures, pawn_moves}, + wizardry::generation::{generate_bishop_magics, generate_rook_magics}, + Magic, + }, +}; + +fn print_magics(out: &mut dyn Write, var_name: &str, magics: &[Magic]) -> Result<()> { + writeln!(out, "static {}: [Magic; {}] = [", var_name, magics.len())?; + for magic in magics.iter() { + writeln!( + out, + " Magic{{magic: {}, offset: {}, mask: Bitboard({}), shift: {},}},", + magic.magic, magic.offset, magic.mask.0, magic.shift + )?; + } + writeln!(out, "];")?; + Ok(()) +} + +fn print_boards(out: &mut dyn Write, var_name: &str, boards: &[Bitboard]) -> Result<()> { + writeln!(out, "static {}: [Bitboard; {}] = [", var_name, boards.len())?; + for board in boards.iter().cloned() { + writeln!(out, " Bitboard({}),", board.0)?; + } + writeln!(out, "];")?; + Ok(()) +} + +fn print_double_sided_boards( + out: &mut dyn Write, + var_name: &str, + white_boards: &[Bitboard], + black_boards: &[Bitboard], +) -> Result<()> { + assert_eq!(white_boards.len(), black_boards.len()); + writeln!( + out, + "static {}: [[Bitboard; {}]; 2] = [", + var_name, + white_boards.len() + )?; + for color in Color::iter() { + let boards = if color == Color::White { + white_boards + } else { + black_boards + }; + writeln!(out, " [")?; + for square in Square::iter() { + writeln!(out, " Bitboard({}),", boards[square.index()].0)?; + } + writeln!(out, " ],")?; + } + writeln!(out, "];")?; + Ok(()) +} + +#[allow(clippy::redundant_clone)] +fn main() -> Result<()> { + // FIXME: rerun-if-changed directives + + let out_dir = std::env::var_os("OUT_DIR").unwrap(); + let magic_path = std::path::Path::new(&out_dir).join("magic_tables.rs"); + let mut out = std::fs::File::create(&magic_path).unwrap(); + + let rng = random::default().seed([12, 27]); + + { + let (magics, moves) = generate_bishop_magics(&mut rng.clone()); + print_magics(&mut out, "BISHOP_MAGICS", &magics)?; + print_boards(&mut out, "BISHOP_MOVES", &moves)?; + } + + { + let (magics, moves) = generate_rook_magics(&mut rng.clone()); + print_magics(&mut out, "ROOK_MAGICS", &magics)?; + print_boards(&mut out, "ROOK_MOVES", &moves)?; + } + + { + let moves: Vec<_> = Square::iter().map(knight_moves).collect(); + print_boards(&mut out, "KNIGHT_MOVES", &moves)?; + } + + { + let white_moves: Vec<_> = Square::iter() + .map(|square| pawn_moves(Color::White, square, Bitboard::EMPTY)) + .collect(); + let black_moves: Vec<_> = Square::iter() + .map(|square| pawn_moves(Color::Black, square, Bitboard::EMPTY)) + .collect(); + print_double_sided_boards(&mut out, "PAWN_MOVES", &white_moves, &black_moves)?; + let white_attacks: Vec<_> = Square::iter() + .map(|square| pawn_captures(Color::White, square)) + .collect(); + let black_attacks: Vec<_> = Square::iter() + .map(|square| pawn_captures(Color::Black, square)) + .collect(); + print_double_sided_boards(&mut out, "PAWN_ATTACKS", &white_attacks, &black_attacks)?; + } + + { + let moves: Vec<_> = Square::iter().map(king_moves).collect(); + print_boards(&mut out, "KING_MOVES", &moves)?; + let king_blockers: Vec<_> = Color::iter() + .map(|color| { + Square::new(File::F, color.first_rank()) | Square::new(File::G, color.first_rank()) + }) + .collect(); + let queen_blockers: Vec<_> = Color::iter() + .map(|color| { + Square::new(File::B, color.first_rank()) + | Square::new(File::C, color.first_rank()) + | Square::new(File::D, color.first_rank()) + }) + .collect(); + print_boards(&mut out, "KING_SIDE_CASTLE_BLOCKERS", &king_blockers)?; + print_boards(&mut out, "QUEEN_SIDE_CASTLE_BLOCKERS", &queen_blockers)?; + } + + Ok(()) +} From 0dddcd9fa9d284d5d196a29258cccf5e9cd1ab2e Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sun, 24 Jul 2022 16:24:35 +0200 Subject: [PATCH 11/14] Move naive move generation into sub-module --- src/build.rs | 8 +++++--- src/movegen/mod.rs | 8 ++------ src/movegen/{ => naive}/bishop.rs | 0 src/movegen/{ => naive}/king.rs | 0 src/movegen/{ => naive}/knight.rs | 0 src/movegen/naive/mod.rs | 5 +++++ src/movegen/{ => naive}/pawn.rs | 0 src/movegen/{ => naive}/rook.rs | 0 src/movegen/wizardry/generation.rs | 3 +-- src/movegen/wizardry/mask.rs | 3 +-- 10 files changed, 14 insertions(+), 13 deletions(-) rename src/movegen/{ => naive}/bishop.rs (100%) rename src/movegen/{ => naive}/king.rs (100%) rename src/movegen/{ => naive}/knight.rs (100%) create mode 100644 src/movegen/naive/mod.rs rename src/movegen/{ => naive}/pawn.rs (100%) rename src/movegen/{ => naive}/rook.rs (100%) diff --git a/src/build.rs b/src/build.rs index 29721d1..e05410d 100644 --- a/src/build.rs +++ b/src/build.rs @@ -7,9 +7,11 @@ pub mod utils; use crate::{ board::{Bitboard, Color, File, Square}, movegen::{ - king::king_moves, - knight::knight_moves, - pawn::{pawn_captures, pawn_moves}, + naive::{ + king::king_moves, + knight::knight_moves, + pawn::{pawn_captures, pawn_moves}, + }, wizardry::generation::{generate_bishop_magics, generate_rook_magics}, Magic, }, diff --git a/src/movegen/mod.rs b/src/movegen/mod.rs index 26b60a3..9ddbf36 100644 --- a/src/movegen/mod.rs +++ b/src/movegen/mod.rs @@ -2,12 +2,8 @@ pub mod magic; pub use magic::*; -// Move generation implementation details -pub(crate) mod bishop; -pub(crate) mod king; -pub(crate) mod knight; -pub(crate) mod pawn; -pub(crate) mod rook; +// Naive move generation +pub mod naive; // Magic bitboard generation pub(crate) mod wizardry; diff --git a/src/movegen/bishop.rs b/src/movegen/naive/bishop.rs similarity index 100% rename from src/movegen/bishop.rs rename to src/movegen/naive/bishop.rs diff --git a/src/movegen/king.rs b/src/movegen/naive/king.rs similarity index 100% rename from src/movegen/king.rs rename to src/movegen/naive/king.rs diff --git a/src/movegen/knight.rs b/src/movegen/naive/knight.rs similarity index 100% rename from src/movegen/knight.rs rename to src/movegen/naive/knight.rs diff --git a/src/movegen/naive/mod.rs b/src/movegen/naive/mod.rs new file mode 100644 index 0000000..f1bbe3e --- /dev/null +++ b/src/movegen/naive/mod.rs @@ -0,0 +1,5 @@ +pub mod bishop; +pub mod king; +pub mod knight; +pub mod pawn; +pub mod rook; diff --git a/src/movegen/pawn.rs b/src/movegen/naive/pawn.rs similarity index 100% rename from src/movegen/pawn.rs rename to src/movegen/naive/pawn.rs diff --git a/src/movegen/rook.rs b/src/movegen/naive/rook.rs similarity index 100% rename from src/movegen/rook.rs rename to src/movegen/naive/rook.rs diff --git a/src/movegen/wizardry/generation.rs b/src/movegen/wizardry/generation.rs index ee75d57..173eef0 100644 --- a/src/movegen/wizardry/generation.rs +++ b/src/movegen/wizardry/generation.rs @@ -1,6 +1,5 @@ use crate::board::{Bitboard, Square}; -use crate::movegen::bishop::bishop_moves; -use crate::movegen::rook::rook_moves; +use crate::movegen::naive::{bishop::bishop_moves, rook::rook_moves}; use crate::movegen::Magic; use super::mask::{generate_bishop_mask, generate_rook_mask}; diff --git a/src/movegen/wizardry/mask.rs b/src/movegen/wizardry/mask.rs index 0c0bbdf..eed93a0 100644 --- a/src/movegen/wizardry/mask.rs +++ b/src/movegen/wizardry/mask.rs @@ -1,6 +1,5 @@ use crate::board::{Bitboard, File, Rank, Square}; -use crate::movegen::bishop::bishop_moves; -use crate::movegen::rook::rook_moves; +use crate::movegen::naive::{bishop::bishop_moves, rook::rook_moves}; pub fn generate_bishop_mask(square: Square) -> Bitboard { let rays = bishop_moves(square, Bitboard::EMPTY); From 6cd5f222def8ab5440fae334f2a54bebb8db9468 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sun, 24 Jul 2022 16:31:26 +0200 Subject: [PATCH 12/14] Make use of generated move tables --- src/build.rs | 3 ++ src/movegen/magic/mod.rs | 5 +++ src/movegen/magic/moves.rs | 71 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 src/movegen/magic/moves.rs diff --git a/src/build.rs b/src/build.rs index e05410d..29265f5 100644 --- a/src/build.rs +++ b/src/build.rs @@ -131,5 +131,8 @@ fn main() -> Result<()> { print_boards(&mut out, "QUEEN_SIDE_CASTLE_BLOCKERS", &queen_blockers)?; } + // Include the generated files now that the build script has run. + println!("cargo:rustc-cfg=generated_boards"); + Ok(()) } diff --git a/src/movegen/magic/mod.rs b/src/movegen/magic/mod.rs index f59bde2..b31abeb 100644 --- a/src/movegen/magic/mod.rs +++ b/src/movegen/magic/mod.rs @@ -19,3 +19,8 @@ impl Magic { base_index + self.offset } } + +#[cfg(generated_boards)] +mod moves; +#[cfg(generated_boards)] +pub use moves::*; diff --git a/src/movegen/magic/moves.rs b/src/movegen/magic/moves.rs new file mode 100644 index 0000000..2901b28 --- /dev/null +++ b/src/movegen/magic/moves.rs @@ -0,0 +1,71 @@ +use super::Magic; +use crate::board::{Bitboard, Color, Square}; + +include!(concat!(env!("OUT_DIR"), "/magic_tables.rs")); + +pub fn quiet_pawn_moves(color: Color, square: Square, blockers: Bitboard) -> Bitboard { + // If there is a piece in front of the pawn, it can't advance + if !(color.backward_direction().move_board(blockers) & square).is_empty() { + return Bitboard::EMPTY; + } + // SAFETY: we know the values are in-bounds + unsafe { + *PAWN_MOVES + .get_unchecked(color.index()) + .get_unchecked(square.index()) + } +} + +pub fn pawn_moves(color: Color, square: Square, blockers: Bitboard) -> Bitboard { + // SAFETY: we know the values are in-bounds + let attacks = unsafe { + *PAWN_ATTACKS + .get_unchecked(color.index()) + .get_unchecked(square.index()) + }; + quiet_pawn_moves(color, square, blockers) | attacks +} + +pub fn knight_moves(square: Square) -> Bitboard { + // SAFETY: we know the values are in-bounds + unsafe { *KNIGHT_MOVES.get_unchecked(square.index()) } +} + +pub fn bishop_moves(square: Square, blockers: Bitboard) -> Bitboard { + // SAFETY: we know the values are in-bounds + unsafe { + let index = BISHOP_MAGICS + .get_unchecked(square.index()) + .get_index(blockers); + *BISHOP_MOVES.get_unchecked(index) + } +} + +pub fn rook_moves(square: Square, blockers: Bitboard) -> Bitboard { + // SAFETY: we know the values are in-bounds + unsafe { + let index = ROOK_MAGICS + .get_unchecked(square.index()) + .get_index(blockers); + *ROOK_MOVES.get_unchecked(index) + } +} + +pub fn queen_moves(square: Square, blockers: Bitboard) -> Bitboard { + bishop_moves(square, blockers) | rook_moves(square, blockers) +} + +pub fn king_moves(square: Square) -> Bitboard { + // SAFETY: we know the values are in-bounds + unsafe { *KING_MOVES.get_unchecked(square.index()) } +} + +pub fn king_side_castle_blockers(color: Color) -> Bitboard { + // SAFETY: we know the values are in-bounds + unsafe { *KING_SIDE_CASTLE_BLOCKERS.get_unchecked(color.index()) } +} + +pub fn queen_side_castle_blockers(color: Color) -> Bitboard { + // SAFETY: we know the values are in-bounds + unsafe { *QUEEN_SIDE_CASTLE_BLOCKERS.get_unchecked(color.index()) } +} From daaeb70bf46bc7a00a07ed9667da4f79fac36871 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sun, 24 Jul 2022 16:33:06 +0200 Subject: [PATCH 13/14] Add 'rerun-if-changed' directives to build script --- src/build.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/build.rs b/src/build.rs index 29265f5..10d7a94 100644 --- a/src/build.rs +++ b/src/build.rs @@ -134,5 +134,10 @@ fn main() -> Result<()> { // Include the generated files now that the build script has run. println!("cargo:rustc-cfg=generated_boards"); + // Run the build script only if something in move generation might have changed. + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=movegen/naive/"); + println!("cargo:rerun-if-changed=movegen/wizardry/"); + Ok(()) } From bb728048135c08d8c9569210ee822845ae52c60d Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sun, 24 Jul 2022 16:43:18 +0200 Subject: [PATCH 14/14] Remove all useless 'allow(unused)' --- src/movegen/naive/bishop.rs | 1 - src/movegen/naive/king.rs | 2 -- src/movegen/naive/knight.rs | 1 - src/movegen/naive/pawn.rs | 3 --- src/movegen/naive/rook.rs | 1 - 5 files changed, 8 deletions(-) diff --git a/src/movegen/naive/bishop.rs b/src/movegen/naive/bishop.rs index 0fe0247..0806077 100644 --- a/src/movegen/naive/bishop.rs +++ b/src/movegen/naive/bishop.rs @@ -1,6 +1,5 @@ use crate::board::{Bitboard, Direction, Square}; -#[allow(unused)] pub fn bishop_moves(square: Square, blockers: Bitboard) -> Bitboard { Direction::iter_bishop() .map(|dir| dir.slide_board_with_blockers(square.into_bitboard(), blockers)) diff --git a/src/movegen/naive/king.rs b/src/movegen/naive/king.rs index 932b4f4..6e98df7 100644 --- a/src/movegen/naive/king.rs +++ b/src/movegen/naive/king.rs @@ -1,7 +1,6 @@ use crate::board::{Bitboard, CastleRights, Color, Direction, File, Square}; // No castling moves included -#[allow(unused)] pub fn king_moves(square: Square) -> Bitboard { let board = square.into_bitboard(); @@ -10,7 +9,6 @@ pub fn king_moves(square: Square) -> Bitboard { .fold(Bitboard::EMPTY, |lhs, rhs| lhs | rhs) } -#[allow(unused)] pub fn king_castling_moves(color: Color, castle_rights: CastleRights) -> Bitboard { let rank = color.first_rank(); diff --git a/src/movegen/naive/knight.rs b/src/movegen/naive/knight.rs index 4783bde..f850d71 100644 --- a/src/movegen/naive/knight.rs +++ b/src/movegen/naive/knight.rs @@ -1,6 +1,5 @@ use crate::board::{Bitboard, Direction, Square}; -#[allow(unused)] pub fn knight_moves(square: Square) -> Bitboard { let board = square.into_bitboard(); diff --git a/src/movegen/naive/pawn.rs b/src/movegen/naive/pawn.rs index 5c929fa..55b5bf6 100644 --- a/src/movegen/naive/pawn.rs +++ b/src/movegen/naive/pawn.rs @@ -1,6 +1,5 @@ use crate::board::{Bitboard, Color, Direction, Rank, Square}; -#[allow(unused)] pub fn pawn_moves(color: Color, square: Square, blockers: Bitboard) -> Bitboard { if (square.rank() == Rank::First) || (square.rank() == Rank::Eighth) { return Bitboard::EMPTY; @@ -22,7 +21,6 @@ pub fn pawn_moves(color: Color, square: Square, blockers: Bitboard) -> Bitboard } } -#[allow(unused)] pub fn pawn_captures(color: Color, square: Square) -> Bitboard { if (square.rank() == Rank::First) || (square.rank() == Rank::Eighth) { return Bitboard::EMPTY; @@ -38,7 +36,6 @@ pub fn pawn_captures(color: Color, square: Square) -> Bitboard { attack_west | attack_east } -#[allow(unused)] pub fn en_passant_origins(square: Square) -> Bitboard { let board = square.into_bitboard(); diff --git a/src/movegen/naive/rook.rs b/src/movegen/naive/rook.rs index 31fd7d8..0b06cef 100644 --- a/src/movegen/naive/rook.rs +++ b/src/movegen/naive/rook.rs @@ -1,6 +1,5 @@ use crate::board::{Bitboard, Direction, Square}; -#[allow(unused)] pub fn rook_moves(square: Square, blockers: Bitboard) -> Bitboard { Direction::iter_rook() .map(|dir| dir.slide_board_with_blockers(square.into_bitboard(), blockers))