use core::fmt;
use core::iter::FusedIterator;
use core::str::FromStr;
use dds_bridge::{Builder, Card, FullDeal, Hand, PartialDeal, Seat};
use rand::{Rng, RngExt as _};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
)]
pub struct Deck(Hand);
impl Deck {
pub const ALL: Self = Self(Hand::ALL);
pub const EMPTY: Self = Self(Hand::EMPTY);
#[must_use]
#[inline]
pub const fn len(&self) -> usize {
self.0.len()
}
#[must_use]
#[inline]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub const fn clear(&mut self) {
self.0 = Hand::EMPTY;
}
pub fn insert(&mut self, card: Card) -> bool {
self.0.insert(card)
}
#[must_use]
#[inline]
pub const fn take(&mut self) -> Hand {
core::mem::replace(&mut self.0, Hand::EMPTY)
}
#[must_use]
pub fn draw(&mut self, rng: &mut (impl Rng + ?Sized), n: usize) -> Hand {
let len = self.0.len();
if n >= len {
return self.take();
}
let mut hand = Hand::EMPTY;
for i in 0..n {
let bits = (0..rng.random_range(..len - i))
.fold(self.0.to_bits(), |bits, _| bits & (bits - 1));
let selected = Hand::from_bits_retain(bits & bits.wrapping_neg());
hand |= selected;
self.0 ^= selected;
}
hand
}
#[must_use]
pub fn pop(&mut self, rng: &mut (impl Rng + ?Sized)) -> Option<Card> {
self.draw(rng, 1).into_iter().next()
}
}
impl From<Hand> for Deck {
fn from(hand: Hand) -> Self {
Self(hand)
}
}
impl fmt::Display for Deck {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for Deck {
type Err = <Hand as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<Hand>().map(Self)
}
}
#[must_use]
pub fn full_deal(rng: &mut (impl Rng + ?Sized)) -> FullDeal {
let mut deck = Deck::ALL;
#[allow(clippy::missing_panics_doc)]
Builder::new()
.north(deck.draw(rng, 13))
.east(deck.draw(rng, 13))
.south(deck.draw(rng, 13))
.west(deck.take())
.build_full()
.expect("each hand receives exactly 13 cards by construction")
}
#[derive(Debug)]
pub struct FillDeals<'a, R: Rng + ?Sized> {
rng: &'a mut R,
builder: Builder,
deck: Deck,
shortest: Seat,
}
impl<R: Rng + ?Sized> Iterator for FillDeals<'_, R> {
type Item = FullDeal;
fn next(&mut self) -> Option<FullDeal> {
let mut deck = self.deck;
let mut builder = self.builder;
let mut fill = |hand: &mut Hand| *hand |= deck.draw(self.rng, 13 - hand.len());
fill(&mut builder[self.shortest.lho()]);
fill(&mut builder[self.shortest.partner()]);
fill(&mut builder[self.shortest.rho()]);
builder[self.shortest] |= deck.take();
Some(
builder
.build_full()
.expect("each seat holds exactly 13 cards after filling"),
)
}
}
impl<R: Rng + ?Sized> FusedIterator for FillDeals<'_, R> {}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn fill_deals<R: Rng + ?Sized>(rng: &mut R, subset: PartialDeal) -> FillDeals<'_, R> {
let builder = Builder::from(subset);
FillDeals {
rng,
builder,
deck: Deck::from(!subset.collected()),
#[allow(clippy::missing_panics_doc)]
shortest: Seat::ALL
.into_iter()
.min_by_key(|&seat| builder[seat].len())
.expect("Seat::ALL shall not be empty"),
}
}
#[cfg(test)]
mod tests;