eschac 0.6.2

computing chess moves
Documentation
mod factors;

use factors::*;

use crate::bitboard::*;
use crate::board::*;
use crate::util::*;

use alloc::vec::Vec;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Magic {
	pub premask: Bitboard,
	pub factor: u64,
	pub offset: isize,
}

#[allow(dead_code)]
pub(crate) fn build() -> (Vec<Magic>, Vec<u64>) {
	let mut magics: Vec<Magic> = Vec::new();
	let mut table: Vec<u64> = Vec::new();
	for (shr, factor_fn, premask_fn, dirs) in [
		(
			BISHOP_SHR,
			bishop_factor as fn(Square) -> u64,
			bishop_premask as fn(Square) -> Bitboard,
			[
				Direction::NorthEast,
				Direction::NorthWest,
				Direction::SouthWest,
				Direction::SouthEast,
			],
		),
		(
			ROOK_SHR,
			rook_factor as fn(Square) -> u64,
			rook_premask as fn(Square) -> Bitboard,
			[
				Direction::East,
				Direction::North,
				Direction::West,
				Direction::South,
			],
		),
	] {
		for square in Square::all() {
			let factor = factor_fn(square);
			let premask = premask_fn(square);
			let mut gap = isize::MAX;
			loop_subsets(!premask, |blockers| {
				gap = gap.min(hash(shr, factor, blockers | premask) as isize);
			});
			let offset = table.len() as isize - gap;
			loop_subsets(!premask, |blockers| {
				let idx = hash(shr, factor, blockers | premask) as isize + offset;
				let idx = idx as usize;
				while table.len() <= idx {
					table.push(0);
				}
				let mut value = 0;
				for dir in dirs {
					value |= blocked_ray(square, dir, blockers).0;
				}
				assert!(table[idx] == 0 || table[idx] == value);
				table[idx] = value;
			});
			magics.push(Magic {
				premask,
				factor,
				offset,
			});
		}
	}
	(magics, table)
}

#[inline]
fn loop_subsets<F>(premask: Bitboard, mut f: F)
where
	F: FnMut(Bitboard),
{
	let mut blockers: u64 = 0;
	loop {
		blockers = blockers.wrapping_sub(premask.0) & premask.0;
		f(Bitboard(blockers));
		if blockers == 0 {
			break;
		}
	}
}

#[inline]
const fn blocked_ray(square: Square, direction: Direction, blockers: Bitboard) -> Bitboard {
	let r = ray(square, direction);
	let blockers = Bitboard(blockers.0 & r.0);
	let square = if (direction as u8) < 4 {
		Bitboard::first(&blockers)
	} else {
		Bitboard::last(&blockers)
	};
	match square {
		None => r,
		Some(square) => Bitboard(r.0 & !ray(square, direction).0),
	}
}

fn bishop_premask(square: Square) -> Bitboard {
	let mut premask = Bitboard::new();
	for dir in [
		Direction::NorthEast,
		Direction::NorthWest,
		Direction::SouthWest,
		Direction::SouthEast,
	] {
		premask |= ray(square, dir);
	}
	premask &= !Rank::First.bitboard();
	premask &= !Rank::Eighth.bitboard();
	premask &= !File::A.bitboard();
	premask &= !File::H.bitboard();
	!premask
}

fn rook_premask(square: Square) -> Bitboard {
	let mut premask = Bitboard::new();
	premask |= ray(square, Direction::East) & !File::H.bitboard();
	premask |= ray(square, Direction::North) & !Rank::Eighth.bitboard();
	premask |= ray(square, Direction::West) & !File::A.bitboard();
	premask |= ray(square, Direction::South) & !Rank::First.bitboard();
	!premask
}