Refactor project structure to use submodules
This commit is contained in:
34
src/board/bitboard.rs
Normal file
34
src/board/bitboard.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use u64 as Bitboard;
|
||||
|
||||
pub const EMPTY: Bitboard = 0x0;
|
||||
pub const FULL: Bitboard = 0xffffffffffffffff;
|
||||
pub const NOT_FILE_A: Bitboard = 0xfefefefefefefefe;
|
||||
pub const NOT_FILE_H: Bitboard = 0x7f7f7f7f7f7f7f7f;
|
||||
pub const NOT_FILE_AB: Bitboard = 0xfcfcfcfcfcfcfcfc;
|
||||
pub const NOT_FILE_GH: Bitboard = 0x3f3f3f3f3f3f3f3f;
|
||||
pub const RANK_1: Bitboard = 0xff;
|
||||
pub const RANK_2: Bitboard = 0xff00;
|
||||
pub const RANK_4: Bitboard = 0xff000000;
|
||||
pub const RANK_5: Bitboard = 0xff00000000;
|
||||
pub const RANK_7: Bitboard = 0xff000000000000;
|
||||
pub const RANK_8: Bitboard = 0xff00000000000000;
|
||||
|
||||
pub const fn square_to_bitboard(square: usize) -> Bitboard {
|
||||
1_u64 << square
|
||||
}
|
||||
|
||||
pub const fn square_to_bitboard_wrapping(square: usize) -> Bitboard {
|
||||
1_u64.wrapping_shl(square as u32)
|
||||
}
|
||||
|
||||
pub const fn lsb(bitboard: Bitboard) -> usize {
|
||||
bitboard.trailing_zeros() as usize
|
||||
}
|
||||
|
||||
pub const fn bit_count(bitboard: Bitboard) -> usize {
|
||||
bitboard.count_ones() as usize
|
||||
}
|
||||
|
||||
pub const fn have_common_bit(bitboard_a: Bitboard, bitboard_b: Bitboard) -> bool {
|
||||
bitboard_a & bitboard_b != 0
|
||||
}
|
||||
285
src/board/board.rs
Normal file
285
src/board/board.rs
Normal file
@@ -0,0 +1,285 @@
|
||||
use u64 as Bitboard;
|
||||
|
||||
use crate::board::bitboard::{have_common_bit, lsb};
|
||||
use crate::board::state::State;
|
||||
use crate::movegen::attack::{
|
||||
fetch_bishop_attacks, fetch_king_attacks, fetch_knight_attacks, fetch_pawn_attacks,
|
||||
fetch_queen_attacks, fetch_rook_attacks,
|
||||
};
|
||||
use crate::movegen::movegen::{
|
||||
bishop_pseudo_moves, king_pseudo_moves, knight_pseudo_moves, pawn_pseudo_moves,
|
||||
queen_pseudo_moves, rook_pseudo_moves,
|
||||
};
|
||||
use crate::movegen::r#move::Move;
|
||||
|
||||
#[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, PieceType::Pawn, Color::White),
|
||||
Piece::new(0x42, PieceType::Knight, Color::White),
|
||||
Piece::new(0x24, PieceType::Bishop, Color::White),
|
||||
Piece::new(0x81, PieceType::Rook, Color::White),
|
||||
Piece::new(0x8, PieceType::Queen, Color::White),
|
||||
Piece::new(0x10, PieceType::King, Color::White),
|
||||
],
|
||||
black_pieces: [
|
||||
Piece::new(0xff000000000000, PieceType::Pawn, Color::Black),
|
||||
Piece::new(0x4200000000000000, PieceType::Knight, Color::Black),
|
||||
Piece::new(0x2400000000000000, PieceType::Bishop, Color::Black),
|
||||
Piece::new(0x8100000000000000, PieceType::Rook, Color::Black),
|
||||
Piece::new(0x800000000000000, PieceType::Queen, Color::Black),
|
||||
Piece::new(0x1000000000000000, PieceType::King, Color::Black),
|
||||
],
|
||||
state: State::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn empty_board() -> Self {
|
||||
Self {
|
||||
white_pieces: [
|
||||
Piece::new(0x0, PieceType::Pawn, Color::White),
|
||||
Piece::new(0x0, PieceType::Knight, Color::White),
|
||||
Piece::new(0x0, PieceType::Bishop, Color::White),
|
||||
Piece::new(0x0, PieceType::Rook, Color::White),
|
||||
Piece::new(0x0, PieceType::Queen, Color::White),
|
||||
Piece::new(0x0, PieceType::King, Color::White),
|
||||
],
|
||||
black_pieces: [
|
||||
Piece::new(0x0, PieceType::Pawn, Color::Black),
|
||||
Piece::new(0x0, PieceType::Knight, Color::Black),
|
||||
Piece::new(0x0, PieceType::Bishop, Color::Black),
|
||||
Piece::new(0x0, PieceType::Rook, Color::Black),
|
||||
Piece::new(0x0, PieceType::Queen, Color::Black),
|
||||
Piece::new(0x0, PieceType::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[PieceType::Pawn].bitboard,
|
||||
fetch_pawn_attacks(square, own_color),
|
||||
) || have_common_bit(
|
||||
opponent[PieceType::Knight].bitboard,
|
||||
fetch_knight_attacks(square),
|
||||
) || have_common_bit(
|
||||
opponent[PieceType::Bishop].bitboard,
|
||||
fetch_bishop_attacks(all_occupancies, square),
|
||||
) || have_common_bit(
|
||||
opponent[PieceType::Rook].bitboard,
|
||||
fetch_rook_attacks(all_occupancies, square),
|
||||
) || have_common_bit(
|
||||
opponent[PieceType::Queen].bitboard,
|
||||
fetch_queen_attacks(all_occupancies, square),
|
||||
) || have_common_bit(
|
||||
opponent[PieceType::King].bitboard,
|
||||
fetch_king_attacks(square),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn king_under_check(&self, color: Color) -> bool {
|
||||
let own_king_square = match color {
|
||||
Color::White => lsb(self.white_pieces[PieceType::King].bitboard),
|
||||
Color::Black => lsb(self.black_pieces[PieceType::King].bitboard),
|
||||
};
|
||||
|
||||
self.is_attacked(own_king_square, Color::opponent_color(color))
|
||||
}
|
||||
|
||||
pub fn pseudo_moves_all(&self, color: Color) -> Vec<Move> {
|
||||
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(&self, color: Color, piece_type: PieceType) -> Vec<Move> {
|
||||
let all_occupancies = self.all_occupancies();
|
||||
let (pieces, opponent_occupancies, own_occupancies) = match color {
|
||||
Color::White => (
|
||||
self.white_pieces[piece_type].bitboard,
|
||||
self.black_occupancies(),
|
||||
self.white_occupancies(),
|
||||
),
|
||||
Color::Black => (
|
||||
self.black_pieces[piece_type].bitboard,
|
||||
self.white_occupancies(),
|
||||
self.black_occupancies(),
|
||||
),
|
||||
};
|
||||
|
||||
match piece_type {
|
||||
PieceType::Pawn => pawn_pseudo_moves(
|
||||
pieces,
|
||||
all_occupancies,
|
||||
opponent_occupancies,
|
||||
self.state.en_passant_target_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, piece: Piece) {
|
||||
match piece.color {
|
||||
Color::Black => self.black_pieces[piece.piece_type].bitboard |= piece.bitboard,
|
||||
Color::White => self.white_pieces[piece.piece_type].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 piece_type: PieceType,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
impl Piece {
|
||||
pub const fn new(bitboard: Bitboard, piece_type: PieceType, color: Color) -> Self {
|
||||
Self {
|
||||
bitboard,
|
||||
piece_type,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum PieceType {
|
||||
Pawn,
|
||||
Knight,
|
||||
Bishop,
|
||||
Rook,
|
||||
Queen,
|
||||
King,
|
||||
}
|
||||
|
||||
impl PieceType {
|
||||
const fn idx(&self) -> usize {
|
||||
match self {
|
||||
Self::Pawn => 0,
|
||||
Self::Knight => 1,
|
||||
Self::Bishop => 2,
|
||||
Self::Rook => 3,
|
||||
Self::Queen => 4,
|
||||
Self::King => 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
impl Index<PieceType> for [Piece] {
|
||||
type Output = Piece;
|
||||
|
||||
fn index(&self, piece_type: PieceType) -> &Self::Output {
|
||||
&self[piece_type.idx()]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<PieceType> for [Piece] {
|
||||
fn index_mut(&mut self, piece_type: PieceType) -> &mut Self::Output {
|
||||
&mut self[piece_type.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::{board::fen::from_fen, movegen::attack::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.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(())
|
||||
}
|
||||
}
|
||||
204
src/board/fen.rs
Normal file
204
src/board/fen.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
use crate::board::board::{Board, Color, Piece, PieceType};
|
||||
use crate::board::game::Game;
|
||||
use crate::board::state::{Castle, State};
|
||||
use String as FenError;
|
||||
|
||||
pub fn from_fen(fen: &str) -> Result<Game, FenError> {
|
||||
let fen_parts: Vec<_> = fen.split_whitespace().collect();
|
||||
|
||||
let mut board = piece_placement(fen_parts[0])?;
|
||||
|
||||
let side_to_move = side_to_move(fen_parts[1])?;
|
||||
|
||||
let castling_ability = castling_ability(fen_parts[2])?;
|
||||
|
||||
let en_passant_target_square = en_passant_target_square(fen_parts[3])?;
|
||||
|
||||
let halfmove_clock = halfmove_clock(fen_parts[4])?;
|
||||
|
||||
let fullmove_counter = fullmove_counter(fen_parts[5])?;
|
||||
|
||||
board.set_state(State::load_state(
|
||||
side_to_move,
|
||||
castling_ability,
|
||||
en_passant_target_square,
|
||||
halfmove_clock,
|
||||
fullmove_counter,
|
||||
));
|
||||
Ok(Game { board })
|
||||
}
|
||||
|
||||
pub fn piece_placement(pieces: &str) -> Result<Board, FenError> {
|
||||
let mut board = Board::empty_board();
|
||||
let (mut file, mut rank): (u8, u8) = (0, 7);
|
||||
|
||||
for c in pieces.chars() {
|
||||
let square = rank * 8 + file;
|
||||
let color = if c.is_ascii_lowercase() {
|
||||
Color::Black
|
||||
} else {
|
||||
Color::White
|
||||
};
|
||||
|
||||
if let Some(piece_type) = match c.to_ascii_lowercase() {
|
||||
'r' => Some(PieceType::Rook),
|
||||
'n' => Some(PieceType::Knight),
|
||||
'b' => Some(PieceType::Bishop),
|
||||
'q' => Some(PieceType::Queen),
|
||||
'k' => Some(PieceType::King),
|
||||
'p' => Some(PieceType::Pawn),
|
||||
'/' => {
|
||||
file = 0;
|
||||
rank = rank.saturating_sub(1);
|
||||
continue;
|
||||
}
|
||||
n if n.is_numeric() => {
|
||||
file += n.to_digit(10).unwrap_or(0) as u8;
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
return Err(FenError::from(
|
||||
"Error while parsing board piece state from FEN",
|
||||
))
|
||||
}
|
||||
} {
|
||||
board.set_piece(Piece::new(1 << square, piece_type, color));
|
||||
file += 1;
|
||||
};
|
||||
}
|
||||
Ok(board)
|
||||
}
|
||||
|
||||
fn side_to_move(side: &str) -> Result<Color, FenError> {
|
||||
match side {
|
||||
"w" => Ok(Color::White),
|
||||
"b" => Ok(Color::Black),
|
||||
_ => Err(FenError::from(
|
||||
"Found invalid character while parsing in side_to_move",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn castling_ability(castling: &str) -> Result<[Castle; 2], FenError> {
|
||||
let mut bitflag = 0b0;
|
||||
for c in castling.chars() {
|
||||
match c {
|
||||
'K' => bitflag |= 0b1000,
|
||||
'Q' => bitflag |= 0b100,
|
||||
'k' => bitflag |= 0b10,
|
||||
'q' => bitflag |= 0b1,
|
||||
'-' => break,
|
||||
_ => {
|
||||
return Err(FenError::from(
|
||||
"Found invalid character while parsing in castling_ability",
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut castling_ability: [Castle; 2] = [Castle::None, Castle::None];
|
||||
|
||||
let white_king_and_queen = (bitflag >> 2) & 0b11 == 0b11;
|
||||
let white_king = (bitflag >> 3) & 1 == 1;
|
||||
let white_queen = (bitflag >> 2) & 1 == 1;
|
||||
castling_ability[0] = match (white_king_and_queen, white_king, white_queen) {
|
||||
(true, _, _) => Castle::Both,
|
||||
(_, true, _) => Castle::Short,
|
||||
(_, _, true) => Castle::Long,
|
||||
_ => Castle::None,
|
||||
};
|
||||
|
||||
let black_king_and_queen = bitflag & 0b11 == 0b11;
|
||||
let black_king = (bitflag >> 1) & 1 == 1;
|
||||
let black_queen = bitflag & 1 == 1;
|
||||
castling_ability[1] = match (black_king_and_queen, black_king, black_queen) {
|
||||
(true, _, _) => Castle::Both,
|
||||
(_, true, _) => Castle::Short,
|
||||
(_, _, true) => Castle::Long,
|
||||
_ => Castle::None,
|
||||
};
|
||||
|
||||
Ok(castling_ability)
|
||||
}
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn en_passant_target_square(square: &str) -> Result<Option<usize>, FenError> {
|
||||
let mut sqr = square.chars();
|
||||
|
||||
let mut files: HashMap<char, usize> = HashMap::new();
|
||||
files.insert('a', 0);
|
||||
files.insert('b', 1);
|
||||
files.insert('c', 2);
|
||||
files.insert('d', 3);
|
||||
files.insert('e', 4);
|
||||
files.insert('f', 5);
|
||||
files.insert('g', 6);
|
||||
files.insert('h', 7);
|
||||
|
||||
let mut ranks: HashMap<char, usize> = HashMap::new();
|
||||
ranks.insert('3', 2);
|
||||
ranks.insert('6', 5);
|
||||
|
||||
match sqr.next() {
|
||||
Some(file) if files.contains_key(&file) => match sqr.next() {
|
||||
Some(rank) if ranks.contains_key(&rank) => Ok(Some(
|
||||
ranks.get(&rank).expect("Invalid rank") * 8
|
||||
+ files.get(&file).expect("Invalid file"),
|
||||
)),
|
||||
Some(_) | None => Err(FenError::from(
|
||||
"Not a valid rank (3 or 6) for an en passant target square",
|
||||
)),
|
||||
},
|
||||
Some('-') => Ok(None),
|
||||
Some(_) | None => Err(FenError::from("Not a file (a..h) or dash (-) character")),
|
||||
}
|
||||
}
|
||||
|
||||
fn halfmove_clock(halfmove: &str) -> Result<u8, FenError> {
|
||||
halfmove
|
||||
.parse::<u8>()
|
||||
.map_err(|_| FenError::from("Invalid halfmove_clock value"))
|
||||
}
|
||||
|
||||
fn fullmove_counter(fullmove: &str) -> Result<u8, FenError> {
|
||||
if "0".eq(fullmove) {
|
||||
Err(FenError::from(
|
||||
"Full move counter is not allowed to start with 0",
|
||||
))
|
||||
} else {
|
||||
fullmove
|
||||
.parse::<u8>()
|
||||
.map_err(|_| FenError::from("Invalid fullmove_counter value"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const FEN_EXAMPLE: [&str; 5] = [
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
|
||||
"rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2",
|
||||
"rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2 ",
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR x KQkq - 0 1",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_from_fen() -> Result<(), String> {
|
||||
let new_game = from_fen(FEN_EXAMPLE[0])?;
|
||||
assert_eq!(new_game, Game::new());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_fen_error() -> () {
|
||||
from_fen(FEN_EXAMPLE[4]).unwrap();
|
||||
}
|
||||
|
||||
//TODO: add more happy path scenarios
|
||||
//TODO: test each panic e.g. #[should_panic(expected = "less than or equal to 100")]
|
||||
}
|
||||
31
src/board/game.rs
Normal file
31
src/board/game.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use crate::board::fen::from_fen;
|
||||
use String as FenError;
|
||||
|
||||
use super::board::Board;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Game {
|
||||
pub board: Board,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
board: Board::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_fen(fen: &str) -> Result<Self, FenError> {
|
||||
from_fen(fen)
|
||||
}
|
||||
|
||||
pub const fn run(&self) {
|
||||
Board::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Game {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
6
src/board/mod.rs
Normal file
6
src/board/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
pub mod bitboard;
|
||||
pub mod board;
|
||||
pub mod fen;
|
||||
pub mod game;
|
||||
pub mod square;
|
||||
pub mod state;
|
||||
97
src/board/square.rs
Normal file
97
src/board/square.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use u64 as Bitboard;
|
||||
|
||||
use crate::board::bitboard::lsb;
|
||||
|
||||
pub struct Square {}
|
||||
|
||||
impl Square {
|
||||
pub const A1: usize = 0;
|
||||
pub const B1: usize = 1;
|
||||
pub const C1: usize = 2;
|
||||
pub const D1: usize = 3;
|
||||
pub const E1: usize = 4;
|
||||
pub const F1: usize = 5;
|
||||
pub const G1: usize = 6;
|
||||
pub const H1: usize = 7;
|
||||
|
||||
pub const A2: usize = 8;
|
||||
pub const B2: usize = 9;
|
||||
pub const C2: usize = 10;
|
||||
pub const D2: usize = 11;
|
||||
pub const E2: usize = 12;
|
||||
pub const F2: usize = 13;
|
||||
pub const G2: usize = 14;
|
||||
pub const H2: usize = 15;
|
||||
|
||||
pub const A3: usize = 16;
|
||||
pub const B3: usize = 17;
|
||||
pub const C3: usize = 18;
|
||||
pub const D3: usize = 19;
|
||||
pub const E3: usize = 20;
|
||||
pub const F3: usize = 21;
|
||||
pub const G3: usize = 22;
|
||||
pub const H3: usize = 23;
|
||||
|
||||
pub const A4: usize = 24;
|
||||
pub const B4: usize = 25;
|
||||
pub const C4: usize = 26;
|
||||
pub const D4: usize = 27;
|
||||
pub const E4: usize = 28;
|
||||
pub const F4: usize = 29;
|
||||
pub const G4: usize = 30;
|
||||
pub const H4: usize = 31;
|
||||
|
||||
pub const A5: usize = 32;
|
||||
pub const B5: usize = 33;
|
||||
pub const C5: usize = 34;
|
||||
pub const D5: usize = 35;
|
||||
pub const E5: usize = 36;
|
||||
pub const F5: usize = 37;
|
||||
pub const G5: usize = 38;
|
||||
pub const H5: usize = 39;
|
||||
|
||||
pub const A6: usize = 40;
|
||||
pub const B6: usize = 41;
|
||||
pub const C6: usize = 42;
|
||||
pub const D6: usize = 43;
|
||||
pub const E6: usize = 44;
|
||||
pub const F6: usize = 45;
|
||||
pub const G6: usize = 46;
|
||||
pub const H6: usize = 47;
|
||||
|
||||
pub const A7: usize = 48;
|
||||
pub const B7: usize = 49;
|
||||
pub const C7: usize = 50;
|
||||
pub const D7: usize = 51;
|
||||
pub const E7: usize = 52;
|
||||
pub const F7: usize = 53;
|
||||
pub const G7: usize = 54;
|
||||
pub const H7: usize = 55;
|
||||
|
||||
pub const A8: usize = 56;
|
||||
pub const B8: usize = 57;
|
||||
pub const C8: usize = 58;
|
||||
pub const D8: usize = 59;
|
||||
pub const E8: usize = 60;
|
||||
pub const F8: usize = 61;
|
||||
pub const G8: usize = 62;
|
||||
pub const H8: usize = 63;
|
||||
}
|
||||
|
||||
pub const fn coords_to_square(rank: usize, file: usize) -> usize {
|
||||
rank * 8 + file
|
||||
}
|
||||
|
||||
pub const fn bitboard_to_coords(bitboard: Bitboard) -> (usize, usize) {
|
||||
match bitboard {
|
||||
0 => (0, 0),
|
||||
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn square_to_algebraic(square: usize) -> String {
|
||||
let file = (square % 8) as u8;
|
||||
let rank = (square / 8) as u8;
|
||||
|
||||
format!("{}{}", (file + b'a') as char, (rank + b'1') as char)
|
||||
}
|
||||
133
src/board/state.rs
Normal file
133
src/board/state.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use crate::{board::board::Color, movegen::r#move::MoveType};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct State {
|
||||
side_to_move: Color,
|
||||
castling_ability: [Castle; 2],
|
||||
en_passant_target_square: Option<usize>,
|
||||
halfmove_clock: u8,
|
||||
fullmove_counter: u8,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
side_to_move: Color::White,
|
||||
castling_ability: [Castle::Both, Castle::Both],
|
||||
en_passant_target_square: None,
|
||||
halfmove_clock: 0,
|
||||
fullmove_counter: 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn load_state(
|
||||
side_to_move: Color,
|
||||
castling_ability: [Castle; 2],
|
||||
en_passant_target_square: Option<usize>,
|
||||
halfmove_clock: u8,
|
||||
fullmove_counter: u8,
|
||||
) -> Self {
|
||||
Self {
|
||||
side_to_move,
|
||||
castling_ability,
|
||||
en_passant_target_square,
|
||||
halfmove_clock,
|
||||
fullmove_counter,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn en_passant_target_square(&self) -> Option<usize> {
|
||||
self.en_passant_target_square
|
||||
}
|
||||
|
||||
pub fn set_castling_ability(&mut self, color: Color, castle: Castle) {
|
||||
match color {
|
||||
Color::White => self.castling_ability[0] = castle,
|
||||
Color::Black => self.castling_ability[1] = castle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_castling_state_quiet(&mut self, src: usize, color: Color) {
|
||||
self.update_castling_state_capture(src, color);
|
||||
|
||||
match (src, color) {
|
||||
(4, Color::White) => self.set_castling_ability(color, Castle::None),
|
||||
(60, Color::Black) => self.set_castling_ability(color, Castle::None),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_castling_state_capture(&mut self, target: usize, color: Color) {
|
||||
let (short_square, long_square) = match color {
|
||||
Color::White => (7, 0),
|
||||
Color::Black => (63, 56),
|
||||
};
|
||||
|
||||
if target == short_square {
|
||||
match self.castling_ability(color) {
|
||||
Castle::Both => self.set_castling_ability(color, Castle::Long),
|
||||
Castle::Short => self.set_castling_ability(color, Castle::None),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if target == long_square {
|
||||
match self.castling_ability(color) {
|
||||
Castle::Both => self.set_castling_ability(color, Castle::Short),
|
||||
Castle::Long => self.set_castling_ability(color, Castle::None),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_half_move(&mut self, move_type: MoveType, pawn_move: bool) {
|
||||
if move_type == MoveType::Capture || pawn_move {
|
||||
self.halfmove_clock = 0;
|
||||
} else {
|
||||
self.halfmove_clock += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_full_move(&mut self, color: Color) {
|
||||
match color {
|
||||
Color::White => (),
|
||||
Color::Black => self.fullmove_counter += 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_side(&mut self) {
|
||||
self.side_to_move = match self.side_to_move {
|
||||
Color::White => Color::Black,
|
||||
Color::Black => Color::White,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn next_turn(&self) -> Color {
|
||||
self.side_to_move
|
||||
}
|
||||
|
||||
pub fn set_en_passant_target_square(&mut self, square: Option<usize>) {
|
||||
self.en_passant_target_square = square;
|
||||
}
|
||||
|
||||
pub const fn castling_ability(&self, color: Color) -> Castle {
|
||||
match color {
|
||||
Color::White => self.castling_ability[0],
|
||||
Color::Black => self.castling_ability[1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Castle {
|
||||
Short,
|
||||
Long,
|
||||
Both,
|
||||
None,
|
||||
}
|
||||
Reference in New Issue
Block a user