#[cfg(test)]
mod test;
use crate::contract::Strain;
use core::fmt;
use core::num::{NonZeroU8, Wrapping};
use core::ops::{Add, 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 ASC: [Self; 4] = [Self::Clubs, Self::Diamonds, Self::Hearts, Self::Spades];
pub const DESC: [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,
}
impl Add<Wrapping<u8>> for Seat {
type Output = Self;
fn add(self, rhs: Wrapping<u8>) -> Self {
unsafe { core::mem::transmute((Wrapping(self as u8) + rhs).0 & 3) }
}
}
impl Sub<Wrapping<u8>> for Seat {
type Output = Self;
fn sub(self, rhs: Wrapping<u8>) -> Self {
unsafe { core::mem::transmute((Wrapping(self as u8) - rhs).0 & 3) }
}
}
impl From<Seat> for char {
fn from(seat: Seat) -> Self {
match seat {
Seat::North => 'N',
Seat::East => 'E',
Seat::South => 'S',
Seat::West => 'W',
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Card(NonZeroU8);
const _: () = assert!(core::mem::size_of::<Option<Card>>() == 1);
impl Card {
#[must_use]
pub const fn new(suit: Suit, rank: u8) -> Self {
assert!(rank >= 2 && rank <= 14);
Self(unsafe { NonZeroU8::new_unchecked(rank << 2 | suit as u8) })
}
#[must_use]
pub const fn suit(self) -> Suit {
unsafe { core::mem::transmute(self.0.get() & 3) }
}
#[must_use]
pub const fn rank(self) -> u8 {
self.0.get() >> 2
}
}
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(pub [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(pub [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]
}
}
#[derive(Debug, Clone, Default)]
struct Deck {
cards: Vec<Card>,
}
impl Deck {
#[must_use]
fn standard_52() -> Self {
Self {
cards: Suit::ASC
.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.into_iter().enumerate() {
#[allow(clippy::cast_possible_truncation)]
deal[unsafe { core::mem::transmute((index & 3) as u8) }].insert(card);
}
deal
}
fn shuffle(&mut self, rng: &mut (impl rand::Rng + ?Sized)) {
self.cards.shuffle(rng);
}
}
struct DealDisplay {
deal: Deal,
seat: Seat,
}
impl fmt::Display for DealDisplay {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}:{} {} {} {}",
char::from(self.seat),
self.deal[self.seat],
self.deal[self.seat + Wrapping(1)],
self.deal[self.seat + Wrapping(2)],
self.deal[self.seat + Wrapping(3)],
)
}
}
impl Deal {
pub fn new(rng: &mut (impl rand::Rng + ?Sized)) -> Self {
let mut deck = Deck::standard_52();
deck.shuffle(rng);
deck.deal()
}
#[must_use]
pub fn display(self, seat: Seat) -> impl fmt::Display {
DealDisplay { deal: self, seat }
}
}