dama 0.1.0

A general-purpose rust library for standard chess and chess960.
Documentation
use dama_core::{magic, moves, square::Square, squareset::SquareSet};
use std::{
    array, env,
    fs::File,
    io::{BufWriter, Write},
    path::PathBuf,
};

fn main() -> anyhow::Result<()> {
    println!("cargo:rerun-if-changed=build.rs");

    let king_table: [_; 64] = array::from_fn(|n| moves::king(Square::from_index(n)));
    let knight_table: [_; 64] = array::from_fn(|n| moves::knight(Square::from_index(n)));
    let black_pawn_cap_table: [_; 64] =
        array::from_fn(|n| moves::black_pawn_captures(Square::from_index(n)));
    let white_pawn_cap_table: [_; 64] =
        array::from_fn(|n| moves::white_pawn_captures(Square::from_index(n)));
    let line_between_table: [[_; 64]; 64] = array::from_fn(|sa| {
        array::from_fn(|sb| between(Square::from_index(sa), Square::from_index(sb)))
    });
    let line_table: [[_; 64]; 64] = array::from_fn(|sa| {
        array::from_fn(|sb| ray(Square::from_index(sa), Square::from_index(sb)))
    });

    let mut sliding_table = vec![SquareSet::EMPTY; magic::SLIDING_TABLE_SIZE];

    Bishop::write(&mut sliding_table);
    Rook::write(&mut sliding_table);

    let path = PathBuf::from(env::var("OUT_DIR")?).join("squareset_tables.rs");
    let mut writer = BufWriter::new(File::create(path)?);

    write_square_table(&mut writer, "KING_MOVES", &king_table)?;
    write_square_table(&mut writer, "KNIGHT_MOVES", &knight_table)?;
    write_square_table(&mut writer, "WHITE_PAWN_CAPTURES", &white_pawn_cap_table)?;
    write_square_table(&mut writer, "BLACK_PAWN_CAPTURES", &black_pawn_cap_table)?;
    write_nested_table(&mut writer, "LINE_TABLE", &line_table)?;
    write_nested_table(&mut writer, "LINE_BETWEEN_TABLE", &line_between_table)?;
    write_sliding_table(&mut writer, &sliding_table)?;

    Ok(())
}

fn write_nested_table(
    w: &mut impl Write,
    name: &str,
    table: &[[SquareSet; 64]; 64],
) -> anyhow::Result<()> {
    writeln!(w, "#[rustfmt::skip]")?;
    writeln!(w, "static {}: [[u64; 64]; 64] = [", name)?;
    for row in table.iter() {
        write!(w, "    [")?;
        for &entry in row.iter() {
            write!(w, "0x{:x}, ", entry.to_bits())?;
        }
        writeln!(w, "],")?;
    }
    writeln!(w, "];")?;
    writeln!(w)?;
    Ok(())
}

fn write_square_table(
    w: &mut impl Write,
    name: &str,
    table: &[SquareSet; 64],
) -> anyhow::Result<()> {
    writeln!(w, "#[rustfmt::skip]")?;
    writeln!(w, "const {}: [u64; {}] = [", name, table.len())?;
    for entry in table.iter() {
        writeln!(w, "    0x{:x},", entry.to_bits())?;
    }
    writeln!(w, "];")?;
    writeln!(w)?;
    Ok(())
}

fn write_sliding_table(w: &mut impl Write, sliding_table: &[SquareSet]) -> anyhow::Result<()> {
    writeln!(w, "#[rustfmt::skip]")?;
    writeln!(
        w,
        "static SLIDING_TABLE: [u64; {}] = [",
        sliding_table.len()
    )?;
    for entry in sliding_table.iter() {
        writeln!(w, "    0x{:x},", entry.to_bits())?;
    }
    writeln!(w, "];")?;
    writeln!(w)?;
    Ok(())
}

#[inline]
fn between(a: Square, b: Square) -> SquareSet {
    let diagonal = moves::bishop(a, b.into());
    if diagonal.contains(b) {
        return diagonal & moves::bishop(b, a.into());
    }

    let orthogonal = moves::rook(a, b.into());
    if orthogonal.contains(b) {
        return orthogonal & moves::rook(b, a.into());
    }

    SquareSet::EMPTY
}

#[inline]
fn ray(a: Square, b: Square) -> SquareSet {
    let diagonal = moves::bishop(a, SquareSet::EMPTY).with(a);
    if diagonal.contains(b) {
        return diagonal & moves::bishop(b, SquareSet::EMPTY).with(b);
    }

    let orthogonal = moves::rook(a, SquareSet::EMPTY).with(a);
    if orthogonal.contains(b) {
        return orthogonal & moves::rook(b, SquareSet::EMPTY).with(b);
    }

    SquareSet::EMPTY
}

trait Slider {
    fn relevant_blockers(square: Square) -> SquareSet;
    fn moves(square: Square, occupied: SquareSet) -> SquareSet;
    fn index(square: Square, occupied: SquareSet) -> usize;

    fn write(table: &mut [SquareSet]) {
        for square in Square::all() {
            for occupied in Self::relevant_blockers(square).subsets() {
                table[Self::index(square, occupied)] = Self::moves(square, occupied);
            }
        }
    }
}

struct Rook;
struct Bishop;

impl Slider for Rook {
    #[inline]
    fn relevant_blockers(square: Square) -> SquareSet {
        magic::rook_relevant_blockers(square)
    }

    #[inline]
    fn index(square: Square, occupied: SquareSet) -> usize {
        magic::rook_table_index(square, occupied)
    }

    #[inline]
    fn moves(square: Square, occupied: SquareSet) -> SquareSet {
        moves::rook(square, occupied)
    }
}

impl Slider for Bishop {
    #[inline]
    fn relevant_blockers(square: Square) -> SquareSet {
        magic::bishop_relevant_blockers(square)
    }

    #[inline]
    fn index(square: Square, occupied: SquareSet) -> usize {
        magic::bishop_table_index(square, occupied)
    }

    #[inline]
    fn moves(square: Square, occupied: SquareSet) -> SquareSet {
        moves::bishop(square, occupied)
    }
}