Skip to main content

dds_bridge/
deal.rs

1use crate::Suit;
2use core::fmt::{self, Write as _};
3use core::iter::FusedIterator;
4use core::num::NonZero;
5use core::ops;
6use core::str::FromStr;
7use thiserror::Error;
8
9/// Position at the table
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub enum Seat {
13    /// Dealer of Board 1, partner of [`Seat::South`]
14    North,
15    /// Dealer of Board 2, partner of [`Seat::West`]
16    East,
17    /// Dealer of Board 3, partner of [`Seat::North`]
18    South,
19    /// Dealer of Board 4, partner of [`Seat::East`]
20    West,
21}
22
23impl Seat {
24    /// Seats in the order of dealing
25    pub const ALL: [Self; 4] = [Self::North, Self::East, Self::South, Self::West];
26
27    /// The partner of the seat
28    #[must_use]
29    pub const fn partner(self) -> Self {
30        match self {
31            Self::North => Self::South,
32            Self::East => Self::West,
33            Self::South => Self::North,
34            Self::West => Self::East,
35        }
36    }
37
38    /// The opponent on the left of the seat
39    #[must_use]
40    pub const fn lho(self) -> Self {
41        match self {
42            Self::North => Self::East,
43            Self::East => Self::South,
44            Self::South => Self::West,
45            Self::West => Self::North,
46        }
47    }
48
49    /// The opponent on the right of the seat
50    #[must_use]
51    pub const fn rho(self) -> Self {
52        match self {
53            Self::North => Self::West,
54            Self::East => Self::North,
55            Self::South => Self::East,
56            Self::West => Self::South,
57        }
58    }
59
60    /// Display character for this seat
61    #[must_use]
62    #[inline]
63    pub const fn letter(self) -> char {
64        match self {
65            Self::North => 'N',
66            Self::East => 'E',
67            Self::South => 'S',
68            Self::West => 'W',
69        }
70    }
71}
72
73const _: () = assert!(Seat::ALL[0] as u8 == 0);
74const _: () = assert!(Seat::ALL[1] as u8 == 1);
75const _: () = assert!(Seat::ALL[2] as u8 == 2);
76const _: () = assert!(Seat::ALL[3] as u8 == 3);
77
78impl fmt::Display for Seat {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        f.write_char(self.letter())
81    }
82}
83
84/// Error returned when parsing a [`Seat`] fails
85#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
86#[error("Invalid seat: expected one of N, E, S, W (or full names)")]
87pub struct ParseSeatError;
88
89impl FromStr for Seat {
90    type Err = ParseSeatError;
91
92    fn from_str(s: &str) -> Result<Self, Self::Err> {
93        match s.to_ascii_uppercase().as_str() {
94            "N" | "NORTH" => Ok(Self::North),
95            "E" | "EAST" => Ok(Self::East),
96            "S" | "SOUTH" => Ok(Self::South),
97            "W" | "WEST" => Ok(Self::West),
98            _ => Err(ParseSeatError),
99        }
100    }
101}
102
103bitflags::bitflags! {
104    /// A set of seats
105    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
107    pub struct SeatFlags: u8 {
108        /// The set containing [`Seat::North`]
109        const NORTH = 0b0001;
110        /// The set containing [`Seat::East`]
111        const EAST = 0b0010;
112        /// The set containing [`Seat::South`]
113        const SOUTH = 0b0100;
114        /// The set containing [`Seat::West`]
115        const WEST = 0b1000;
116    }
117}
118
119impl SeatFlags {
120    /// The empty set
121    pub const EMPTY: Self = Self::empty();
122
123    /// The set containing all seats
124    pub const ALL: Self = Self::all();
125
126    /// The set containing [`Seat::North`] and [`Seat::South`]
127    pub const NS: Self = Self::NORTH.union(Self::SOUTH);
128
129    /// The set containing [`Seat::East`] and [`Seat::West`]
130    pub const EW: Self = Self::EAST.union(Self::WEST);
131}
132
133impl From<Seat> for SeatFlags {
134    fn from(seat: Seat) -> Self {
135        match seat {
136            Seat::North => Self::NORTH,
137            Seat::East => Self::EAST,
138            Seat::South => Self::SOUTH,
139            Seat::West => Self::WEST,
140        }
141    }
142}
143
144/// Error indicating an invalid rank
145///
146/// The rank of a card must be in `2..=14`, where J, Q, K, A are denoted as 11,
147/// 12, 13, 14 respectively.
148#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
149#[error("{0} is not a valid rank (2..=14)")]
150pub struct InvalidRank(u8);
151
152/// The rank of a card, from 2 to 14, where J, Q, K, A are internally denoted as
153/// 11, 12, 13, 14 respectively.
154#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
155#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
156#[cfg_attr(feature = "serde", serde(transparent))]
157#[repr(transparent)]
158pub struct Rank(NonZero<u8>);
159
160impl Rank {
161    /// Ace
162    pub const A: Self = Self(NonZero::new(14).unwrap());
163
164    /// King
165    pub const K: Self = Self(NonZero::new(13).unwrap());
166
167    /// Queen
168    pub const Q: Self = Self(NonZero::new(12).unwrap());
169
170    /// Jack
171    pub const J: Self = Self(NonZero::new(11).unwrap());
172
173    /// Ten
174    pub const T: Self = Self(NonZero::new(10).unwrap());
175
176    /// Create a rank from a number
177    ///
178    /// # Panics
179    ///
180    /// When the rank is not in `2..=14`.  In const contexts, this is a
181    /// compile-time error.
182    #[must_use]
183    #[inline]
184    pub const fn new(rank: u8) -> Self {
185        match Self::try_new(rank) {
186            Ok(r) => r,
187            Err(_) => panic!("rank must be in 2..=14"),
188        }
189    }
190
191    /// Try to create a rank from a number
192    ///
193    /// # Errors
194    ///
195    /// When the rank is not in `2..=14`.
196    #[inline]
197    pub const fn try_new(rank: u8) -> Result<Self, InvalidRank> {
198        match NonZero::new(rank) {
199            Some(nonzero) if rank >= 2 && rank <= 14 => Ok(Self(nonzero)),
200            _ => Err(InvalidRank(rank)),
201        }
202    }
203
204    /// Get the stored rank as [`u8`]
205    #[must_use]
206    #[inline]
207    pub const fn get(self) -> u8 {
208        self.0.get()
209    }
210
211    /// Display character for this rank
212    #[must_use]
213    #[inline]
214    pub const fn letter(self) -> char {
215        b"23456789TJQKA"[self.get() as usize - 2] as char
216    }
217}
218
219impl fmt::Display for Rank {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        f.write_char(self.letter())
222    }
223}
224
225/// Error returned when parsing a [`Rank`] fails
226#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
227#[error("Invalid rank: expected 2-10, T, J, Q, K, A")]
228pub struct ParseRankError;
229
230impl FromStr for Rank {
231    type Err = ParseRankError;
232    fn from_str(s: &str) -> Result<Self, Self::Err> {
233        match s.to_ascii_uppercase().as_str() {
234            "A" => Ok(Self::A),
235            "K" => Ok(Self::K),
236            "Q" => Ok(Self::Q),
237            "J" => Ok(Self::J),
238            "T" | "10" => Ok(Self::T),
239            "9" => Ok(Self::new(9)),
240            "8" => Ok(Self::new(8)),
241            "7" => Ok(Self::new(7)),
242            "6" => Ok(Self::new(6)),
243            "5" => Ok(Self::new(5)),
244            "4" => Ok(Self::new(4)),
245            "3" => Ok(Self::new(3)),
246            "2" => Ok(Self::new(2)),
247            _ => Err(ParseRankError),
248        }
249    }
250}
251
252/// A playing card
253///
254/// Internally packed as `(rank << 2) | suit` in a single byte.
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
256pub struct Card {
257    /// The suit of the card
258    pub suit: Suit,
259    /// The rank of the card
260    pub rank: Rank,
261}
262
263impl fmt::Display for Card {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        write!(f, "{}{}", self.suit, self.rank)
266    }
267}
268
269/// Error returned when parsing a [`Card`] fails
270#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
271#[non_exhaustive]
272pub enum ParseCardError {
273    /// Invalid suit in card
274    #[error("Invalid suit in card: expected one of C, D, H, S, ♣, ♦, ♥, ♠, ♧, ♢, ♡, ♤")]
275    Suit,
276    /// Invalid rank in card
277    #[error("Invalid rank in card: expected 2-10, T, J, Q, K, A")]
278    Rank,
279}
280
281impl FromStr for Card {
282    type Err = ParseCardError;
283    fn from_str(s: &str) -> Result<Self, Self::Err> {
284        let rank_len = if s.ends_with("10") {
285            2
286        } else {
287            s.chars().next_back().map_or(0, char::len_utf8)
288        };
289        let border = s.len().saturating_sub(rank_len);
290        let (suit, rank) = s.split_at(border);
291        let suit: Suit = suit.parse().map_err(|_| ParseCardError::Suit)?;
292        let rank: Rank = rank.parse().map_err(|_| ParseCardError::Rank)?;
293        Ok(Self { suit, rank })
294    }
295}
296
297/// A set of cards of the same suit
298#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
299#[repr(transparent)]
300pub struct Holding(u16);
301
302/// Iterator over the ranks in a [`Holding`], yielding [`Rank`]s in descending order
303#[derive(Debug, Clone, PartialEq, Eq)]
304pub struct HoldingIter {
305    rest: u16,
306    cursor: u8,
307}
308
309impl Iterator for HoldingIter {
310    type Item = Rank;
311
312    fn next(&mut self) -> Option<Self::Item> {
313        if self.rest == 0 {
314            return None;
315        }
316
317        // For non-zero u16, leading_zeros is in 0..=15, fitting in u8.
318        // Bit 15 is never set in a valid Holding (max rank is 14), so pos <= 14.
319        #[allow(clippy::cast_possible_truncation)]
320        let pos = 15 - self.rest.leading_zeros() as u8;
321        self.rest &= !(1u16 << pos);
322        let rank = self.cursor + pos;
323
324        // SAFETY: rank is in 2..=14 by construction from a valid Holding
325        Some(Rank(unsafe { core::num::NonZero::new_unchecked(rank) }))
326    }
327
328    fn size_hint(&self) -> (usize, Option<usize>) {
329        let count = self.rest.count_ones() as usize;
330        (count, Some(count))
331    }
332
333    fn count(self) -> usize {
334        self.rest.count_ones() as usize
335    }
336}
337
338impl DoubleEndedIterator for HoldingIter {
339    fn next_back(&mut self) -> Option<Self::Item> {
340        if self.rest == 0 {
341            return None;
342        }
343
344        // 1. Trailing zeros are in the range of 0..=15, which fits in `u8`
345        // 2. Trailing zeros cannot be 15 since the bitset is from a `Holding`
346        #[allow(clippy::cast_possible_truncation)]
347        let step = self.rest.trailing_zeros() as u8 + 1;
348        self.rest >>= step;
349        self.cursor += step;
350
351        // SAFETY: cursor is in 2..=14 by construction from a valid Holding
352        Some(Rank(unsafe {
353            core::num::NonZero::new_unchecked(self.cursor - 1)
354        }))
355    }
356}
357
358impl ExactSizeIterator for HoldingIter {
359    fn len(&self) -> usize {
360        self.rest.count_ones() as usize
361    }
362}
363
364impl FusedIterator for HoldingIter {}
365
366impl Holding {
367    /// The empty set
368    pub const EMPTY: Self = Self(0);
369
370    /// The set containing all possible ranks (2..=14)
371    pub const ALL: Self = Self(0x7FFC);
372
373    /// The number of cards in the holding
374    #[must_use]
375    #[inline]
376    pub const fn len(self) -> usize {
377        self.0.count_ones() as usize
378    }
379
380    /// Whether the holding is empty
381    #[must_use]
382    #[inline]
383    pub const fn is_empty(self) -> bool {
384        self.0 == 0
385    }
386
387    /// Whether the holding contains a rank
388    #[must_use]
389    #[inline]
390    pub const fn contains(self, rank: Rank) -> bool {
391        self.0 & 1 << rank.get() != 0
392    }
393
394    /// Insert a rank into the holding, returning whether it was newly inserted
395    #[inline]
396    pub const fn insert(&mut self, rank: Rank) -> bool {
397        let insertion = 1 << rank.get() & Self::ALL.0;
398        let inserted = insertion & !self.0 != 0;
399        self.0 |= insertion;
400        inserted
401    }
402
403    /// Remove a rank from the holding, returning whether it was present
404    #[inline]
405    pub const fn remove(&mut self, rank: Rank) -> bool {
406        let removed = self.contains(rank);
407        self.0 &= !(1 << rank.get());
408        removed
409    }
410
411    /// Toggle a rank in the holding, returning whether it is now present
412    #[inline]
413    pub const fn toggle(&mut self, rank: Rank) -> bool {
414        self.0 ^= 1 << rank.get() & Self::ALL.0;
415        self.contains(rank)
416    }
417
418    /// Conditionally insert/remove a rank from the holding
419    #[inline]
420    pub const fn set(&mut self, rank: Rank, condition: bool) {
421        let flag = 1 << rank.get();
422        let mask = (condition as u16).wrapping_neg();
423        self.0 = (self.0 & !flag) | (mask & flag);
424    }
425
426    /// Iterate over the ranks in the holding
427    #[inline]
428    #[must_use]
429    pub const fn iter(self) -> HoldingIter {
430        HoldingIter {
431            rest: self.0,
432            cursor: 0,
433        }
434    }
435
436    /// As a bitset of ranks
437    #[must_use]
438    #[inline]
439    pub const fn to_bits(self) -> u16 {
440        self.0
441    }
442
443    /// Create a holding from a bitset of ranks, retaining invalid ranks
444    #[must_use]
445    #[inline]
446    pub const fn from_bits_retain(bits: u16) -> Self {
447        Self(bits)
448    }
449
450    /// Whether the holding contains an invalid rank
451    #[must_use]
452    #[inline]
453    pub const fn contains_unknown_bits(self) -> bool {
454        self.0 & Self::ALL.0 != self.0
455    }
456
457    /// Create a holding from a bitset of ranks, checking for invalid ranks
458    #[must_use]
459    #[inline]
460    pub const fn from_bits(bits: u16) -> Option<Self> {
461        if bits & Self::ALL.0 == bits {
462            Some(Self(bits))
463        } else {
464            None
465        }
466    }
467
468    /// Create a holding from a bitset of ranks, removing invalid ranks
469    #[must_use]
470    #[inline]
471    pub const fn from_bits_truncate(bits: u16) -> Self {
472        Self(bits & Self::ALL.0)
473    }
474
475    /// Create a holding from a rank
476    #[must_use]
477    #[inline]
478    pub const fn from_rank(rank: Rank) -> Self {
479        Self(1 << rank.get())
480    }
481}
482
483impl IntoIterator for Holding {
484    type Item = Rank;
485    type IntoIter = HoldingIter;
486
487    fn into_iter(self) -> HoldingIter {
488        self.iter()
489    }
490}
491
492impl ops::BitAnd for Holding {
493    type Output = Self;
494
495    #[inline]
496    fn bitand(self, rhs: Self) -> Self {
497        Self(self.0 & rhs.0)
498    }
499}
500
501impl ops::BitOr for Holding {
502    type Output = Self;
503
504    #[inline]
505    fn bitor(self, rhs: Self) -> Self {
506        Self(self.0 | rhs.0)
507    }
508}
509
510impl ops::BitXor for Holding {
511    type Output = Self;
512
513    #[inline]
514    fn bitxor(self, rhs: Self) -> Self {
515        Self(self.0 ^ rhs.0)
516    }
517}
518
519impl ops::Not for Holding {
520    type Output = Self;
521
522    #[inline]
523    fn not(self) -> Self {
524        Self::from_bits_truncate(!self.0)
525    }
526}
527
528impl ops::Sub for Holding {
529    type Output = Self;
530
531    #[inline]
532    fn sub(self, rhs: Self) -> Self {
533        Self(self.0 & !rhs.0)
534    }
535}
536
537impl ops::BitAndAssign for Holding {
538    #[inline]
539    fn bitand_assign(&mut self, rhs: Self) {
540        *self = *self & rhs;
541    }
542}
543
544impl ops::BitOrAssign for Holding {
545    #[inline]
546    fn bitor_assign(&mut self, rhs: Self) {
547        *self = *self | rhs;
548    }
549}
550
551impl ops::BitXorAssign for Holding {
552    #[inline]
553    fn bitxor_assign(&mut self, rhs: Self) {
554        *self = *self ^ rhs;
555    }
556}
557
558impl ops::SubAssign for Holding {
559    #[inline]
560    fn sub_assign(&mut self, rhs: Self) {
561        *self = *self - rhs;
562    }
563}
564
565/// Show cards in descending order
566///
567/// 1. The ten is shown as `T` for PBN compatibility.
568/// 2. This implementation ignores formatting flags for simplicity and speed.
569///    If you want to pad or align the output, use [`fmt::Formatter::pad`].
570impl fmt::Display for Holding {
571    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
572        for rank in (2u8..15).rev() {
573            if self.0 & 1 << rank != 0 {
574                f.write_char(b"23456789TJQKA"[rank as usize - 2] as char)?;
575            }
576        }
577        Ok(())
578    }
579}
580
581/// An error which can be returned when parsing a [`Holding`]
582#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
583#[non_exhaustive]
584pub enum ParseHoldingError {
585    /// Ranks are not all valid or in descending order
586    #[error("Ranks are not all valid or in descending order")]
587    InvalidRanks,
588
589    /// The same rank appears more than once
590    #[error("The same rank appears more than once")]
591    RepeatedRank,
592
593    /// A suit contains more than 13 cards
594    #[error("A suit contains more than 13 cards")]
595    TooManyCards,
596}
597
598/// An error which can be returned when parsing a [`Hand`]
599#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
600#[non_exhaustive]
601pub enum ParseHandError {
602    /// Error in a holding
603    #[error(transparent)]
604    Holding(#[from] ParseHoldingError),
605
606    /// The hand does not contain 4 suits
607    #[error("The hand does not contain 4 suits")]
608    NotFourSuits,
609}
610
611/// An error which can be returned when parsing a [`Deal`]
612#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
613#[non_exhaustive]
614pub enum ParseDealError {
615    /// Invalid dealer tag
616    #[error("Invalid dealer tag for a deal")]
617    InvalidDealer,
618
619    /// Error in a hand
620    #[error(transparent)]
621    Hand(#[from] ParseHandError),
622
623    /// The deal does not contain 4 hands
624    #[error("The deal does not contain 4 hands")]
625    NotFourHands,
626}
627
628impl FromStr for Holding {
629    type Err = ParseHoldingError;
630
631    fn from_str(s: &str) -> Result<Self, Self::Err> {
632        // 13 cards + 1 extra char for "10"
633        if s.len() > 14 {
634            return Err(ParseHoldingError::TooManyCards);
635        }
636
637        let bytes = s.as_bytes();
638        let mut i = 0;
639        let mut prev_rank: u8 = 15;
640        let mut explicit = Self::EMPTY;
641
642        while i < bytes.len() {
643            let c = bytes[i].to_ascii_uppercase();
644            let rank: u8 = match c {
645                b'A' => 14,
646                b'K' => 13,
647                b'Q' => 12,
648                b'J' => 11,
649                b'T' => 10,
650                b'1' => {
651                    if bytes.get(i + 1) != Some(&b'0') {
652                        return Err(ParseHoldingError::InvalidRanks);
653                    }
654                    i += 1;
655                    10
656                }
657                b'2'..=b'9' => c - b'0',
658                b'X' => break,
659                _ => return Err(ParseHoldingError::InvalidRanks),
660            };
661
662            if rank >= prev_rank {
663                return Err(ParseHoldingError::InvalidRanks);
664            }
665            prev_rank = rank;
666
667            // SAFETY: rank is in 2..=14 by construction above
668            let r = Rank(unsafe { core::num::NonZero::new_unchecked(rank) });
669            explicit.insert(r);
670            i += 1;
671        }
672
673        let spot_count = bytes.len() - i;
674        if bytes[i..].iter().any(|&b| !b.eq_ignore_ascii_case(&b'x')) {
675            return Err(ParseHoldingError::InvalidRanks);
676        }
677        if spot_count > 13 {
678            return Err(ParseHoldingError::TooManyCards);
679        }
680
681        let spots = Self::from_bits_truncate((4u16 << spot_count) - 4);
682
683        if explicit & spots == Self::EMPTY {
684            Ok(explicit | spots)
685        } else {
686            Err(ParseHoldingError::RepeatedRank)
687        }
688    }
689}
690
691/// A hand of playing cards
692#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
693pub struct Hand([Holding; 4]);
694
695/// Iterator over the cards in a [`Hand`], yielding [`Card`]s in descending
696/// suit order and descending rank order within each suit
697#[derive(Debug, Clone, PartialEq, Eq)]
698pub struct HandIter {
699    suits: [HoldingIter; 4],
700    fwd: u8,
701    bwd: u8,
702}
703
704impl Iterator for HandIter {
705    type Item = Card;
706
707    fn next(&mut self) -> Option<Self::Item> {
708        loop {
709            if self.fwd > 3 {
710                return None;
711            }
712            let suit = Suit::ASC[self.fwd as usize];
713            if let Some(rank) = self.suits[self.fwd as usize].next() {
714                return Some(Card { suit, rank });
715            }
716            if self.fwd == self.bwd {
717                self.fwd = 4;
718                return None;
719            }
720            self.fwd -= 1;
721        }
722    }
723
724    fn size_hint(&self) -> (usize, Option<usize>) {
725        let count = self.len();
726        (count, Some(count))
727    }
728
729    fn count(self) -> usize {
730        self.len()
731    }
732}
733
734impl DoubleEndedIterator for HandIter {
735    fn next_back(&mut self) -> Option<Self::Item> {
736        loop {
737            if self.fwd > 3 {
738                return None;
739            }
740            let suit = Suit::ASC[self.bwd as usize];
741            if let Some(rank) = self.suits[self.bwd as usize].next_back() {
742                return Some(Card { suit, rank });
743            }
744            if self.fwd == self.bwd {
745                self.fwd = 4;
746                return None;
747            }
748            self.bwd += 1;
749        }
750    }
751}
752
753impl ExactSizeIterator for HandIter {
754    fn len(&self) -> usize {
755        if self.fwd > 3 {
756            return 0;
757        }
758        (self.bwd as usize..=self.fwd as usize)
759            .map(|i| self.suits[i].len())
760            .sum()
761    }
762}
763
764impl FusedIterator for HandIter {}
765
766impl ops::Index<Suit> for Hand {
767    type Output = Holding;
768
769    #[inline]
770    fn index(&self, suit: Suit) -> &Holding {
771        &self.0[suit as usize]
772    }
773}
774
775impl ops::IndexMut<Suit> for Hand {
776    #[inline]
777    fn index_mut(&mut self, suit: Suit) -> &mut Holding {
778        &mut self.0[suit as usize]
779    }
780}
781
782impl Hand {
783    /// As a bitset of cards
784    #[must_use]
785    #[inline]
786    pub const fn to_bits(self) -> u64 {
787        unsafe { core::mem::transmute(self.0) }
788    }
789
790    /// Create a hand from a bitset of cards, retaining invalid cards
791    #[must_use]
792    #[inline]
793    pub const fn from_bits_retain(bits: u64) -> Self {
794        unsafe { core::mem::transmute(bits) }
795    }
796
797    /// Whether the hand contains an invalid card
798    #[must_use]
799    #[inline]
800    pub const fn contains_unknown_bits(self) -> bool {
801        self.to_bits() & Self::ALL.to_bits() != self.to_bits()
802    }
803
804    /// Create a hand from a bitset of cards, checking for invalid cards
805    #[must_use]
806    #[inline]
807    pub const fn from_bits(bits: u64) -> Option<Self> {
808        if bits & Self::ALL.to_bits() == bits {
809            Some(Self::from_bits_retain(bits))
810        } else {
811            None
812        }
813    }
814
815    /// Create a hand from a bitset of cards, removing invalid cards
816    #[must_use]
817    #[inline]
818    pub const fn from_bits_truncate(bits: u64) -> Self {
819        Self::from_bits_retain(bits & Self::ALL.to_bits())
820    }
821
822    /// Create a hand from four holdings in suit order (clubs, diamonds, hearts, spades)
823    #[must_use]
824    #[inline]
825    pub const fn new(clubs: Holding, diamonds: Holding, hearts: Holding, spades: Holding) -> Self {
826        Self([clubs, diamonds, hearts, spades])
827    }
828
829    /// The empty hand
830    pub const EMPTY: Self = Self([Holding::EMPTY; 4]);
831
832    /// The hand containing all 52 cards
833    pub const ALL: Self = Self([Holding::ALL; 4]);
834
835    /// The number of cards in the hand
836    #[must_use]
837    #[inline]
838    pub const fn len(self) -> usize {
839        self.to_bits().count_ones() as usize
840    }
841
842    /// Whether the hand is empty
843    #[must_use]
844    #[inline]
845    pub const fn is_empty(self) -> bool {
846        self.to_bits() == 0
847    }
848
849    /// Whether the hand contains a card
850    #[must_use]
851    #[inline]
852    pub fn contains(self, card: Card) -> bool {
853        self[card.suit].contains(card.rank)
854    }
855
856    /// Insert a card into the hand, returning whether it was newly inserted
857    #[inline]
858    pub fn insert(&mut self, card: Card) -> bool {
859        self[card.suit].insert(card.rank)
860    }
861
862    /// Remove a card from the hand, returning whether it was present
863    #[inline]
864    pub fn remove(&mut self, card: Card) -> bool {
865        self[card.suit].remove(card.rank)
866    }
867
868    /// Toggle a card in the hand, returning whether it is now present
869    #[inline]
870    pub fn toggle(&mut self, card: Card) -> bool {
871        self[card.suit].toggle(card.rank)
872    }
873
874    /// Conditionally insert/remove a card from the hand
875    #[inline]
876    pub fn set(&mut self, card: Card, condition: bool) {
877        self[card.suit].set(card.rank, condition);
878    }
879
880    /// Iterate over the cards in the hand
881    #[inline]
882    #[must_use]
883    pub const fn iter(self) -> HandIter {
884        HandIter {
885            suits: [
886                self.0[0].iter(),
887                self.0[1].iter(),
888                self.0[2].iter(),
889                self.0[3].iter(),
890            ],
891            fwd: 3,
892            bwd: 0,
893        }
894    }
895}
896
897impl IntoIterator for Hand {
898    type Item = Card;
899    type IntoIter = HandIter;
900
901    #[inline]
902    fn into_iter(self) -> HandIter {
903        self.iter()
904    }
905}
906
907impl FromIterator<Card> for Hand {
908    fn from_iter<I: IntoIterator<Item = Card>>(iter: I) -> Self {
909        iter.into_iter().fold(Self::EMPTY, |mut hand, card| {
910            hand.insert(card);
911            hand
912        })
913    }
914}
915
916/// PBN-compatible display of a hand
917///
918/// This implementation ignores formatting flags for simplicity and speed.
919impl fmt::Display for Hand {
920    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
921        if self.is_empty() {
922            return f.write_char('-');
923        }
924
925        self[Suit::Spades].fmt(f)?;
926        f.write_char('.')?;
927
928        self[Suit::Hearts].fmt(f)?;
929        f.write_char('.')?;
930
931        self[Suit::Diamonds].fmt(f)?;
932        f.write_char('.')?;
933
934        self[Suit::Clubs].fmt(f)
935    }
936}
937
938impl FromStr for Hand {
939    type Err = ParseHandError;
940
941    fn from_str(s: &str) -> Result<Self, Self::Err> {
942        // 52 cards + 4 tens + 3 dots
943        if s.len() > 52 + 4 + 3 {
944            return Err(ParseHoldingError::TooManyCards.into());
945        }
946
947        if s == "-" {
948            return Ok(Self::EMPTY);
949        }
950
951        let holdings: Result<Vec<_>, _> = s.split('.').map(Holding::from_str).rev().collect();
952
953        Ok(Self(
954            holdings?
955                .try_into()
956                .map_err(|_| ParseHandError::NotFourSuits)?,
957        ))
958    }
959}
960
961impl ops::BitAnd for Hand {
962    type Output = Self;
963
964    #[inline]
965    fn bitand(self, rhs: Self) -> Self {
966        Self::from_bits_retain(self.to_bits() & rhs.to_bits())
967    }
968}
969
970impl ops::BitOr for Hand {
971    type Output = Self;
972
973    #[inline]
974    fn bitor(self, rhs: Self) -> Self {
975        Self::from_bits_retain(self.to_bits() | rhs.to_bits())
976    }
977}
978
979impl ops::BitXor for Hand {
980    type Output = Self;
981
982    #[inline]
983    fn bitxor(self, rhs: Self) -> Self {
984        Self::from_bits_retain(self.to_bits() ^ rhs.to_bits())
985    }
986}
987
988impl ops::Not for Hand {
989    type Output = Self;
990
991    #[inline]
992    fn not(self) -> Self {
993        Self::from_bits_truncate(!self.to_bits())
994    }
995}
996
997impl ops::Sub for Hand {
998    type Output = Self;
999
1000    #[inline]
1001    fn sub(self, rhs: Self) -> Self {
1002        Self::from_bits_retain(self.to_bits() & !rhs.to_bits())
1003    }
1004}
1005
1006impl ops::BitAndAssign for Hand {
1007    #[inline]
1008    fn bitand_assign(&mut self, rhs: Self) {
1009        *self = *self & rhs;
1010    }
1011}
1012
1013impl ops::BitOrAssign for Hand {
1014    #[inline]
1015    fn bitor_assign(&mut self, rhs: Self) {
1016        *self = *self | rhs;
1017    }
1018}
1019
1020impl ops::BitXorAssign for Hand {
1021    #[inline]
1022    fn bitxor_assign(&mut self, rhs: Self) {
1023        *self = *self ^ rhs;
1024    }
1025}
1026
1027impl ops::SubAssign for Hand {
1028    #[inline]
1029    fn sub_assign(&mut self, rhs: Self) {
1030        *self = *self - rhs;
1031    }
1032}
1033
1034/// A deal of four hands
1035///
1036/// Parses the [PBN] deal format: `<dealer>:<hand> <hand> <hand> <hand>`,
1037/// where each hand is four dot-separated holdings ordered spades, hearts,
1038/// diamonds, clubs.  Hands are listed clockwise starting from the dealer.
1039///
1040/// # Examples
1041///
1042/// ```
1043/// use dds_bridge::{Deal, Rank, Seat, Suit};
1044///
1045/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1046/// let deal: Deal = "N:.63.AKQ987.A9732 A8654.KQ5.T.QJT6 \
1047///                   J973.J98742.3.K4 KQT2.AT.J6542.85".parse()?;
1048/// assert!(deal[Seat::East][Suit::Spades].contains(Rank::A));
1049/// # Ok(())
1050/// # }
1051/// ```
1052///
1053/// [PBN]: https://www.tistis.nl/pbn/
1054#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
1055pub struct Deal([Hand; 4]);
1056
1057impl IntoIterator for Deal {
1058    type Item = Hand;
1059    type IntoIter = core::array::IntoIter<Hand, 4>;
1060
1061    #[inline]
1062    fn into_iter(self) -> Self::IntoIter {
1063        self.0.into_iter()
1064    }
1065}
1066
1067impl ops::Index<Seat> for Deal {
1068    type Output = Hand;
1069
1070    #[inline]
1071    fn index(&self, seat: Seat) -> &Hand {
1072        &self.0[seat as usize]
1073    }
1074}
1075
1076impl ops::IndexMut<Seat> for Deal {
1077    #[inline]
1078    fn index_mut(&mut self, seat: Seat) -> &mut Hand {
1079        &mut self.0[seat as usize]
1080    }
1081}
1082
1083impl Deal {
1084    /// Empty deal
1085    pub const EMPTY: Self = Self([Hand::EMPTY; 4]);
1086
1087    /// Construct a deal from four hands
1088    #[must_use]
1089    pub const fn new(north: Hand, east: Hand, south: Hand, west: Hand) -> Self {
1090        Self([north, east, south, west])
1091    }
1092
1093    /// If the deal is a subset of a bridge deal, collect all the cards into a
1094    /// single hand.  Otherwise, return `None`.  This function checks the
1095    /// validity of the deal and also returns potentially useful information.
1096    ///
1097    /// A deal is a subset of a bridge deal if it satisfies the following
1098    /// conditions:
1099    ///
1100    /// 1. Each hand contains at most 13 cards.
1101    /// 2. The hands are pairwise disjoint.
1102    #[must_use]
1103    pub fn validate_and_collect(self) -> Option<Hand> {
1104        let mut seen = Hand::EMPTY;
1105        for hand in self.0 {
1106            if hand.len() > 13 || hand & seen != Hand::EMPTY {
1107                return None;
1108            }
1109            seen |= hand;
1110        }
1111        Some(seen)
1112    }
1113
1114    /// PBN-compatible display from a seat's perspective
1115    #[must_use]
1116    pub fn display(self, seat: Seat) -> impl fmt::Display {
1117        struct DisplayAt {
1118            deal: Deal,
1119            seat: Seat,
1120        }
1121        impl fmt::Display for DisplayAt {
1122            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1123                f.write_char(self.seat.letter())?;
1124                f.write_char(':')?;
1125
1126                self.deal[self.seat].fmt(f)?;
1127                f.write_char(' ')?;
1128
1129                self.deal[self.seat.lho()].fmt(f)?;
1130                f.write_char(' ')?;
1131
1132                self.deal[self.seat.partner()].fmt(f)?;
1133                f.write_char(' ')?;
1134
1135                self.deal[self.seat.rho()].fmt(f)
1136            }
1137        }
1138        DisplayAt { deal: self, seat }
1139    }
1140}
1141
1142impl FromStr for Deal {
1143    type Err = ParseDealError;
1144
1145    fn from_str(s: &str) -> Result<Self, Self::Err> {
1146        let bytes = s.as_bytes();
1147
1148        let dealer = match bytes.first().map(u8::to_ascii_uppercase) {
1149            Some(b'N') => Seat::North,
1150            Some(b'E') => Seat::East,
1151            Some(b'S') => Seat::South,
1152            Some(b'W') => Seat::West,
1153            _ => return Err(ParseDealError::InvalidDealer),
1154        };
1155
1156        if bytes.get(1) != Some(&b':') {
1157            return Err(ParseDealError::InvalidDealer);
1158        }
1159
1160        let hands: Result<Vec<_>, _> = s[2..].split_whitespace().map(Hand::from_str).collect();
1161
1162        let mut deal = Self(
1163            hands?
1164                .try_into()
1165                .map_err(|_| ParseDealError::NotFourHands)?,
1166        );
1167        deal.0.rotate_right(dealer as usize);
1168        Ok(deal)
1169    }
1170}
1171
1172#[cfg(feature = "serde")]
1173mod serde_string {
1174    use super::{Card, Deal, Hand, Holding, Seat};
1175    use core::fmt::Display;
1176    use core::str::FromStr;
1177    use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
1178
1179    fn serialize<T: Display, S: Serializer>(value: &T, serializer: S) -> Result<S::Ok, S::Error> {
1180        serializer.collect_str(value)
1181    }
1182
1183    fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
1184    where
1185        T: FromStr,
1186        T::Err: Display,
1187        D: Deserializer<'de>,
1188    {
1189        let s = <&str>::deserialize(deserializer)?;
1190        s.parse().map_err(de::Error::custom)
1191    }
1192
1193    impl Serialize for Card {
1194        fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1195            serialize(self, s)
1196        }
1197    }
1198    impl<'de> Deserialize<'de> for Card {
1199        fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
1200            deserialize(d)
1201        }
1202    }
1203
1204    impl Serialize for Holding {
1205        fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1206            serialize(self, s)
1207        }
1208    }
1209    impl<'de> Deserialize<'de> for Holding {
1210        fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
1211            deserialize(d)
1212        }
1213    }
1214
1215    impl Serialize for Hand {
1216        fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1217            serialize(self, s)
1218        }
1219    }
1220    impl<'de> Deserialize<'de> for Hand {
1221        fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
1222            deserialize(d)
1223        }
1224    }
1225
1226    impl Serialize for Deal {
1227        fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1228            s.collect_str(&self.display(Seat::North))
1229        }
1230    }
1231    impl<'de> Deserialize<'de> for Deal {
1232        fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
1233            deserialize(d)
1234        }
1235    }
1236}