pounce 2.0.2

A mediocre (but trying its best) uci chess engine
Documentation
use core::fmt;
use std::fmt::Formatter;

use crate::chess::{Color, File, Rank, Square};

#[derive(Copy, Clone, Eq, PartialEq, Hash, Default)]
pub struct Bitboard(pub u64);

impl Bitboard {
    #[inline]
    pub const fn any(self) -> bool {
        self.0 != 0
    }

    #[inline]
    pub const fn none(self) -> bool {
        self.0 == 0
    }

    #[inline]
    pub fn contains(self, sq: Square) -> bool {
        (self & sq).any()
    }

    #[inline]
    pub const fn count(self) -> u32 {
        self.0.count_ones()
    }

    #[inline]
    pub fn set(&mut self, sq: Square) {
        *self |= sq;
    }

    #[inline]
    pub fn clear(&mut self, sq: Square) {
        *self &= !Bitboard::from(sq);
    }

    #[inline]
    pub fn toggle(&mut self, sq: Square) {
        *self ^= sq;
    }

    #[inline]
    pub fn north(self) -> Bitboard {
        self << 8
    }

    #[inline]
    pub fn south(self) -> Bitboard {
        self >> 8
    }

    #[inline]
    pub fn up(self, color: Color) -> Bitboard {
        if color == Color::White {
            self.north()
        } else {
            self.south()
        }
    }

    #[inline]
    pub fn down(self, color: Color) -> Bitboard {
        if color == Color::White {
            self.south()
        } else {
            self.north()
        }
    }

    #[inline]
    pub fn flip(self) -> Bitboard {
        Bitboard(self.0.swap_bytes())
    }

    #[inline]
    pub fn lsb(self) -> Option<Square> {
        if self.0 == 0 {
            None
        } else {
            Some(Square::new_unchecked(self.0.trailing_zeros() as u8))
        }
    }

    pub const EMPTY: Bitboard = Bitboard(0);
    pub const FULL: Bitboard = Bitboard(0xFFFFFFFFFFFFFFFF);
}

impl fmt::Debug for Bitboard {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let mut dbg_list = f.debug_list();
        for rank in Rank::ALL.iter().rev() {
            let mut file_str = "".to_string();
            for file in File::ALL.iter() {
                let sq = Square::make(*file, *rank);
                if self.contains(sq) {
                    file_str.push('1');
                } else {
                    file_str.push('.');
                }
            }
            dbg_list.entry(&file_str);
        }
        dbg_list.finish()
    }
}

impl fmt::LowerHex for Bitboard {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        fmt::LowerHex::fmt(&self.0, f)
    }
}

impl fmt::UpperHex for Bitboard {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        fmt::UpperHex::fmt(&self.0, f)
    }
}

impl fmt::Binary for Bitboard {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        fmt::Binary::fmt(&self.0, f)
    }
}

impl From<Bitboard> for u64 {
    #[inline]
    fn from(bb: Bitboard) -> Self {
        bb.0
    }
}

impl From<Rank> for Bitboard {
    #[inline]
    fn from(rank: Rank) -> Self {
        Bitboard(0xFF << (rank as u8 * 8))
    }
}

impl From<File> for Bitboard {
    #[inline]
    fn from(file: File) -> Self {
        Bitboard(0x0101010101010101 << file as u8)
    }
}

impl From<Square> for Bitboard {
    #[inline]
    fn from(sq: Square) -> Self {
        Bitboard(1 << sq as u8)
    }
}

impl From<u64> for Bitboard {
    #[inline]
    fn from(bb: u64) -> Self {
        Bitboard(bb)
    }
}

impl<T> std::ops::BitAnd<T> for Bitboard
where
    T: Into<Bitboard>,
{
    type Output = Bitboard;
    #[inline]
    fn bitand(self, rhs: T) -> Bitboard {
        let Bitboard(rhs) = rhs.into();
        Bitboard(self.0 & rhs)
    }
}

impl<T> std::ops::BitAndAssign<T> for Bitboard
where
    T: Into<Bitboard>,
{
    #[inline]
    fn bitand_assign(&mut self, rhs: T) {
        let Bitboard(rhs) = rhs.into();
        self.0 &= rhs;
    }
}

impl<T> std::ops::BitOr<T> for Bitboard
where
    T: Into<Bitboard>,
{
    type Output = Bitboard;
    #[inline]
    fn bitor(self, rhs: T) -> Bitboard {
        let Bitboard(rhs) = rhs.into();
        Bitboard(self.0 | rhs)
    }
}

impl<T> std::ops::BitOrAssign<T> for Bitboard
where
    T: Into<Bitboard>,
{
    #[inline]
    fn bitor_assign(&mut self, rhs: T) {
        let Bitboard(rhs) = rhs.into();
        self.0 |= rhs;
    }
}

impl<T> std::ops::BitXor<T> for Bitboard
where
    T: Into<Bitboard>,
{
    type Output = Bitboard;
    #[inline]
    fn bitxor(self, rhs: T) -> Bitboard {
        let Bitboard(rhs) = rhs.into();
        Bitboard(self.0 ^ rhs)
    }
}

impl<T> std::ops::BitXorAssign<T> for Bitboard
where
    T: Into<Bitboard>,
{
    #[inline]
    fn bitxor_assign(&mut self, rhs: T) {
        let Bitboard(rhs) = rhs.into();
        self.0 ^= rhs;
    }
}

impl std::ops::Not for Bitboard {
    type Output = Bitboard;
    #[inline]
    fn not(self) -> Self::Output {
        Bitboard(!self.0)
    }
}

impl std::ops::Shl<usize> for Bitboard {
    type Output = Self;
    #[inline]
    fn shl(self, rhs: usize) -> Bitboard {
        Bitboard(self.0 << rhs)
    }
}

impl std::ops::ShlAssign<usize> for Bitboard {
    #[inline]
    fn shl_assign(&mut self, rhs: usize) {
        self.0 <<= rhs;
    }
}

impl std::ops::Shr<usize> for Bitboard {
    type Output = Self;
    #[inline]
    fn shr(self, rhs: usize) -> Bitboard {
        Bitboard(self.0 >> rhs)
    }
}

impl std::ops::ShrAssign<usize> for Bitboard {
    #[inline]
    fn shr_assign(&mut self, rhs: usize) {
        self.0 >>= rhs;
    }
}

impl Iterator for Bitboard {
    type Item = Square;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.0 == 0 {
            return None;
        }
        let sq = Square::new_unchecked(self.0.trailing_zeros() as u8);
        *self ^= sq;
        Some(sq)
    }
}