Files
zeal/src/board/board.rs

288 lines
9.2 KiB
Rust

use strum_macros::EnumIter;
use u64 as Bitboard;
use crate::board::bitboard::{have_common_bit, lsb, square_to_bitboard};
use crate::board::state::State;
use crate::movegen::attack_generator::{
fetch_bishop_attacks, fetch_king_attacks, fetch_knight_attacks, fetch_pawn_attacks,
fetch_queen_attacks, fetch_rook_attacks,
};
use crate::movegen::move_generator::{
bishop_pseudo_moves, king_pseudo_moves, knight_pseudo_moves, pawn_pseudo_moves,
queen_pseudo_moves, rook_pseudo_moves,
};
use crate::movegen::r#move::{Move, MoveType, Promote};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Board {
pub pieces: [Bitboard; 6],
pub color: [Bitboard; 2],
pub state: State,
}
impl Board {
pub const fn startpos() -> Self {
Self {
pieces: [
0xff00000000ff00,
0x4200000000000042,
0x2400000000000024,
0x8100000000000081,
0x800000000000008,
0x1000000000000010,
],
color: [0xffff, 0xffff000000000000],
state: State::new(),
}
}
pub const fn new_empty() -> Self {
Self {
pieces: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
color: [0x0, 0x0],
state: State::new(),
}
}
pub fn is_attacked(&self, square: usize, opponent_color: Color) -> bool {
let all_occupancies = self.color[Color::White] | self.color[Color::Black];
let own_color = opponent_color.opponent();
let opponent_color_bb = &self.color[opponent_color];
let pawns = self.pieces[PieceType::Pawn] & opponent_color_bb;
let knights = self.pieces[PieceType::Knight] & opponent_color_bb;
let bishops = self.pieces[PieceType::Bishop] & opponent_color_bb;
let rooks = self.pieces[PieceType::Rook] & opponent_color_bb;
let queens = self.pieces[PieceType::Queen] & opponent_color_bb;
let king = self.pieces[PieceType::King] & opponent_color_bb;
have_common_bit(pawns, fetch_pawn_attacks(square, own_color))
|| have_common_bit(knights, fetch_knight_attacks(square))
|| have_common_bit(bishops, fetch_bishop_attacks(all_occupancies, square))
|| have_common_bit(rooks, fetch_rook_attacks(all_occupancies, square))
|| have_common_bit(queens, fetch_queen_attacks(all_occupancies, square))
|| have_common_bit(king, fetch_king_attacks(square))
}
pub fn king_under_check(&self, color: Color) -> bool {
let own_king_square = lsb(self.pieces[PieceType::King] & self.color[color]);
self.is_attacked(own_king_square, color.opponent())
}
pub fn pseudo_moves_all(&self) -> Vec<Move> {
let color = self.state.current_player();
let mut moves = vec![];
moves.extend(self.pseudo_moves(color, PieceType::Pawn));
moves.extend(self.pseudo_moves(color, PieceType::Knight));
moves.extend(self.pseudo_moves(color, PieceType::Bishop));
moves.extend(self.pseudo_moves(color, PieceType::Rook));
moves.extend(self.pseudo_moves(color, PieceType::Queen));
moves.extend(self.pseudo_moves(color, PieceType::King));
moves
}
pub fn pseudo_moves_all_captures(&self) -> Vec<Move> {
self.pseudo_moves_all()
.into_iter()
.filter(|m| {
matches!(
m.move_type,
MoveType::Capture | MoveType::PromotionCapture(_)
)
})
.collect()
}
pub fn pseudo_moves(&self, color: Color, piece_type: PieceType) -> Vec<Move> {
let all_occupancies = self.color[Color::White] | self.color[Color::Black];
let pieces = self.pieces[piece_type] & self.color[color];
let own_occupancies = self.color[color];
let opponent_occupancies = self.color[color.opponent()];
match piece_type {
PieceType::Pawn => pawn_pseudo_moves(
pieces,
all_occupancies,
opponent_occupancies,
self.state.en_passant_square(),
color,
),
PieceType::Knight => knight_pseudo_moves(pieces, all_occupancies, own_occupancies),
PieceType::Bishop => bishop_pseudo_moves(pieces, all_occupancies, own_occupancies),
PieceType::Rook => rook_pseudo_moves(pieces, all_occupancies, own_occupancies),
PieceType::Queen => queen_pseudo_moves(pieces, all_occupancies, own_occupancies),
PieceType::King => {
king_pseudo_moves(pieces, all_occupancies, own_occupancies, self, color)
}
}
}
pub fn set_piece(&mut self, bb: Bitboard, piece_type: PieceType, color: Color) {
self.pieces[piece_type] |= bb;
self.color[color] |= bb;
}
pub fn set_state(&mut self, state: State) {
self.state = state;
}
pub fn is_pawn_move(&self, square: usize) -> bool {
let side = self.state.current_player();
let own_pawns = self.pieces[PieceType::Pawn] & self.color[side];
have_common_bit(square_to_bitboard(square), own_pawns)
}
pub fn move_piece(&mut self, src: usize, dst: usize, piece_type: PieceType) {
self.pieces[piece_type] &= !square_to_bitboard(src);
self.pieces[piece_type] |= square_to_bitboard(dst);
self.color[self.state.current_player()] &= !square_to_bitboard(src);
self.color[self.state.current_player()] |= square_to_bitboard(dst);
}
pub fn insert_own_piece(&mut self, square: usize, piece_type: PieceType) {
self.pieces[piece_type] |= square_to_bitboard(square);
self.color[self.state.current_player()] |= square_to_bitboard(square);
}
pub fn insert_opponent_piece(&mut self, square: usize, piece_type: PieceType) {
self.pieces[piece_type] |= square_to_bitboard(square);
self.color[self.state.next_player()] |= square_to_bitboard(square);
}
pub fn remove_own_piece(&mut self, square: usize, piece_type: PieceType) {
self.pieces[piece_type] &= !square_to_bitboard(square);
self.color[self.state.current_player()] &= !square_to_bitboard(square);
}
pub fn remove_opponent_piece(&mut self, square: usize, piece_type: PieceType) {
self.pieces[piece_type] &= !square_to_bitboard(square);
self.color[self.state.next_player()] &= !square_to_bitboard(square);
}
pub fn promote_piece(&mut self, square: usize, promote: &Promote) {
match promote {
Promote::Knight => self.pieces[PieceType::Knight] |= square_to_bitboard(square),
Promote::Bishop => self.pieces[PieceType::Bishop] |= square_to_bitboard(square),
Promote::Rook => self.pieces[PieceType::Rook] |= square_to_bitboard(square),
Promote::Queen => self.pieces[PieceType::Queen] |= square_to_bitboard(square),
};
self.color[self.state.current_player()] |= square_to_bitboard(square);
}
}
impl Default for Board {
fn default() -> Self {
Self::new_empty()
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumIter)]
pub enum PieceType {
Pawn,
Knight,
Bishop,
Rook,
Queen,
King,
}
impl PieceType {
pub const fn idx(self) -> usize {
self as usize
}
pub const fn score(self) -> i32 {
match self {
Self::Pawn => 100,
Self::Knight => 320,
Self::Bishop => 330,
Self::Rook => 500,
Self::Queen => 900,
Self::King => 20000,
}
}
}
use std::ops::{Index, IndexMut};
impl<T> Index<PieceType> for [T] {
type Output = T;
fn index(&self, piece_type: PieceType) -> &Self::Output {
&self[piece_type.idx()]
}
}
impl<T> IndexMut<PieceType> for [T] {
fn index_mut(&mut self, piece_type: PieceType) -> &mut Self::Output {
&mut self[piece_type.idx()]
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumIter)]
pub enum Color {
White,
Black,
}
impl Color {
pub const fn idx(self) -> usize {
self as usize
}
pub const fn opponent(self) -> Self {
match self {
Self::White => Self::Black,
Self::Black => Self::White,
}
}
}
impl<T> Index<Color> for [T] {
type Output = T;
fn index(&self, color: Color) -> &Self::Output {
&self[color.idx()]
}
}
impl<T> IndexMut<Color> for [T] {
fn index_mut(&mut self, color: Color) -> &mut Self::Output {
&mut self[color.idx()]
}
}
#[cfg(test)]
mod tests {
use crate::{board::fen::from_fen, movegen::attack_generator::init_attacks};
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.color[Color::White], 0x40000002000000);
assert_eq!(new_game.board.color[Color::Black], 0x900204401002);
assert_eq!(
new_game.board.color[Color::White] | new_game.board.color[Color::Black],
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(())
}
}