use crate::hand::{Hand, ParseHandError};
use crate::seat::Seat;
use core::fmt::{self, Write as _};
use core::ops;
use core::str::FromStr;
use thiserror::Error;
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ParseDealError {
#[error("Invalid dealer tag for a deal")]
InvalidDealer,
#[error(transparent)]
Hand(#[from] ParseHandError),
#[error("The deal does not contain 4 hands")]
NotFourHands,
#[error("The deal is not a valid subset (>13 cards per hand or overlapping hands)")]
InvalidPartialDeal,
#[error("The deal is not a full deal (each hand must have exactly 13 cards)")]
NotFullDeal,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Builder([Hand; 4]);
impl IntoIterator for Builder {
type Item = Hand;
type IntoIter = core::array::IntoIter<Hand, 4>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl ops::Index<Seat> for Builder {
type Output = Hand;
#[inline]
fn index(&self, seat: Seat) -> &Hand {
&self.0[seat as usize]
}
}
impl ops::IndexMut<Seat> for Builder {
#[inline]
fn index_mut(&mut self, seat: Seat) -> &mut Hand {
&mut self.0[seat as usize]
}
}
impl Builder {
#[must_use]
pub const fn new() -> Self {
Self([Hand::EMPTY; 4])
}
#[must_use]
pub const fn north(mut self, hand: Hand) -> Self {
self.0[Seat::North as usize] = hand;
self
}
#[must_use]
pub const fn east(mut self, hand: Hand) -> Self {
self.0[Seat::East as usize] = hand;
self
}
#[must_use]
pub const fn south(mut self, hand: Hand) -> Self {
self.0[Seat::South as usize] = hand;
self
}
#[must_use]
pub const fn west(mut self, hand: Hand) -> Self {
self.0[Seat::West as usize] = hand;
self
}
pub fn build_partial(self) -> Result<PartialDeal, Self> {
let mut seen = Hand::EMPTY;
for hand in self.0 {
if hand.len() > 13 || hand & seen != Hand::EMPTY {
return Err(self);
}
seen |= hand;
}
Ok(PartialDeal(self))
}
pub fn build_full(self) -> Result<FullDeal, Self> {
match self.build_partial() {
Ok(subset) if subset.len() == 52 => Ok(FullDeal(subset.0)),
Ok(subset) => Err(subset.0),
Err(builder) => Err(builder),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "serde",
derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
)]
pub struct PartialDeal(Builder);
impl ops::Index<Seat> for PartialDeal {
type Output = Hand;
#[inline]
fn index(&self, seat: Seat) -> &Hand {
&self.0[seat]
}
}
impl PartialDeal {
pub const EMPTY: Self = Self(Builder::new());
#[must_use]
pub fn collected(&self) -> Hand {
self.0.into_iter().fold(Hand::EMPTY, |a, h| a | h)
}
#[must_use]
pub fn len(&self) -> usize {
self.collected().len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.collected().is_empty()
}
#[must_use]
pub fn display(&self, seat: Seat) -> impl fmt::Display + use<> {
DisplayAt {
builder: self.0,
seat,
}
}
}
impl From<PartialDeal> for Builder {
#[inline]
fn from(subset: PartialDeal) -> Self {
subset.0
}
}
impl TryFrom<Builder> for PartialDeal {
type Error = Builder;
#[inline]
fn try_from(builder: Builder) -> Result<Self, Self::Error> {
builder.build_partial()
}
}
impl FromStr for PartialDeal {
type Err = ParseDealError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_pbn(s)?
.build_partial()
.map_err(|_| ParseDealError::InvalidPartialDeal)
}
}
impl fmt::Display for PartialDeal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.display(Seat::North).fmt(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "serde",
derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
)]
pub struct FullDeal(Builder);
impl ops::Index<Seat> for FullDeal {
type Output = Hand;
#[inline]
fn index(&self, seat: Seat) -> &Hand {
&self.0[seat]
}
}
impl IntoIterator for FullDeal {
type Item = Hand;
type IntoIter = core::array::IntoIter<Hand, 4>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl FullDeal {
#[must_use]
pub fn display(&self, seat: Seat) -> impl fmt::Display + use<> {
DisplayAt {
builder: self.0,
seat,
}
}
}
impl From<FullDeal> for Builder {
#[inline]
fn from(deal: FullDeal) -> Self {
deal.0
}
}
impl From<FullDeal> for PartialDeal {
#[inline]
fn from(deal: FullDeal) -> Self {
Self(deal.0)
}
}
impl TryFrom<Builder> for FullDeal {
type Error = Builder;
#[inline]
fn try_from(builder: Builder) -> Result<Self, Self::Error> {
builder.build_full()
}
}
impl TryFrom<PartialDeal> for FullDeal {
type Error = PartialDeal;
#[inline]
fn try_from(subset: PartialDeal) -> Result<Self, Self::Error> {
match subset.0.build_full() {
Ok(full) => Ok(full),
Err(builder) => Err(PartialDeal(builder)),
}
}
}
impl FromStr for FullDeal {
type Err = ParseDealError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_pbn(s)?
.build_full()
.map_err(|_| ParseDealError::NotFullDeal)
}
}
impl fmt::Display for FullDeal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.display(Seat::North).fmt(f)
}
}
fn parse_pbn(s: &str) -> Result<Builder, ParseDealError> {
let bytes = s.as_bytes();
let dealer = match bytes.first().map(u8::to_ascii_uppercase) {
Some(b'N') => Seat::North,
Some(b'E') => Seat::East,
Some(b'S') => Seat::South,
Some(b'W') => Seat::West,
_ => return Err(ParseDealError::InvalidDealer),
};
if bytes.get(1) != Some(&b':') {
return Err(ParseDealError::InvalidDealer);
}
let hands: Result<Vec<_>, _> = s[2..].split_whitespace().map(Hand::from_str).collect();
let mut builder = Builder(
hands?
.try_into()
.map_err(|_| ParseDealError::NotFourHands)?,
);
builder.0.rotate_right(dealer as usize);
Ok(builder)
}
struct DisplayAt {
builder: Builder,
seat: Seat,
}
impl fmt::Display for DisplayAt {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_char(self.seat.letter())?;
f.write_char(':')?;
self.builder[self.seat].fmt(f)?;
f.write_char(' ')?;
self.builder[self.seat.lho()].fmt(f)?;
f.write_char(' ')?;
self.builder[self.seat.partner()].fmt(f)?;
f.write_char(' ')?;
self.builder[self.seat.rho()].fmt(f)
}
}