#[cfg(test)]
mod test;
use crate::contract::Strain;
use core::fmt;
use core::ops::{BitAnd, BitOr, BitXor, Index, IndexMut, Not, Sub};
use rand::prelude::SliceRandom as _;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum Suit {
Clubs,
Diamonds,
Hearts,
Spades,
}
impl Suit {
pub const ASCENDING: [Self; 4] = [Self::Clubs, Self::Diamonds, Self::Hearts, Self::Spades];
pub const DESCENDING: [Self; 4] = [Self::Spades, Self::Hearts, Self::Diamonds, Self::Clubs];
}
impl From<Suit> for Strain {
fn from(suit: Suit) -> Self {
match suit {
Suit::Clubs => Self::Clubs,
Suit::Diamonds => Self::Diamonds,
Suit::Hearts => Self::Hearts,
Suit::Spades => Self::Spades,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Seat {
North,
East,
South,
West,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Card {
pub suit: Suit,
pub rank: u8,
}
impl Card {
#[must_use]
pub const fn new(suit: Suit, rank: u8) -> Self {
Self { suit, rank }
}
}
pub trait SmallSet<T>: Copy + Eq + BitAnd + BitOr + BitXor + Not + Sub {
const EMPTY: Self;
const ALL: Self;
#[must_use]
fn len(self) -> usize;
#[must_use]
fn is_empty(self) -> bool {
self == Self::EMPTY
}
fn contains(self, value: T) -> bool;
fn insert(&mut self, value: T) -> bool;
fn remove(&mut self, value: T) -> bool;
fn toggle(&mut self, value: T) -> bool;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Holding(u16);
impl SmallSet<u8> for Holding {
const EMPTY: Self = Self(0);
const ALL: Self = Self(0x7FFC);
fn len(self) -> usize {
self.0.count_ones() as usize
}
fn contains(self, rank: u8) -> bool {
self.0 & 1 << rank != 0
}
fn insert(&mut self, rank: u8) -> bool {
let insertion = 1 << rank & Self::ALL.0;
let inserted = insertion & !self.0 != 0;
self.0 |= insertion;
inserted
}
fn remove(&mut self, rank: u8) -> bool {
let removed = self.contains(rank);
self.0 &= !(1 << rank);
removed
}
fn toggle(&mut self, rank: u8) -> bool {
self.0 ^= 1 << rank & Self::ALL.0;
self.contains(rank)
}
}
impl Holding {
#[must_use]
pub const fn to_bits(self) -> u16 {
self.0
}
#[must_use]
pub const fn from_bits(bits: u16) -> Self {
Self(bits & Self::ALL.0)
}
}
impl BitAnd for Holding {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
impl BitOr for Holding {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl BitXor for Holding {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self {
Self(self.0 ^ rhs.0)
}
}
impl Not for Holding {
type Output = Self;
fn not(self) -> Self {
Self::ALL ^ self
}
}
impl Sub for Holding {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self & !rhs
}
}
impl fmt::Display for Holding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for rank in (2..15).rev() {
if self.contains(rank) {
use fmt::Write;
f.write_char(b"23456789TJQKA"[rank as usize - 2] as char)?;
}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Hand([Holding; 4]);
impl Index<Suit> for Hand {
type Output = Holding;
fn index(&self, suit: Suit) -> &Holding {
&self.0[suit as usize]
}
}
impl IndexMut<Suit> for Hand {
fn index_mut(&mut self, suit: Suit) -> &mut Holding {
&mut self.0[suit as usize]
}
}
impl Hand {
#[must_use]
pub const fn to_bits(self) -> u64 {
unsafe { core::mem::transmute(self.0) }
}
#[must_use]
pub const fn from_bits(bits: u64) -> Self {
Self(unsafe { core::mem::transmute(bits & Self::ALL.to_bits()) })
}
#[must_use]
pub const unsafe fn from_bits_unchecked(bits: u64) -> Self {
Self(core::mem::transmute(bits))
}
}
impl SmallSet<Card> for Hand {
const EMPTY: Self = Self([Holding::EMPTY; 4]);
const ALL: Self = Self([Holding::ALL; 4]);
fn len(self) -> usize {
self.to_bits().count_ones() as usize
}
fn contains(self, card: Card) -> bool {
self[card.suit].contains(card.rank)
}
fn insert(&mut self, card: Card) -> bool {
self[card.suit].insert(card.rank)
}
fn remove(&mut self, card: Card) -> bool {
self[card.suit].remove(card.rank)
}
fn toggle(&mut self, card: Card) -> bool {
self[card.suit].toggle(card.rank)
}
}
impl fmt::Display for Hand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}.{}.{}.{}",
self[Suit::Spades],
self[Suit::Hearts],
self[Suit::Diamonds],
self[Suit::Clubs]
)
}
}
impl BitAnd for Hand {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
unsafe { Self::from_bits_unchecked(self.to_bits() & rhs.to_bits()) }
}
}
impl BitOr for Hand {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
unsafe { Self::from_bits_unchecked(self.to_bits() | rhs.to_bits()) }
}
}
impl BitXor for Hand {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self {
unsafe { Self::from_bits_unchecked(self.to_bits() ^ rhs.to_bits()) }
}
}
impl Not for Hand {
type Output = Self;
fn not(self) -> Self {
Self::ALL ^ self
}
}
impl Sub for Hand {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self & !rhs
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Deal([Hand; 4]);
impl Index<Seat> for Deal {
type Output = Hand;
fn index(&self, seat: Seat) -> &Hand {
&self.0[seat as usize]
}
}
impl IndexMut<Seat> for Deal {
fn index_mut(&mut self, seat: Seat) -> &mut Hand {
&mut self.0[seat as usize]
}
}
impl fmt::Display for Deal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"N:{} {} {} {}",
self[Seat::North],
self[Seat::East],
self[Seat::South],
self[Seat::West]
)
}
}
#[derive(Debug, Clone, Default)]
struct Deck {
cards: Vec<Card>,
}
impl Deck {
#[must_use]
fn standard_52() -> Self {
Self {
cards: Suit::ASCENDING
.into_iter()
.flat_map(|x| core::iter::repeat(x).zip(2..=14))
.map(|(suit, rank)| Card::new(suit, rank))
.collect(),
}
}
#[must_use]
fn deal(&self) -> Deal {
let mut deal = Deal::default();
for (index, card) in self.cards.iter().enumerate() {
#[allow(clippy::cast_possible_truncation)]
deal[unsafe { core::mem::transmute((index & 0x3) as u8) }].insert(*card);
}
deal
}
fn shuffle(&mut self, rng: &mut (impl rand::Rng + ?Sized)) {
self.cards.shuffle(rng);
}
}
impl Deal {
pub fn new(rng: &mut (impl rand::Rng + ?Sized)) -> Self {
let mut deck = Deck::standard_52();
deck.shuffle(rng);
deck.deal()
}
}