Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions src/backend/compile_time/gen_caches.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use crate::backend::compile_time::gen_caches_non_sliders::{
PawnMoveType, calculate_potential_moves_cache, generate_pawn_moves,
};
use crate::backend::compile_time::gen_caches_sliders::{PEXT_TABLE_SIZE, gen_cache_sliders};
use crate::backend::compile_time::generated::caches::{
CACHE_BISHOP_PEXT_INDEX, CACHE_BISHOP_PEXT_MASK, CACHE_CAPTURE_PAWN, CACHE_DOUBLE_PUSH_PAWN,
CACHE_KING, CACHE_KNIGHT, CACHE_PEXT_TABLE, CACHE_QUIET_PAWN, CACHE_ROOK_PEXT_INDEX,
CACHE_ROOK_PEXT_MASK,
};
use crate::backend::constants::{SIDES, SQUARES_AMOUNT};
use crate::backend::state::board::bitboard::BitBoard;
use crate::backend::state::piece::Piece;
use std::fs;

// ----------------------------------------
// READING
// ----------------------------------------

/// All of this gets generated at compile time, in the functions below.
/// At runtime, we only have to read the values.
/// It contains for each square, a bitboard with every square that the king could potentially move to set to 1.
/// Example:
/// This: `KING_MOVES[Square::new(0,0)]` returns at bitboard that looks like this:
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `X X _ _ _ _ _ _`
/// `_ X _ _ _ _ _ _`
/// At runtime we have to apply some further checks to this bitboard:
/// 1. Are some of these squares blocked by friendly pieces?
/// 2. Would this move put me in check etc...?
pub const KING_MOVES: [BitBoard; SQUARES_AMOUNT] = read_bb_cache(&CACHE_KING);
pub const KNIGHT_MOVES: [BitBoard; SQUARES_AMOUNT] = read_bb_cache(&CACHE_KNIGHT);

// Various pawn caches:
pub const PAWN_QUIET_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
read_2d_bb_cache(CACHE_QUIET_PAWN);
pub const PAWN_CAPTURE_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
read_2d_bb_cache(CACHE_CAPTURE_PAWN);
pub const PAWN_DOUBLE_PUSH_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
read_2d_bb_cache(CACHE_DOUBLE_PUSH_PAWN);

// Various slider caches:
pub const ROOK_PEXT_MASK: [BitBoard; SQUARES_AMOUNT] = read_bb_cache(&CACHE_ROOK_PEXT_MASK);
pub const ROOK_PEXT_INDEX: [usize; SQUARES_AMOUNT] = CACHE_ROOK_PEXT_INDEX;

pub const BISHOP_PEXT_MASK: [BitBoard; SQUARES_AMOUNT] = read_bb_cache(&CACHE_BISHOP_PEXT_MASK);
pub const BISHOP_PEXT_INDEX: [usize; SQUARES_AMOUNT] = CACHE_BISHOP_PEXT_INDEX;

pub static PEXT_TABLE: [BitBoard; PEXT_TABLE_SIZE] = read_bb_cache(&CACHE_PEXT_TABLE);

pub const fn read_bb_cache<const N: usize>(cache: &[u64; N]) -> [BitBoard; N] {
let mut potential_moves = [BitBoard::new(); N];

let mut square_index: usize = 0;
while square_index < N {
let value = cache[square_index];
let bb = BitBoard { value };
potential_moves[square_index] = bb;

square_index += 1;
}

potential_moves
}

pub const fn read_2d_bb_cache(
cache_2d: [[u64; SQUARES_AMOUNT]; SIDES],
) -> [[BitBoard; SQUARES_AMOUNT]; SIDES] {
let mut potential_moves = [[BitBoard::new(); SQUARES_AMOUNT]; SIDES];

let mut side_index: usize = 0;
while side_index < SIDES {
let cache = &cache_2d[side_index];

potential_moves[side_index] = read_bb_cache(cache);

side_index += 1;
}

potential_moves
}

// ----------------------------------------
// WRITING
// ----------------------------------------

const DIR_PATH: &str = "src/backend/compile_time/generated/";
pub fn write_caches() {
let king_moves = calculate_potential_moves_cache(Piece::King);
let king_moves = king_moves.map(|b| b.value);
let knight_moves = calculate_potential_moves_cache(Piece::Knight);
let knight_moves = knight_moves.map(|b| b.value);

let quiet_pawn_moves = generate_pawn_moves(PawnMoveType::Quiet);
let quiet_pawn_moves = quiet_pawn_moves.map(|a| a.map(|b| b.value));
let capture_pawn_moves = generate_pawn_moves(PawnMoveType::Capture);
let capture_pawn_moves = capture_pawn_moves.map(|a| a.map(|b| b.value));
let double_push_pawn_moves = generate_pawn_moves(PawnMoveType::DoublePush);
let double_push_pawn_moves = double_push_pawn_moves.map(|a| a.map(|b| b.value));

let pext_data = gen_cache_sliders();
let rook_pext_mask = pext_data.rook_pext_mask;
let rook_pext_mask = rook_pext_mask.map(|b| b.value);
let rook_pext_index = pext_data.rook_pext_index;
let bishop_pext_mask = pext_data.bishop_pext_mask;
let bishop_pext_mask = bishop_pext_mask.map(|b| b.value);
let bishop_pext_index = pext_data.bishop_pext_index;
let pext_table = pext_data.pext_table;
let pext_table = pext_table.map(|b| b.value);

let cache_strings = [
format!("pub const CACHE_KING: [u64; 64] = {:?};", king_moves),
format!("pub const CACHE_KNIGHT: [u64; 64] = {:?};", knight_moves),
format!(
"pub const CACHE_QUIET_PAWN: [[u64; 64]; 2] = {:?};",
quiet_pawn_moves
),
format!(
"pub const CACHE_CAPTURE_PAWN: [[u64; 64]; 2] = {:?};",
capture_pawn_moves
),
format!(
"pub const CACHE_DOUBLE_PUSH_PAWN: [[u64; 64]; 2] = {:?};",
double_push_pawn_moves
),
format!(
"pub const CACHE_ROOK_PEXT_MASK: [u64; 64] = {:?};",
rook_pext_mask
),
format!(
"pub const CACHE_ROOK_PEXT_INDEX: [usize; 64] = {:?};",
rook_pext_index
),
format!(
"pub const CACHE_BISHOP_PEXT_MASK: [u64; 64] = {:?};",
bishop_pext_mask
),
format!(
"pub const CACHE_BISHOP_PEXT_INDEX: [usize; 64] = {:?};",
bishop_pext_index
),
// TODO: const leads to inlining, static does not. Is this better in my case?
format!(
"pub static CACHE_PEXT_TABLE: [u64; 107648] = {:?};",
pext_table
),
];

let mut file = String::from("");
for string in cache_strings {
file.push_str("#[rustfmt::skip] \n");
file.push_str(&string);
file.push_str("\n\n");
}

fs::write(format!("{}caches.rs", DIR_PATH), file).unwrap();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,12 @@ use crate::backend::state::board::bitboard::BitBoard;
use crate::backend::state::piece::{Piece, Side};
use crate::backend::state::square::Square;

/// All of this gets generated at compile time, in the functions below.
/// At runtime, we only have to read the values.
/// It contains for each square, a bitboard with every square that the king could potentially move to set to 1.
/// Example:
/// This: `KING_MOVES[Square::new(0,0)]` returns at bitboard that looks like this:
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `_ _ _ _ _ _ _ _`
/// `X X _ _ _ _ _ _`
/// `_ X _ _ _ _ _ _`
/// At runtime we have to apply some further checks to this bitboard:
/// 1. Are some of these squares blocked by friendly pieces?
/// 2. Would this move put me in check etc...?
pub const KING_MOVES: [BitBoard; SQUARES_AMOUNT] = calculate_potential_moves_cache(Piece::King);

/// The same as above, but for the knight.
pub const KNIGHT_MOVES: [BitBoard; SQUARES_AMOUNT] = calculate_potential_moves_cache(Piece::Knight);

enum PawnMoveType {
pub enum PawnMoveType {
Quiet,
Capture,
DoublePush,
}

/// All quiet moves for pawns.
pub const PAWN_QUIET_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
generate_pawn_moves(PawnMoveType::Quiet);

/// All capture moves for pawns.
pub const PAWN_CAPTURE_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
generate_pawn_moves(PawnMoveType::Capture);

/// All capture moves for pawns.
pub const PAWN_DOUBLE_PUSH_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
generate_pawn_moves(PawnMoveType::DoublePush);

/// Initializes a collection of bitboards representing all possible moves for each square.
///
/// Since this function is const, it can be evaluated at compile time.
Expand All @@ -52,7 +19,7 @@ pub const PAWN_DOUBLE_PUSH_MOVES: [[BitBoard; SQUARES_AMOUNT]; SIDES] =
/// # Returns
/// An array of `BitBoard` of size `SQUARES_AMOUNT`, where each entry corresponds to the
/// possible moves for the square at the same index.
const fn calculate_potential_moves_cache(piece_type: Piece) -> [BitBoard; SQUARES_AMOUNT] {
pub fn calculate_potential_moves_cache(piece_type: Piece) -> [BitBoard; SQUARES_AMOUNT] {
let mut potential_moves = [BitBoard::new(); SQUARES_AMOUNT];

// iterate over all squares
Expand Down Expand Up @@ -81,7 +48,7 @@ const fn calculate_potential_moves_cache(piece_type: Piece) -> [BitBoard; SQUARE
///
/// # Returns
/// A `BitBoard` containing all valid surrounding squares a king can move to.
const fn generate_king_moves(square: Square) -> BitBoard {
fn generate_king_moves(square: Square) -> BitBoard {
let mut bitboard = BitBoard::new();

// since this fn is const, we can't use a loop
Expand Down Expand Up @@ -115,7 +82,7 @@ const fn generate_king_moves(square: Square) -> BitBoard {
}

/// Same as above but for the knight.
const fn generate_knight_moves(square: Square) -> BitBoard {
fn generate_knight_moves(square: Square) -> BitBoard {
let mut bitboard = BitBoard::new();

// The offsets for the knight moves. Starting in the top left corner.
Expand Down Expand Up @@ -148,7 +115,7 @@ const fn generate_knight_moves(square: Square) -> BitBoard {
bitboard
}

const fn generate_pawn_moves(pawn_move_type: PawnMoveType) -> [[BitBoard; SQUARES_AMOUNT]; SIDES] {
pub fn generate_pawn_moves(pawn_move_type: PawnMoveType) -> [[BitBoard; SQUARES_AMOUNT]; SIDES] {
let mut quiet_moves = [[BitBoard::new(); SQUARES_AMOUNT]; SIDES];

let mut side_index = 0;
Expand Down Expand Up @@ -193,14 +160,14 @@ const fn generate_pawn_moves(pawn_move_type: PawnMoveType) -> [[BitBoard; SQUARE
quiet_moves
}

const fn generate_pawn_quiet_moves(square: Square, bitboard: &mut BitBoard, active_color: Side) {
fn generate_pawn_quiet_moves(square: Square, bitboard: &mut BitBoard, active_color: Side) {
let forward_square = square.forward_by_one(active_color);
if forward_square.is_valid() {
bitboard.fill_square(forward_square);
}
}

const fn generate_pawn_attack_moves(square: Square, bitboard: &mut BitBoard, active_color: Side) {
fn generate_pawn_attack_moves(square: Square, bitboard: &mut BitBoard, active_color: Side) {
let right_diagonal_square = square.right_by_one().forward_by_one(active_color);
let left_diagonal_square = square.left_by_one().forward_by_one(active_color);

Expand All @@ -212,11 +179,7 @@ const fn generate_pawn_attack_moves(square: Square, bitboard: &mut BitBoard, act
}
}

const fn generate_pawn_double_push_moves(
square: Square,
bitboard: &mut BitBoard,
active_color: Side,
) {
fn generate_pawn_double_push_moves(square: Square, bitboard: &mut BitBoard, active_color: Side) {
if !square.is_pawn_start(active_color) {
return;
}
Expand Down
Loading