Compare commits
14 commits
ea686aa95f
...
874abbf82f
Author | SHA1 | Date | |
---|---|---|---|
Bruno BELANYI | 874abbf82f | ||
Bruno BELANYI | 48819de868 | ||
Bruno BELANYI | ac8887c11a | ||
Bruno BELANYI | 44a67cf79a | ||
Bruno BELANYI | 0a635971dc | ||
Bruno BELANYI | 917b74b376 | ||
Bruno BELANYI | f136466359 | ||
Bruno BELANYI | 23ec5f71cd | ||
Bruno BELANYI | 7dbe48ad23 | ||
Bruno BELANYI | 2eb7e4c8ef | ||
Bruno BELANYI | 1a0aa5fddb | ||
Bruno BELANYI | a04b1f3a42 | ||
Bruno BELANYI | 3f00c6d1fc | ||
Bruno BELANYI | 5bee69c38e |
|
@ -8,6 +8,10 @@ steps:
|
|||
commands:
|
||||
- nix develop . --command pre-commit run --all
|
||||
|
||||
- name: flake check
|
||||
commands:
|
||||
- nix flake check
|
||||
|
||||
- name: package check
|
||||
commands:
|
||||
- nix build
|
||||
|
|
11
Cargo.toml
11
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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<Item = Self> {
|
||||
Self::ALL.iter().cloned()
|
||||
}
|
||||
|
||||
/// Convert from a piece index into a [Color] type.
|
||||
#[inline(always)]
|
||||
pub fn from_index(index: usize) -> Self {
|
||||
|
@ -20,6 +27,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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -109,6 +113,7 @@ impl std::ops::Shl<usize> 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 +124,7 @@ impl std::ops::Shr<usize> for Square {
|
|||
|
||||
#[inline(always)]
|
||||
fn shr(self, rhs: usize) -> Self::Output {
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
Square::from_index(self as usize - rhs)
|
||||
}
|
||||
}
|
||||
|
|
143
src/build.rs
Normal file
143
src/build.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
use std::io::{Result, Write};
|
||||
|
||||
pub mod board;
|
||||
pub mod movegen;
|
||||
pub mod utils;
|
||||
|
||||
use crate::{
|
||||
board::{Bitboard, Color, File, Square},
|
||||
movegen::{
|
||||
naive::{
|
||||
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)?;
|
||||
}
|
||||
|
||||
// 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(())
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
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.
|
||||
magic: u64,
|
||||
/// Base offset into the magic square table.
|
||||
offset: usize,
|
||||
/// Mask to apply to the blocker board before applying the magic.
|
||||
mask: Bitboard,
|
||||
/// Length of the resulting mask after applying the magic.
|
||||
shift: u8,
|
||||
}
|
||||
|
||||
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;
|
||||
base_index + self.offset
|
||||
}
|
||||
}
|
|
@ -1,2 +1,26 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(generated_boards)]
|
||||
mod moves;
|
||||
#[cfg(generated_boards)]
|
||||
pub use moves::*;
|
||||
|
|
71
src/movegen/magic/moves.rs
Normal file
71
src/movegen/magic/moves.rs
Normal file
|
@ -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()) }
|
||||
}
|
|
@ -2,12 +2,8 @@
|
|||
pub mod magic;
|
||||
pub use magic::*;
|
||||
|
||||
// Move generation implementation details
|
||||
mod bishop;
|
||||
mod king;
|
||||
mod knight;
|
||||
mod pawn;
|
||||
mod rook;
|
||||
// Naive move generation
|
||||
pub mod naive;
|
||||
|
||||
// Magic bitboard generation
|
||||
mod wizardry;
|
||||
pub(crate) mod wizardry;
|
||||
|
|
|
@ -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))
|
|
@ -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();
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
use crate::board::{Bitboard, Direction, Square};
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn knight_moves(square: Square) -> Bitboard {
|
||||
let board = square.into_bitboard();
|
||||
|
5
src/movegen/naive/mod.rs
Normal file
5
src/movegen/naive/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub mod bishop;
|
||||
pub mod king;
|
||||
pub mod knight;
|
||||
pub mod pawn;
|
||||
pub mod rook;
|
|
@ -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();
|
||||
|
|
@ -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))
|
67
src/movegen/wizardry/generation.rs
Normal file
67
src/movegen/wizardry/generation.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use crate::board::{Bitboard, Square};
|
||||
use crate::movegen::naive::{bishop::bishop_moves, rook::rook_moves};
|
||||
use crate::movegen::Magic;
|
||||
|
||||
use super::mask::{generate_bishop_mask, generate_rook_mask};
|
||||
|
||||
type MagicGenerationType = (Vec<Magic>, Vec<Bitboard>);
|
||||
|
||||
#[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 magics = Vec::new();
|
||||
let mut boards = Vec::new();
|
||||
|
||||
for square in Square::iter() {
|
||||
let mask = mask_fn(square);
|
||||
let mut candidate: Magic;
|
||||
|
||||
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: 0,
|
||||
mask,
|
||||
shift: (64 - mask.count()) as u8,
|
||||
};
|
||||
let mut candidate_moves = vec![Bitboard::EMPTY; occupancy_to_moves.len()];
|
||||
|
||||
for (occupancy, moves) in occupancy_to_moves.iter().cloned() {
|
||||
let index = candidate.get_index(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, boards)
|
||||
}
|
||||
|
||||
fn magic_candidate(rng: &mut dyn random::Source) -> u64 {
|
||||
rng.read_u64() & rng.read_u64() & rng.read_u64()
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
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};
|
||||
|
||||
#[allow(unused)] // FIXME: remove once used
|
||||
pub fn generate_bishop_mask(square: Square) -> Bitboard {
|
||||
let rays = bishop_moves(square, Bitboard::EMPTY);
|
||||
|
||||
|
@ -14,7 +12,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);
|
||||
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
pub(crate) mod generation;
|
||||
mod mask;
|
||||
|
|
Loading…
Reference in a new issue