mod fen;
pub mod piece;
use std::fmt::Display;
use std::ops::Index;
use bitflags::bitflags;
use owo_colors::OwoColorize;
use strum::{EnumCount, IntoEnumIterator};
use crate::error::Error;
use crate::{Colour, Piece, UncolouredPiece};
#[derive(Clone, Copy, Debug)]
pub struct Board {
board: [Option<Piece>; 64], bitboards: [Bitboard; Piece::COUNT], to_move: Colour,
castling_rights: CastlingRights,
en_passant: Option<Square>,
halfmove: usize,
fullmove: usize,
}
#[derive(Clone, Copy, Debug)]
pub struct Square(usize);
pub type Bitboard = u64;
bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct CastlingRights: u8 {
const WHITE_KINGSIDE = 0b1000;
const WHITE_QUEENSIDE = 0b0100;
const BLACK_KINGSIDE = 0b0010;
const BLACK_QUEENSIDE = 0b0001;
}
}
impl Board {
pub fn start() -> Self {
Self::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap()
}
pub fn print_bitboards(&self) {
for p in Piece::iter() {
println!("{:?}: {}", p, self.bitboards[p.as_index()]);
for rank in (0..8).rev() {
for file in 0..8 {
print!(
"{}",
if self.bitboards[p.as_index()] & (1 << (file + rank * 8)) != 0 {
'1'
} else {
'0'
}
);
}
println!();
}
println!();
}
}
}
impl Index<Square> for Board {
type Output = Option<Piece>;
fn index(&self, square: Square) -> &Self::Output {
&self.board[square.0]
}
}
impl Display for Board {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let print_piece = |piece: Option<Piece>| {
if let Some(p) = piece {
use UncolouredPiece as U;
let s = match p.piece() {
U::King => "♚",
U::Queen => "♛",
U::Rook => "♜",
U::Bishop => "♝",
U::Knight => "♞",
U::Pawn => "♟",
};
if p.colour() == Colour::White {
s.fg_rgb::<255, 255, 255>().to_string()
} else {
s.fg_rgb::<0, 0, 0>().to_string()
}
} else {
" ".to_owned()
}
};
f.write_str("╔══════════════════════╗\n║ A B C D E F G H ║\n")?;
for rank in (0..8).rev() {
write!(f, "║ {} ", rank + 1)?;
for file in 0..8 {
let p = format!("{} ", print_piece(self.board[file + rank * 8]));
if (rank + file + 1) % 2 == 0 {
write!(f, "{}", p.bg_rgb::<162, 115, 20>())?;
} else {
write!(f, "{}", p.bg_rgb::<250, 200, 125>())?;
}
}
writeln!(f, " {} ║", rank + 1)?;
}
f.write_str("║ A B C D E F G H ║\n╚══════════════════════╝\n")?;
Ok(())
}
}
impl TryFrom<(char, char)> for Square {
type Error = Error;
fn try_from((f, r): (char, char)) -> Result<Self, Self::Error> {
if !('a'..='h').contains(&f) {
return Err(Error::InvalidFile(f));
}
if !('1'..='8').contains(&r) {
return Err(Error::InvalidRank(r));
}
let file = f as usize - 'a' as usize;
let rank = r as usize - '1' as usize;
Ok(Self(file + rank * 8))
}
}
impl Display for Square {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let file = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'][self.0 % 8];
let rank = ['1', '2', '3', '4', '5', '6', '7', '8'][self.0 / 8];
write!(f, "{}{}", file, rank)
}
}
impl Display for CastlingRights {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_empty() {
return f.write_str("-");
}
if self.contains(CastlingRights::WHITE_KINGSIDE) {
f.write_str("K")?;
}
if self.contains(CastlingRights::WHITE_QUEENSIDE) {
f.write_str("Q")?;
}
if self.contains(CastlingRights::BLACK_KINGSIDE) {
f.write_str("k")?;
}
if self.contains(CastlingRights::BLACK_QUEENSIDE) {
f.write_str("q")?;
}
Ok(())
}
}