Compare commits

...

5 commits

Author SHA1 Message Date
7db4ec990e Remove all useless 'allow(unused)'
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-24 16:47:11 +02:00
36729a1131 Add 'rerun-if-changed' directives to build script 2022-07-24 16:47:11 +02:00
b348c89eee Make use of generated move tables 2022-07-24 16:34:12 +02:00
bf4c34554c Move naive move generation into sub-module 2022-07-24 16:34:12 +02:00
55b6dc72bb Generate magic tables with build script 2022-07-24 16:34:12 +02:00
13 changed files with 239 additions and 18 deletions

View file

@ -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

143
src/build.rs Normal file
View 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(())
}

View file

@ -19,3 +19,8 @@ impl Magic {
base_index + self.offset
}
}
#[cfg(generated_boards)]
mod moves;
#[cfg(generated_boards)]
pub use moves::*;

View 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()) }
}

View file

@ -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;

View file

@ -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))

View file

@ -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();

View file

@ -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
View file

@ -0,0 +1,5 @@
pub mod bishop;
pub mod king;
pub mod knight;
pub mod pawn;
pub mod rook;

View file

@ -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();

View file

@ -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))

View file

@ -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};

View file

@ -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);