Files
zeal/src/board.rs

276 lines
8.4 KiB
Rust

use u64 as Bitboard;
use crate::attack::{
fetch_bishop_attacks, fetch_king_attacks, fetch_knight_attacks, fetch_pawn_attacks,
fetch_queen_attacks, fetch_rook_attacks,
};
use crate::bitboard::have_common_bit;
use crate::movegen::{
bishop_pseudo_moves, king_pseudo_moves, knight_pseudo_moves, pawn_pseudo_moves,
queen_pseudo_moves, rook_pseudo_moves,
};
use crate::r#move::Move;
use crate::state::State;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Board {
pub white_pieces: [Piece; 6],
pub black_pieces: [Piece; 6],
pub state: State,
}
impl Board {
pub const fn new() -> Self {
Self {
white_pieces: [
Piece::new(0xff00, Kind::Pawn, Color::White),
Piece::new(0x42, Kind::Knight, Color::White),
Piece::new(0x24, Kind::Bishop, Color::White),
Piece::new(0x81, Kind::Rook, Color::White),
Piece::new(0x8, Kind::Queen, Color::White),
Piece::new(0x10, Kind::King, Color::White),
],
black_pieces: [
Piece::new(0xff000000000000, Kind::Pawn, Color::Black),
Piece::new(0x4200000000000000, Kind::Knight, Color::Black),
Piece::new(0x2400000000000000, Kind::Bishop, Color::Black),
Piece::new(0x8100000000000000, Kind::Rook, Color::Black),
Piece::new(0x800000000000000, Kind::Queen, Color::Black),
Piece::new(0x1000000000000000, Kind::King, Color::Black),
],
state: State::new(),
}
}
pub const fn empty_board() -> Self {
Self {
white_pieces: [
Piece::new(0x0, Kind::Pawn, Color::White),
Piece::new(0x0, Kind::Knight, Color::White),
Piece::new(0x0, Kind::Bishop, Color::White),
Piece::new(0x0, Kind::Rook, Color::White),
Piece::new(0x0, Kind::Queen, Color::White),
Piece::new(0x0, Kind::King, Color::White),
],
black_pieces: [
Piece::new(0x0, Kind::Pawn, Color::Black),
Piece::new(0x0, Kind::Knight, Color::Black),
Piece::new(0x0, Kind::Bishop, Color::Black),
Piece::new(0x0, Kind::Rook, Color::Black),
Piece::new(0x0, Kind::Queen, Color::Black),
Piece::new(0x0, Kind::King, Color::Black),
],
state: State::new(),
}
}
pub fn white_occupancies(&self) -> Bitboard {
self.white_pieces.iter().fold(0, |acc, p| p.bitboard | acc)
}
pub fn black_occupancies(&self) -> Bitboard {
self.black_pieces.iter().fold(0, |acc, p| p.bitboard | acc)
}
pub fn all_occupancies(&self) -> Bitboard {
self.white_occupancies() | self.black_occupancies()
}
pub fn is_attacked(&self, square: usize, opponent_color: Color) -> bool {
let all_occupancies = self.all_occupancies();
let (opponent, own_color) = match opponent_color {
Color::Black => (&self.black_pieces, Color::White),
Color::White => (&self.white_pieces, Color::Black),
};
have_common_bit(
opponent[Kind::Pawn].bitboard,
fetch_pawn_attacks(square, own_color),
) || have_common_bit(
opponent[Kind::Knight].bitboard,
fetch_knight_attacks(square),
) || have_common_bit(
opponent[Kind::Bishop].bitboard,
fetch_bishop_attacks(all_occupancies, square),
) || have_common_bit(
opponent[Kind::Rook].bitboard,
fetch_rook_attacks(all_occupancies, square),
) || have_common_bit(
opponent[Kind::Queen].bitboard,
fetch_queen_attacks(all_occupancies, square),
) || have_common_bit(opponent[Kind::King].bitboard, fetch_king_attacks(square))
}
pub fn is_move_legit(&self, square: usize, opponent_color: Color) -> bool {
!self.is_attacked(square, opponent_color)
}
pub fn pseudo_moves_all(&self, color: Color) -> Vec<Move> {
let mut moves = vec![];
moves.extend(self.pseudo_moves(color, Kind::Pawn));
moves.extend(self.pseudo_moves(color, Kind::Knight));
moves.extend(self.pseudo_moves(color, Kind::Bishop));
moves.extend(self.pseudo_moves(color, Kind::Rook));
moves.extend(self.pseudo_moves(color, Kind::Queen));
moves.extend(self.pseudo_moves(color, Kind::King));
moves
}
pub fn pseudo_moves(&self, color: Color, kind: Kind) -> Vec<Move> {
let all_occupancies = self.all_occupancies();
let (pieces, opponent_occupancies, own_occupancies) = match color {
Color::White => (
self.white_pieces[kind].bitboard,
self.black_occupancies(),
self.white_occupancies(),
),
Color::Black => (
self.black_pieces[kind].bitboard,
self.white_occupancies(),
self.black_occupancies(),
),
};
match kind {
Kind::Pawn => pawn_pseudo_moves(
pieces,
all_occupancies,
opponent_occupancies,
self.state.en_passant_target_square(),
color,
),
Kind::Knight => knight_pseudo_moves(pieces, all_occupancies, own_occupancies),
Kind::Bishop => bishop_pseudo_moves(pieces, all_occupancies, own_occupancies),
Kind::Rook => rook_pseudo_moves(pieces, all_occupancies, own_occupancies),
Kind::Queen => queen_pseudo_moves(pieces, all_occupancies, own_occupancies),
Kind::King => king_pseudo_moves(pieces, all_occupancies, own_occupancies, self, color),
}
}
pub fn set_piece(&mut self, piece: Piece) {
match piece.color {
Color::Black => self.black_pieces[piece.kind].bitboard |= piece.bitboard,
Color::White => self.white_pieces[piece.kind].bitboard |= piece.bitboard,
};
}
pub fn set_state(&mut self, state: State) {
self.state = state;
}
}
impl Default for Board {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Piece {
pub bitboard: Bitboard,
pub kind: Kind,
pub color: Color,
}
impl Piece {
pub const fn new(bitboard: Bitboard, kind: Kind, color: Color) -> Self {
Self {
bitboard,
kind,
color,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Kind {
Pawn,
Knight,
Bishop,
Rook,
Queen,
King,
}
impl Kind {
fn idx(&self) -> usize {
match self {
Kind::Pawn => 0,
Kind::Knight => 1,
Kind::Bishop => 2,
Kind::Rook => 3,
Kind::Queen => 4,
Kind::King => 5,
}
}
}
use std::ops::{Index, IndexMut};
impl Index<Kind> for [Piece] {
type Output = Piece;
fn index(&self, kind: Kind) -> &Self::Output {
&self[kind.idx()]
}
}
impl IndexMut<Kind> for [Piece] {
fn index_mut(&mut self, kind: Kind) -> &mut Self::Output {
&mut self[kind.idx()]
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Color {
White,
Black,
}
impl Color {
pub const fn opponent_color(color: Self) -> Self {
match color {
Self::White => Self::Black,
Self::Black => Self::White,
}
}
}
#[cfg(test)]
mod tests {
use crate::{attack::init_attacks, fen::from_fen};
use super::*;
const FEN_EXAMPLE: [&str; 1] = ["8/6P1/4n2b/1p6/1Kp5/6n1/4b3/1k6 w - - 0 1"];
#[test]
fn test_occupancies() -> Result<(), String> {
let new_game = from_fen(FEN_EXAMPLE[0])?;
assert_eq!(new_game.board.white_occupancies(), 0x40000002000000);
assert_eq!(new_game.board.black_occupancies(), 0x900204401002);
assert_eq!(new_game.board.all_occupancies(), 0x40900206401002);
Ok(())
}
#[test]
fn test_is_attacked() -> Result<(), String> {
let new_game = from_fen(FEN_EXAMPLE[0])?;
init_attacks();
assert!(new_game.board.is_attacked(54, Color::Black));
Ok(())
}
#[test]
fn test_fifty_move_draw() -> Result<(), String> {
//TODO: make a MoveHistory/BoardHistory/GameHistory struct
Ok(())
}
#[test]
fn test_threefold_repetition() -> Result<(), String> {
//TODO: make a MoveHistory/BoardHistory/GameHistory struct
Ok(())
}
}