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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum Seat {
12 North,
14 East,
16 South,
18 West,
20}
21
22impl Seat {
23 pub const ALL: [Self; 4] = [Self::North, Self::East, Self::South, Self::West];
25
26 #[must_use]
28 pub const fn partner(self) -> Self {
29 match self {
30 Self::North => Self::South,
31 Self::East => Self::West,
32 Self::South => Self::North,
33 Self::West => Self::East,
34 }
35 }
36
37 #[must_use]
39 pub const fn lho(self) -> Self {
40 match self {
41 Self::North => Self::East,
42 Self::East => Self::South,
43 Self::South => Self::West,
44 Self::West => Self::North,
45 }
46 }
47
48 #[must_use]
50 pub const fn rho(self) -> Self {
51 match self {
52 Self::North => Self::West,
53 Self::East => Self::North,
54 Self::South => Self::East,
55 Self::West => Self::South,
56 }
57 }
58
59 #[must_use]
61 #[inline]
62 pub const fn letter(self) -> char {
63 match self {
64 Self::North => 'N',
65 Self::East => 'E',
66 Self::South => 'S',
67 Self::West => 'W',
68 }
69 }
70}
71
72const _: () = assert!(Seat::ALL[0] as u8 == 0);
73const _: () = assert!(Seat::ALL[1] as u8 == 1);
74const _: () = assert!(Seat::ALL[2] as u8 == 2);
75const _: () = assert!(Seat::ALL[3] as u8 == 3);
76
77impl fmt::Display for Seat {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 f.write_char(self.letter())
80 }
81}
82
83#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
85#[error("Invalid seat: expected one of N, E, S, W (or full names)")]
86pub struct ParseSeatError;
87
88impl FromStr for Seat {
89 type Err = ParseSeatError;
90
91 fn from_str(s: &str) -> Result<Self, Self::Err> {
92 match s.to_ascii_uppercase().as_str() {
93 "N" | "NORTH" => Ok(Self::North),
94 "E" | "EAST" => Ok(Self::East),
95 "S" | "SOUTH" => Ok(Self::South),
96 "W" | "WEST" => Ok(Self::West),
97 _ => Err(ParseSeatError),
98 }
99 }
100}
101
102bitflags::bitflags! {
103 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105 pub struct SeatFlags: u8 {
106 const NORTH = 0b0001;
108 const EAST = 0b0010;
110 const SOUTH = 0b0100;
112 const WEST = 0b1000;
114 }
115}
116
117impl SeatFlags {
118 pub const EMPTY: Self = Self::empty();
120
121 pub const ALL: Self = Self::all();
123
124 pub const NS: Self = Self::NORTH.union(Self::SOUTH);
126
127 pub const EW: Self = Self::EAST.union(Self::WEST);
129}
130
131impl From<Seat> for SeatFlags {
132 fn from(seat: Seat) -> Self {
133 match seat {
134 Seat::North => Self::NORTH,
135 Seat::East => Self::EAST,
136 Seat::South => Self::SOUTH,
137 Seat::West => Self::WEST,
138 }
139 }
140}
141
142#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
147#[error("{0} is not a valid rank (2..=14)")]
148pub struct InvalidRank(u8);
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
153pub struct Rank(NonZero<u8>);
154
155impl Rank {
156 pub const A: Self = Self(NonZero::new(14).unwrap());
158
159 pub const K: Self = Self(NonZero::new(13).unwrap());
161
162 pub const Q: Self = Self(NonZero::new(12).unwrap());
164
165 pub const J: Self = Self(NonZero::new(11).unwrap());
167
168 pub const T: Self = Self(NonZero::new(10).unwrap());
170
171 #[must_use]
178 #[inline]
179 pub const fn new(rank: u8) -> Self {
180 match Self::try_new(rank) {
181 Ok(r) => r,
182 Err(_) => panic!("rank must be in 2..=14"),
183 }
184 }
185
186 #[inline]
192 pub const fn try_new(rank: u8) -> Result<Self, InvalidRank> {
193 match NonZero::new(rank) {
194 Some(nonzero) if rank >= 2 && rank <= 14 => Ok(Self(nonzero)),
195 _ => Err(InvalidRank(rank)),
196 }
197 }
198
199 #[must_use]
201 #[inline]
202 pub const fn get(self) -> u8 {
203 self.0.get()
204 }
205
206 #[must_use]
208 #[inline]
209 pub const fn letter(self) -> char {
210 b"23456789TJQKA"[self.get() as usize - 2] as char
211 }
212}
213
214impl fmt::Display for Rank {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 f.write_char(self.letter())
217 }
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
224pub struct Card(NonZero<u8>);
225
226impl Card {
227 #[must_use]
229 #[inline]
230 pub const fn new(suit: Suit, rank: Rank) -> Self {
231 Self(unsafe { NonZero::new_unchecked(rank.get() << 2 | suit as u8) })
233 }
234
235 #[must_use]
237 #[inline]
238 pub const fn suit(self) -> Suit {
239 unsafe { core::mem::transmute(self.0.get() & 3) }
241 }
242
243 #[must_use]
245 #[inline]
246 pub const fn rank(self) -> Rank {
247 unsafe { Rank(core::num::NonZero::new_unchecked(self.0.get() >> 2)) }
249 }
250}
251
252impl fmt::Display for Card {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 write!(f, "{}{}", self.suit(), self.rank())
255 }
256}
257
258#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
260pub struct Holding(u16);
261
262#[derive(Debug, Clone, PartialEq, Eq)]
264pub struct HoldingIter {
265 rest: u16,
266 cursor: u8,
267}
268
269impl Iterator for HoldingIter {
270 type Item = Rank;
271
272 fn next(&mut self) -> Option<Self::Item> {
273 if self.rest == 0 {
274 return None;
275 }
276
277 #[allow(clippy::cast_possible_truncation)]
280 let pos = 15 - self.rest.leading_zeros() as u8;
281 self.rest &= !(1u16 << pos);
282 let rank = self.cursor + pos;
283
284 Some(Rank(unsafe { core::num::NonZero::new_unchecked(rank) }))
286 }
287
288 fn size_hint(&self) -> (usize, Option<usize>) {
289 let count = self.rest.count_ones() as usize;
290 (count, Some(count))
291 }
292
293 fn count(self) -> usize {
294 self.rest.count_ones() as usize
295 }
296}
297
298impl DoubleEndedIterator for HoldingIter {
299 fn next_back(&mut self) -> Option<Self::Item> {
300 if self.rest == 0 {
301 return None;
302 }
303
304 #[allow(clippy::cast_possible_truncation)]
307 let step = self.rest.trailing_zeros() as u8 + 1;
308 self.rest >>= step;
309 self.cursor += step;
310
311 Some(Rank(unsafe {
313 core::num::NonZero::new_unchecked(self.cursor - 1)
314 }))
315 }
316}
317
318impl ExactSizeIterator for HoldingIter {
319 fn len(&self) -> usize {
320 self.rest.count_ones() as usize
321 }
322}
323
324impl FusedIterator for HoldingIter {}
325
326impl Holding {
327 pub const EMPTY: Self = Self(0);
329
330 pub const ALL: Self = Self(0x7FFC);
332
333 #[must_use]
335 #[inline]
336 pub const fn len(self) -> usize {
337 self.0.count_ones() as usize
338 }
339
340 #[must_use]
342 #[inline]
343 pub const fn is_empty(self) -> bool {
344 self.0 == 0
345 }
346
347 #[must_use]
349 #[inline]
350 pub const fn contains(self, rank: Rank) -> bool {
351 self.0 & 1 << rank.get() != 0
352 }
353
354 #[inline]
356 pub const fn insert(&mut self, rank: Rank) -> bool {
357 let insertion = 1 << rank.get() & Self::ALL.0;
358 let inserted = insertion & !self.0 != 0;
359 self.0 |= insertion;
360 inserted
361 }
362
363 #[inline]
365 pub const fn remove(&mut self, rank: Rank) -> bool {
366 let removed = self.contains(rank);
367 self.0 &= !(1 << rank.get());
368 removed
369 }
370
371 #[inline]
373 pub const fn toggle(&mut self, rank: Rank) -> bool {
374 self.0 ^= 1 << rank.get() & Self::ALL.0;
375 self.contains(rank)
376 }
377
378 #[inline]
380 pub fn set(&mut self, rank: Rank, condition: bool) {
381 let flag = 1 << rank.get();
382 let mask = u16::from(condition).wrapping_neg();
383 self.0 = (self.0 & !flag) | (mask & flag);
384 }
385
386 #[inline]
388 #[must_use]
389 pub const fn iter(self) -> HoldingIter {
390 HoldingIter {
391 rest: self.0,
392 cursor: 0,
393 }
394 }
395
396 #[must_use]
398 #[inline]
399 pub const fn to_bits(self) -> u16 {
400 self.0
401 }
402
403 #[must_use]
405 #[inline]
406 pub const fn from_bits_retain(bits: u16) -> Self {
407 Self(bits)
408 }
409
410 #[must_use]
412 #[inline]
413 pub const fn contains_unknown_bits(self) -> bool {
414 self.0 & Self::ALL.0 != self.0
415 }
416
417 #[must_use]
419 #[inline]
420 pub const fn from_bits(bits: u16) -> Option<Self> {
421 if bits & Self::ALL.0 == bits {
422 Some(Self(bits))
423 } else {
424 None
425 }
426 }
427
428 #[must_use]
430 #[inline]
431 pub const fn from_bits_truncate(bits: u16) -> Self {
432 Self(bits & Self::ALL.0)
433 }
434
435 #[must_use]
437 #[inline]
438 pub const fn from_rank(rank: Rank) -> Self {
439 Self(1 << rank.get())
440 }
441}
442
443impl IntoIterator for Holding {
444 type Item = Rank;
445 type IntoIter = HoldingIter;
446
447 fn into_iter(self) -> HoldingIter {
448 self.iter()
449 }
450}
451
452impl ops::BitAnd for Holding {
453 type Output = Self;
454
455 fn bitand(self, rhs: Self) -> Self {
456 Self(self.0 & rhs.0)
457 }
458}
459
460impl ops::BitOr for Holding {
461 type Output = Self;
462
463 fn bitor(self, rhs: Self) -> Self {
464 Self(self.0 | rhs.0)
465 }
466}
467
468impl ops::BitXor for Holding {
469 type Output = Self;
470
471 fn bitxor(self, rhs: Self) -> Self {
472 Self(self.0 ^ rhs.0)
473 }
474}
475
476impl ops::Not for Holding {
477 type Output = Self;
478
479 fn not(self) -> Self {
480 Self::from_bits_truncate(!self.0)
481 }
482}
483
484impl ops::Sub for Holding {
485 type Output = Self;
486
487 fn sub(self, rhs: Self) -> Self {
488 Self(self.0 & !rhs.0)
489 }
490}
491
492impl ops::BitAndAssign for Holding {
493 fn bitand_assign(&mut self, rhs: Self) {
494 *self = *self & rhs;
495 }
496}
497
498impl ops::BitOrAssign for Holding {
499 fn bitor_assign(&mut self, rhs: Self) {
500 *self = *self | rhs;
501 }
502}
503
504impl ops::BitXorAssign for Holding {
505 fn bitxor_assign(&mut self, rhs: Self) {
506 *self = *self ^ rhs;
507 }
508}
509
510impl ops::SubAssign for Holding {
511 fn sub_assign(&mut self, rhs: Self) {
512 *self = *self - rhs;
513 }
514}
515
516impl fmt::Display for Holding {
522 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
523 for rank in (2u8..15).rev() {
524 if self.0 & 1 << rank != 0 {
525 f.write_char(b"23456789TJQKA"[rank as usize - 2] as char)?;
526 }
527 }
528 Ok(())
529 }
530}
531
532#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
534pub enum ParseHoldingError {
535 #[error("Ranks are not all valid or in descending order")]
537 InvalidRanks,
538
539 #[error("The same rank appears more than once")]
541 RepeatedRank,
542
543 #[error("A suit contains more than 13 cards")]
545 TooManyCards,
546}
547
548#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
550pub enum ParseHandError {
551 #[error(transparent)]
553 Holding(#[from] ParseHoldingError),
554
555 #[error("The hand does not contain 4 suits")]
557 NotFourSuits,
558}
559
560#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
562pub enum ParseDealError {
563 #[error("Invalid dealer tag for a deal")]
565 InvalidDealer,
566
567 #[error(transparent)]
569 Hand(#[from] ParseHandError),
570
571 #[error("The deal does not contain 4 hands")]
573 NotFourHands,
574}
575
576impl FromStr for Holding {
577 type Err = ParseHoldingError;
578
579 fn from_str(s: &str) -> Result<Self, Self::Err> {
580 if s.len() > 14 {
582 return Err(ParseHoldingError::TooManyCards);
583 }
584
585 let bytes = s.as_bytes();
586 let mut i = 0;
587 let mut prev_rank: u8 = 15;
588 let mut explicit = Self::EMPTY;
589
590 while i < bytes.len() {
591 let c = bytes[i].to_ascii_uppercase();
592 let rank: u8 = match c {
593 b'A' => 14,
594 b'K' => 13,
595 b'Q' => 12,
596 b'J' => 11,
597 b'T' => 10,
598 b'1' => {
599 if bytes.get(i + 1) != Some(&b'0') {
600 return Err(ParseHoldingError::InvalidRanks);
601 }
602 i += 1;
603 10
604 }
605 b'2'..=b'9' => c - b'0',
606 b'X' => break,
607 _ => return Err(ParseHoldingError::InvalidRanks),
608 };
609
610 if rank >= prev_rank {
611 return Err(ParseHoldingError::InvalidRanks);
612 }
613 prev_rank = rank;
614
615 let r = Rank(unsafe { core::num::NonZero::new_unchecked(rank) });
617 explicit.insert(r);
618 i += 1;
619 }
620
621 let spot_count = bytes.len() - i;
622 if bytes[i..].iter().any(|&b| !b.eq_ignore_ascii_case(&b'x')) {
623 return Err(ParseHoldingError::InvalidRanks);
624 }
625 if spot_count > 13 {
626 return Err(ParseHoldingError::TooManyCards);
627 }
628
629 let spots = Self::from_bits_truncate((4u16 << spot_count) - 4);
630
631 if explicit & spots == Self::EMPTY {
632 Ok(explicit | spots)
633 } else {
634 Err(ParseHoldingError::RepeatedRank)
635 }
636 }
637}
638
639#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
641pub struct Hand([Holding; 4]);
642
643#[derive(Debug, Clone, PartialEq, Eq)]
646pub struct HandIter {
647 suits: [HoldingIter; 4],
648 fwd: u8,
649 bwd: u8,
650}
651
652impl Iterator for HandIter {
653 type Item = Card;
654
655 fn next(&mut self) -> Option<Self::Item> {
656 loop {
657 if self.fwd > 3 {
658 return None;
659 }
660 let suit = Suit::ASC[self.fwd as usize];
661 if let Some(rank) = self.suits[self.fwd as usize].next() {
662 return Some(Card::new(suit, rank));
663 }
664 if self.fwd == self.bwd {
665 self.fwd = 4;
666 return None;
667 }
668 self.fwd -= 1;
669 }
670 }
671
672 fn size_hint(&self) -> (usize, Option<usize>) {
673 let count = self.len();
674 (count, Some(count))
675 }
676
677 fn count(self) -> usize {
678 self.len()
679 }
680}
681
682impl DoubleEndedIterator for HandIter {
683 fn next_back(&mut self) -> Option<Self::Item> {
684 loop {
685 if self.fwd > 3 {
686 return None;
687 }
688 let suit = Suit::ASC[self.bwd as usize];
689 if let Some(rank) = self.suits[self.bwd as usize].next_back() {
690 return Some(Card::new(suit, rank));
691 }
692 if self.fwd == self.bwd {
693 self.fwd = 4;
694 return None;
695 }
696 self.bwd += 1;
697 }
698 }
699}
700
701impl ExactSizeIterator for HandIter {
702 fn len(&self) -> usize {
703 if self.fwd > 3 {
704 return 0;
705 }
706 (self.bwd as usize..=self.fwd as usize)
707 .map(|i| self.suits[i].len())
708 .sum()
709 }
710}
711
712impl FusedIterator for HandIter {}
713
714impl ops::Index<Suit> for Hand {
715 type Output = Holding;
716
717 #[inline]
718 fn index(&self, suit: Suit) -> &Holding {
719 &self.0[suit as usize]
720 }
721}
722
723impl ops::IndexMut<Suit> for Hand {
724 #[inline]
725 fn index_mut(&mut self, suit: Suit) -> &mut Holding {
726 &mut self.0[suit as usize]
727 }
728}
729
730impl Hand {
731 #[must_use]
733 #[inline]
734 pub const fn to_bits(self) -> u64 {
735 unsafe { core::mem::transmute(self.0) }
736 }
737
738 #[must_use]
740 #[inline]
741 pub const fn from_bits_retain(bits: u64) -> Self {
742 unsafe { core::mem::transmute(bits) }
743 }
744
745 #[must_use]
747 #[inline]
748 pub const fn contains_unknown_bits(self) -> bool {
749 self.to_bits() & Self::ALL.to_bits() != self.to_bits()
750 }
751
752 #[must_use]
754 #[inline]
755 pub const fn from_bits(bits: u64) -> Option<Self> {
756 if bits & Self::ALL.to_bits() == bits {
757 Some(Self::from_bits_retain(bits))
758 } else {
759 None
760 }
761 }
762
763 #[must_use]
765 #[inline]
766 pub const fn from_bits_truncate(bits: u64) -> Self {
767 Self::from_bits_retain(bits & Self::ALL.to_bits())
768 }
769
770 #[must_use]
772 #[inline]
773 pub const fn new(clubs: Holding, diamonds: Holding, hearts: Holding, spades: Holding) -> Self {
774 Self([clubs, diamonds, hearts, spades])
775 }
776
777 pub const EMPTY: Self = Self([Holding::EMPTY; 4]);
779
780 pub const ALL: Self = Self([Holding::ALL; 4]);
782
783 #[must_use]
785 #[inline]
786 pub const fn len(self) -> usize {
787 self.to_bits().count_ones() as usize
788 }
789
790 #[must_use]
792 #[inline]
793 pub const fn is_empty(self) -> bool {
794 self.to_bits() == 0
795 }
796
797 #[must_use]
799 #[inline]
800 pub fn contains(self, card: Card) -> bool {
801 self[card.suit()].contains(card.rank())
802 }
803
804 #[inline]
806 pub fn insert(&mut self, card: Card) -> bool {
807 self[card.suit()].insert(card.rank())
808 }
809
810 #[inline]
812 pub fn remove(&mut self, card: Card) -> bool {
813 self[card.suit()].remove(card.rank())
814 }
815
816 #[inline]
818 pub fn toggle(&mut self, card: Card) -> bool {
819 self[card.suit()].toggle(card.rank())
820 }
821
822 #[inline]
824 pub fn set(&mut self, card: Card, condition: bool) {
825 self[card.suit()].set(card.rank(), condition);
826 }
827
828 #[inline]
830 #[must_use]
831 pub const fn iter(self) -> HandIter {
832 HandIter {
833 suits: [
834 self.0[0].iter(),
835 self.0[1].iter(),
836 self.0[2].iter(),
837 self.0[3].iter(),
838 ],
839 fwd: 3,
840 bwd: 0,
841 }
842 }
843}
844
845impl IntoIterator for Hand {
846 type Item = Card;
847 type IntoIter = HandIter;
848
849 #[inline]
850 fn into_iter(self) -> HandIter {
851 self.iter()
852 }
853}
854
855impl FromIterator<Card> for Hand {
856 fn from_iter<I: IntoIterator<Item = Card>>(iter: I) -> Self {
857 iter.into_iter().fold(Self::EMPTY, |mut hand, card| {
858 hand.insert(card);
859 hand
860 })
861 }
862}
863
864impl fmt::Display for Hand {
868 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
869 if self.is_empty() {
870 return f.write_char('-');
871 }
872
873 self[Suit::Spades].fmt(f)?;
874 f.write_char('.')?;
875
876 self[Suit::Hearts].fmt(f)?;
877 f.write_char('.')?;
878
879 self[Suit::Diamonds].fmt(f)?;
880 f.write_char('.')?;
881
882 self[Suit::Clubs].fmt(f)
883 }
884}
885
886impl FromStr for Hand {
887 type Err = ParseHandError;
888
889 fn from_str(s: &str) -> Result<Self, Self::Err> {
890 if s.len() > 52 + 4 + 3 {
892 return Err(ParseHoldingError::TooManyCards.into());
893 }
894
895 if s == "-" {
896 return Ok(Self::EMPTY);
897 }
898
899 let holdings: Result<Vec<_>, _> = s.split('.').map(Holding::from_str).rev().collect();
900
901 Ok(Self(
902 holdings?
903 .try_into()
904 .map_err(|_| ParseHandError::NotFourSuits)?,
905 ))
906 }
907}
908
909impl ops::BitAnd for Hand {
910 type Output = Self;
911
912 #[inline]
913 fn bitand(self, rhs: Self) -> Self {
914 Self::from_bits_retain(self.to_bits() & rhs.to_bits())
915 }
916}
917
918impl ops::BitOr for Hand {
919 type Output = Self;
920
921 #[inline]
922 fn bitor(self, rhs: Self) -> Self {
923 Self::from_bits_retain(self.to_bits() | rhs.to_bits())
924 }
925}
926
927impl ops::BitXor for Hand {
928 type Output = Self;
929
930 #[inline]
931 fn bitxor(self, rhs: Self) -> Self {
932 Self::from_bits_retain(self.to_bits() ^ rhs.to_bits())
933 }
934}
935
936impl ops::Not for Hand {
937 type Output = Self;
938
939 #[inline]
940 fn not(self) -> Self {
941 Self::from_bits_truncate(!self.to_bits())
942 }
943}
944
945impl ops::Sub for Hand {
946 type Output = Self;
947
948 #[inline]
949 fn sub(self, rhs: Self) -> Self {
950 Self::from_bits_retain(self.to_bits() & !rhs.to_bits())
951 }
952}
953
954impl ops::BitAndAssign for Hand {
955 #[inline]
956 fn bitand_assign(&mut self, rhs: Self) {
957 *self = *self & rhs;
958 }
959}
960
961impl ops::BitOrAssign for Hand {
962 #[inline]
963 fn bitor_assign(&mut self, rhs: Self) {
964 *self = *self | rhs;
965 }
966}
967
968impl ops::BitXorAssign for Hand {
969 #[inline]
970 fn bitxor_assign(&mut self, rhs: Self) {
971 *self = *self ^ rhs;
972 }
973}
974
975impl ops::SubAssign for Hand {
976 #[inline]
977 fn sub_assign(&mut self, rhs: Self) {
978 *self = *self - rhs;
979 }
980}
981
982#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
984pub struct Deal([Hand; 4]);
985
986impl IntoIterator for Deal {
987 type Item = Hand;
988 type IntoIter = core::array::IntoIter<Hand, 4>;
989
990 #[inline]
991 fn into_iter(self) -> Self::IntoIter {
992 self.0.into_iter()
993 }
994}
995
996impl ops::Index<Seat> for Deal {
997 type Output = Hand;
998
999 #[inline]
1000 fn index(&self, seat: Seat) -> &Hand {
1001 &self.0[seat as usize]
1002 }
1003}
1004
1005impl ops::IndexMut<Seat> for Deal {
1006 #[inline]
1007 fn index_mut(&mut self, seat: Seat) -> &mut Hand {
1008 &mut self.0[seat as usize]
1009 }
1010}
1011
1012impl Deal {
1013 pub const EMPTY: Self = Self([Hand::EMPTY; 4]);
1015
1016 #[must_use]
1018 pub const fn new(north: Hand, east: Hand, south: Hand, west: Hand) -> Self {
1019 Self([north, east, south, west])
1020 }
1021
1022 #[must_use]
1032 pub fn validate_and_collect(self) -> Option<Hand> {
1033 let mut seen = Hand::EMPTY;
1034 for hand in self.0 {
1035 if hand.len() > 13 || hand & seen != Hand::EMPTY {
1036 return None;
1037 }
1038 seen |= hand;
1039 }
1040 Some(seen)
1041 }
1042
1043 #[must_use]
1045 pub fn display(self, seat: Seat) -> impl fmt::Display {
1046 struct DisplayAt {
1047 deal: Deal,
1048 seat: Seat,
1049 }
1050 impl fmt::Display for DisplayAt {
1051 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1052 f.write_char(self.seat.letter())?;
1053 f.write_char(':')?;
1054
1055 self.deal[self.seat].fmt(f)?;
1056 f.write_char(' ')?;
1057
1058 self.deal[self.seat.lho()].fmt(f)?;
1059 f.write_char(' ')?;
1060
1061 self.deal[self.seat.partner()].fmt(f)?;
1062 f.write_char(' ')?;
1063
1064 self.deal[self.seat.rho()].fmt(f)
1065 }
1066 }
1067 DisplayAt { deal: self, seat }
1068 }
1069}
1070
1071impl FromStr for Deal {
1072 type Err = ParseDealError;
1073
1074 fn from_str(s: &str) -> Result<Self, Self::Err> {
1075 let bytes = s.as_bytes();
1076
1077 let dealer = match bytes.first().map(u8::to_ascii_uppercase) {
1078 Some(b'N') => Seat::North,
1079 Some(b'E') => Seat::East,
1080 Some(b'S') => Seat::South,
1081 Some(b'W') => Seat::West,
1082 _ => return Err(ParseDealError::InvalidDealer),
1083 };
1084
1085 if bytes.get(1) != Some(&b':') {
1086 return Err(ParseDealError::InvalidDealer);
1087 }
1088
1089 let hands: Result<Vec<_>, _> = s[2..].split_whitespace().map(Hand::from_str).collect();
1090
1091 let mut deal = Self(
1092 hands?
1093 .try_into()
1094 .map_err(|_| ParseDealError::NotFourHands)?,
1095 );
1096 deal.0.rotate_right(dealer as usize);
1097 Ok(deal)
1098 }
1099}