use std::fmt::{self, Display};
use std::ops::{Index, IndexMut, Range};
use crate::bitboard::Bitboard;
use crate::boardrepr::Mailbox;
use crate::coretypes::{Color, Piece, PieceKind, Square};
use crate::coretypes::{Color::*, PieceKind::*};
impl Color {
#[inline(always)]
const fn offset_block(&self) -> usize {
match self {
White => 0,
Black => 6,
}
}
}
impl PieceKind {
#[inline(always)]
const fn offset_pk(&self) -> usize {
match self {
King => 0,
Pawn => 1,
Knight => 2,
Queen => 3,
Rook => 4,
Bishop => 5,
}
}
}
impl Piece {
#[inline(always)]
const fn offset(&self) -> usize {
self.color.offset_block() + self.piece_kind.offset_pk()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct PieceSets {
pieces: [Bitboard; Self::SIZE],
}
impl PieceSets {
const SIZE: usize = 12;
pub fn new() -> Self {
PieceSets {
pieces: [Bitboard::EMPTY; Self::SIZE],
}
}
pub fn start_position() -> Self {
let mb: Mailbox = Mailbox::start_position();
Self::from(&mb)
}
pub fn occupied(&self) -> Bitboard {
self.pieces.iter().fold(Bitboard::EMPTY, |acc, bb| acc | bb)
}
pub fn color_occupied(&self, color: Color) -> Bitboard {
self[color].iter().fold(Bitboard::EMPTY, |acc, bb| acc | bb)
}
pub fn on_square(&self, sq: Square) -> Option<Piece> {
for player in Color::iter() {
for pk in PieceKind::iter() {
if self[(player, pk)].has_square(sq) {
return Some(Piece::new(player, pk));
}
}
}
None
}
pub fn on_player_square(&self, player: Color, sq: Square) -> Option<PieceKind> {
for pk in PieceKind::iter() {
if self[(player, pk)].has_square(sq) {
return Some(pk);
}
}
None
}
pub fn pretty(&self) -> String {
Mailbox::from(self).pretty()
}
pub fn is_disjoint(&self) -> bool {
let occupied_sum = self.occupied().count_squares();
let individual_sum = self
.pieces
.iter()
.fold(0, |acc, bb| acc + bb.count_squares());
occupied_sum == individual_sum
}
pub fn is_valid(&self) -> bool {
if self[(White, King)].count_squares() != 1 {
return false;
}
if self[(Black, King)].count_squares() != 1 {
return false;
}
if !self.is_disjoint() {
return false;
}
let w_pawns_first_rank = self[(White, Pawn)] & Bitboard::RANK_1;
let b_pawns_eighth_rank = self[(Black, Pawn)] & Bitboard::RANK_8;
if !w_pawns_first_rank.is_empty() || !b_pawns_eighth_rank.is_empty() {
return false;
}
true
}
}
impl Index<Piece> for PieceSets {
type Output = Bitboard;
fn index(&self, piece: Piece) -> &Self::Output {
&self.pieces[piece.offset()]
}
}
impl IndexMut<Piece> for PieceSets {
fn index_mut(&mut self, piece: Piece) -> &mut Self::Output {
&mut self.pieces[piece.offset()]
}
}
impl Index<&Piece> for PieceSets {
type Output = Bitboard;
fn index(&self, piece: &Piece) -> &Self::Output {
&self.pieces[piece.offset()]
}
}
impl IndexMut<&Piece> for PieceSets {
fn index_mut(&mut self, piece: &Piece) -> &mut Self::Output {
&mut self.pieces[piece.offset()]
}
}
impl Index<(Color, PieceKind)> for PieceSets {
type Output = Bitboard;
fn index(&self, (color, piece_kind): (Color, PieceKind)) -> &Self::Output {
&self.pieces[color.offset_block() + piece_kind.offset_pk()]
}
}
impl IndexMut<(Color, PieceKind)> for PieceSets {
fn index_mut(&mut self, (color, piece_kind): (Color, PieceKind)) -> &mut Self::Output {
&mut self.pieces[color.offset_block() + piece_kind.offset_pk()]
}
}
impl Index<&(Color, PieceKind)> for PieceSets {
type Output = Bitboard;
fn index(&self, (color, piece_kind): &(Color, PieceKind)) -> &Self::Output {
&self.pieces[color.offset_block() + piece_kind.offset_pk()]
}
}
impl IndexMut<&(Color, PieceKind)> for PieceSets {
fn index_mut(&mut self, (color, piece_kind): &(Color, PieceKind)) -> &mut Self::Output {
&mut self.pieces[color.offset_block() + piece_kind.offset_pk()]
}
}
impl Index<Color> for PieceSets {
type Output = [Bitboard];
fn index(&self, color: Color) -> &Self::Output {
const RANGES: (Range<usize>, Range<usize>) = color_ranges();
match color {
White => &self.pieces[RANGES.0],
Black => &self.pieces[RANGES.1],
}
}
}
impl IndexMut<Color> for PieceSets {
fn index_mut(&mut self, color: Color) -> &mut Self::Output {
const RANGES: (Range<usize>, Range<usize>) = color_ranges();
match color {
White => &mut self.pieces[RANGES.0],
Black => &mut self.pieces[RANGES.1],
}
}
}
const fn color_ranges() -> (Range<usize>, Range<usize>) {
const W_RANGE: Range<usize> = match White.offset_block() < Black.offset_block() {
true => White.offset_block()..Black.offset_block(),
false => White.offset_block()..PieceSets::SIZE,
};
const B_RANGE: Range<usize> = match White.offset_block() < Black.offset_block() {
true => Black.offset_block()..PieceSets::SIZE,
false => Black.offset_block()..White.offset_block(),
};
(W_RANGE, B_RANGE)
}
impl From<&Mailbox> for PieceSets {
fn from(mb: &Mailbox) -> Self {
let mut pieces = Self::new();
for square in Square::iter() {
if let Some(ref piece) = mb[square] {
pieces[piece].set_square(square);
}
}
pieces
}
}
impl Default for PieceSets {
fn default() -> Self {
Self::start_position()
}
}
impl Display for PieceSets {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.pretty())
}
}
#[cfg(test)]
mod tests {
use super::*;
use Square::*;
#[test]
fn piece_indexing() {
let pieces = PieceSets::start_position();
let w_king = &pieces[Piece::new(White, King)];
assert_eq!(w_king.count_squares(), 1);
assert!(w_king.has_square(E1));
}
#[test]
fn color_indexing() {
let pieces = PieceSets::start_position();
let white_pieces = &pieces[White];
let w_occupancy = white_pieces
.iter()
.fold(Bitboard::EMPTY, |acc, piece| acc | piece);
assert_eq!(w_occupancy.count_squares(), 16);
for square in [A1, B1, C1, D1, E1, F1, G1, H1] {
assert!(w_occupancy.has_square(&square));
}
for square in [A2, B2, C2, D2, E2, F2, G2, H2] {
assert!(w_occupancy.has_square(&square));
}
let black_pieces = &pieces[Black];
let b_occupancy = black_pieces
.iter()
.fold(Bitboard::EMPTY, |acc, piece| acc | piece);
assert_eq!(b_occupancy.count_squares(), 16);
for square in [A7, B7, C7, D7, E7, F7, G7, H7] {
assert!(b_occupancy.has_square(&square));
}
for square in [A8, B8, C8, D8, E8, F8, G8, H8] {
assert!(b_occupancy.has_square(&square));
}
}
#[test]
fn check_is_valid() {
let mut set = PieceSets::start_position();
assert!(set.is_valid());
set[(White, Pawn)].set_square(H8);
assert!(!set.is_valid());
}
}