1use crate::Suit;
20use core::fmt::{self, Write as _};
21use core::iter::FusedIterator;
22use core::num::NonZero;
23use core::ops;
24use core::str::FromStr;
25use thiserror::Error;
26
27#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
32#[error("{0} is not a valid rank (2..=14)")]
33pub struct InvalidRank(u8);
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[cfg_attr(feature = "serde", serde(transparent))]
40#[repr(transparent)]
41pub struct Rank(NonZero<u8>);
42
43impl Rank {
44 pub const A: Self = Self(NonZero::new(14).unwrap());
46
47 pub const K: Self = Self(NonZero::new(13).unwrap());
49
50 pub const Q: Self = Self(NonZero::new(12).unwrap());
52
53 pub const J: Self = Self(NonZero::new(11).unwrap());
55
56 pub const T: Self = Self(NonZero::new(10).unwrap());
58
59 #[must_use]
66 #[inline]
67 pub const fn new(rank: u8) -> Self {
68 match Self::try_new(rank) {
69 Ok(r) => r,
70 Err(_) => panic!("rank must be in 2..=14"),
71 }
72 }
73
74 #[inline]
80 pub const fn try_new(rank: u8) -> Result<Self, InvalidRank> {
81 match NonZero::new(rank) {
82 Some(nonzero) if rank >= 2 && rank <= 14 => Ok(Self(nonzero)),
83 _ => Err(InvalidRank(rank)),
84 }
85 }
86
87 #[must_use]
89 #[inline]
90 pub const fn get(self) -> u8 {
91 self.0.get()
92 }
93
94 #[must_use]
96 #[inline]
97 pub const fn letter(self) -> char {
98 b"23456789TJQKA"[self.get() as usize - 2] as char
99 }
100}
101
102impl fmt::Display for Rank {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 f.write_char(self.letter())
105 }
106}
107
108#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
110#[error("Invalid rank: expected 2-10, T, J, Q, K, A")]
111pub struct ParseRankError;
112
113impl FromStr for Rank {
114 type Err = ParseRankError;
115 fn from_str(s: &str) -> Result<Self, Self::Err> {
116 match s.to_ascii_uppercase().as_str() {
117 "A" => Ok(Self::A),
118 "K" => Ok(Self::K),
119 "Q" => Ok(Self::Q),
120 "J" => Ok(Self::J),
121 "T" | "10" => Ok(Self::T),
122 "9" => Ok(Self::new(9)),
123 "8" => Ok(Self::new(8)),
124 "7" => Ok(Self::new(7)),
125 "6" => Ok(Self::new(6)),
126 "5" => Ok(Self::new(5)),
127 "4" => Ok(Self::new(4)),
128 "3" => Ok(Self::new(3)),
129 "2" => Ok(Self::new(2)),
130 _ => Err(ParseRankError),
131 }
132 }
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
139#[cfg_attr(
140 feature = "serde",
141 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
142)]
143pub struct Card {
144 pub suit: Suit,
146 pub rank: Rank,
148}
149
150impl fmt::Display for Card {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 write!(f, "{}{}", self.suit, self.rank)
153 }
154}
155
156#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
158#[non_exhaustive]
159pub enum ParseCardError {
160 #[error("Invalid suit in card: expected one of C, D, H, S, ♣, ♦, ♥, ♠, ♧, ♢, ♡, ♤")]
162 Suit,
163 #[error("Invalid rank in card: expected 2-10, T, J, Q, K, A")]
165 Rank,
166}
167
168impl FromStr for Card {
169 type Err = ParseCardError;
170 fn from_str(s: &str) -> Result<Self, Self::Err> {
171 let rank_len = if s.ends_with("10") {
172 2
173 } else {
174 s.chars().next_back().map_or(0, char::len_utf8)
175 };
176 let border = s.len().saturating_sub(rank_len);
177 let (suit, rank) = s.split_at(border);
178 let suit: Suit = suit.parse().map_err(|_| ParseCardError::Suit)?;
179 let rank: Rank = rank.parse().map_err(|_| ParseCardError::Rank)?;
180 Ok(Self { suit, rank })
181 }
182}
183
184#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
186#[cfg_attr(
187 feature = "serde",
188 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
189)]
190#[repr(transparent)]
191pub struct Holding(u16);
192
193#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct HoldingIter {
196 rest: u16,
197 cursor: u8,
198}
199
200impl Iterator for HoldingIter {
201 type Item = Rank;
202
203 fn next(&mut self) -> Option<Self::Item> {
204 if self.rest == 0 {
205 return None;
206 }
207
208 #[allow(clippy::cast_possible_truncation)]
211 let pos = 15 - self.rest.leading_zeros() as u8;
212 self.rest &= !(1u16 << pos);
213 let rank = self.cursor + pos;
214
215 Some(Rank(unsafe { core::num::NonZero::new_unchecked(rank) }))
217 }
218
219 fn size_hint(&self) -> (usize, Option<usize>) {
220 let count = self.rest.count_ones() as usize;
221 (count, Some(count))
222 }
223
224 fn count(self) -> usize {
225 self.rest.count_ones() as usize
226 }
227}
228
229impl DoubleEndedIterator for HoldingIter {
230 fn next_back(&mut self) -> Option<Self::Item> {
231 if self.rest == 0 {
232 return None;
233 }
234
235 #[allow(clippy::cast_possible_truncation)]
238 let step = self.rest.trailing_zeros() as u8 + 1;
239 self.rest >>= step;
240 self.cursor += step;
241
242 Some(Rank(unsafe {
244 core::num::NonZero::new_unchecked(self.cursor - 1)
245 }))
246 }
247}
248
249impl ExactSizeIterator for HoldingIter {
250 fn len(&self) -> usize {
251 self.rest.count_ones() as usize
252 }
253}
254
255impl FusedIterator for HoldingIter {}
256
257impl Holding {
258 pub const EMPTY: Self = Self(0);
260
261 pub const ALL: Self = Self(0x7FFC);
263
264 #[must_use]
266 #[inline]
267 pub const fn len(self) -> usize {
268 self.0.count_ones() as usize
269 }
270
271 #[must_use]
273 #[inline]
274 pub const fn is_empty(self) -> bool {
275 self.0 == 0
276 }
277
278 #[must_use]
280 #[inline]
281 pub const fn contains(self, rank: Rank) -> bool {
282 self.0 & 1 << rank.get() != 0
283 }
284
285 #[inline]
287 pub const fn insert(&mut self, rank: Rank) -> bool {
288 let insertion = 1 << rank.get() & Self::ALL.0;
289 let inserted = insertion & !self.0 != 0;
290 self.0 |= insertion;
291 inserted
292 }
293
294 #[inline]
296 pub const fn remove(&mut self, rank: Rank) -> bool {
297 let removed = self.contains(rank);
298 self.0 &= !(1 << rank.get());
299 removed
300 }
301
302 #[inline]
304 pub const fn toggle(&mut self, rank: Rank) -> bool {
305 self.0 ^= 1 << rank.get() & Self::ALL.0;
306 self.contains(rank)
307 }
308
309 #[inline]
311 pub const fn set(&mut self, rank: Rank, condition: bool) {
312 let flag = 1 << rank.get();
313 let mask = (condition as u16).wrapping_neg();
314 self.0 = (self.0 & !flag) | (mask & flag);
315 }
316
317 #[inline]
319 #[must_use]
320 pub const fn iter(self) -> HoldingIter {
321 HoldingIter {
322 rest: self.0,
323 cursor: 0,
324 }
325 }
326
327 #[must_use]
329 #[inline]
330 pub const fn to_bits(self) -> u16 {
331 self.0
332 }
333
334 #[must_use]
336 #[inline]
337 pub const fn from_bits_retain(bits: u16) -> Self {
338 Self(bits)
339 }
340
341 #[must_use]
343 #[inline]
344 pub const fn contains_unknown_bits(self) -> bool {
345 self.0 & Self::ALL.0 != self.0
346 }
347
348 #[must_use]
350 #[inline]
351 pub const fn from_bits(bits: u16) -> Option<Self> {
352 if bits & Self::ALL.0 == bits {
353 Some(Self(bits))
354 } else {
355 None
356 }
357 }
358
359 #[must_use]
361 #[inline]
362 pub const fn from_bits_truncate(bits: u16) -> Self {
363 Self(bits & Self::ALL.0)
364 }
365
366 #[must_use]
368 #[inline]
369 pub const fn from_rank(rank: Rank) -> Self {
370 Self(1 << rank.get())
371 }
372}
373
374impl IntoIterator for Holding {
375 type Item = Rank;
376 type IntoIter = HoldingIter;
377
378 fn into_iter(self) -> HoldingIter {
379 self.iter()
380 }
381}
382
383impl ops::BitAnd for Holding {
384 type Output = Self;
385
386 #[inline]
387 fn bitand(self, rhs: Self) -> Self {
388 Self(self.0 & rhs.0)
389 }
390}
391
392impl ops::BitOr for Holding {
393 type Output = Self;
394
395 #[inline]
396 fn bitor(self, rhs: Self) -> Self {
397 Self(self.0 | rhs.0)
398 }
399}
400
401impl ops::BitXor for Holding {
402 type Output = Self;
403
404 #[inline]
405 fn bitxor(self, rhs: Self) -> Self {
406 Self(self.0 ^ rhs.0)
407 }
408}
409
410impl ops::Not for Holding {
411 type Output = Self;
412
413 #[inline]
414 fn not(self) -> Self {
415 Self::from_bits_truncate(!self.0)
416 }
417}
418
419impl ops::Sub for Holding {
420 type Output = Self;
421
422 #[inline]
423 fn sub(self, rhs: Self) -> Self {
424 Self(self.0 & !rhs.0)
425 }
426}
427
428impl ops::BitAndAssign for Holding {
429 #[inline]
430 fn bitand_assign(&mut self, rhs: Self) {
431 *self = *self & rhs;
432 }
433}
434
435impl ops::BitOrAssign for Holding {
436 #[inline]
437 fn bitor_assign(&mut self, rhs: Self) {
438 *self = *self | rhs;
439 }
440}
441
442impl ops::BitXorAssign for Holding {
443 #[inline]
444 fn bitxor_assign(&mut self, rhs: Self) {
445 *self = *self ^ rhs;
446 }
447}
448
449impl ops::SubAssign for Holding {
450 #[inline]
451 fn sub_assign(&mut self, rhs: Self) {
452 *self = *self - rhs;
453 }
454}
455
456impl fmt::Display for Holding {
462 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
463 for rank in (2u8..15).rev() {
464 if self.0 & 1 << rank != 0 {
465 f.write_char(b"23456789TJQKA"[rank as usize - 2] as char)?;
466 }
467 }
468 Ok(())
469 }
470}
471
472#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
474#[non_exhaustive]
475pub enum ParseHoldingError {
476 #[error("Ranks are not all valid or in descending order")]
478 InvalidRanks,
479
480 #[error("The same rank appears more than once")]
482 RepeatedRank,
483
484 #[error("A suit contains more than 13 cards")]
486 TooManyCards,
487}
488
489#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
491#[non_exhaustive]
492pub enum ParseHandError {
493 #[error(transparent)]
495 Holding(#[from] ParseHoldingError),
496
497 #[error("The hand does not contain 4 suits")]
499 NotFourSuits,
500}
501
502impl FromStr for Holding {
503 type Err = ParseHoldingError;
504
505 fn from_str(s: &str) -> Result<Self, Self::Err> {
506 if s.len() > 14 {
508 return Err(ParseHoldingError::TooManyCards);
509 }
510
511 let bytes = s.as_bytes();
512 let mut i = 0;
513 let mut prev_rank: u8 = 15;
514 let mut explicit = Self::EMPTY;
515
516 while i < bytes.len() {
517 let c = bytes[i].to_ascii_uppercase();
518 let rank: u8 = match c {
519 b'A' => 14,
520 b'K' => 13,
521 b'Q' => 12,
522 b'J' => 11,
523 b'T' => 10,
524 b'1' => {
525 if bytes.get(i + 1) != Some(&b'0') {
526 return Err(ParseHoldingError::InvalidRanks);
527 }
528 i += 1;
529 10
530 }
531 b'2'..=b'9' => c - b'0',
532 b'X' => break,
533 _ => return Err(ParseHoldingError::InvalidRanks),
534 };
535
536 if rank >= prev_rank {
537 return Err(ParseHoldingError::InvalidRanks);
538 }
539 prev_rank = rank;
540
541 let r = Rank(unsafe { core::num::NonZero::new_unchecked(rank) });
543 explicit.insert(r);
544 i += 1;
545 }
546
547 let spot_count = bytes.len() - i;
548 if bytes[i..].iter().any(|&b| !b.eq_ignore_ascii_case(&b'x')) {
549 return Err(ParseHoldingError::InvalidRanks);
550 }
551 if spot_count > 13 {
552 return Err(ParseHoldingError::TooManyCards);
553 }
554
555 let spots = Self::from_bits_truncate((4u16 << spot_count) - 4);
556
557 if explicit & spots == Self::EMPTY {
558 Ok(explicit | spots)
559 } else {
560 Err(ParseHoldingError::RepeatedRank)
561 }
562 }
563}
564
565#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
567#[cfg_attr(
568 feature = "serde",
569 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
570)]
571pub struct Hand([Holding; 4]);
572
573#[derive(Debug, Clone, PartialEq, Eq)]
576pub struct HandIter {
577 suits: [HoldingIter; 4],
578 fwd: u8,
579 bwd: u8,
580}
581
582impl Iterator for HandIter {
583 type Item = Card;
584
585 fn next(&mut self) -> Option<Self::Item> {
586 loop {
587 if self.fwd > 3 {
588 return None;
589 }
590 let suit = Suit::ASC[self.fwd as usize];
591 if let Some(rank) = self.suits[self.fwd as usize].next() {
592 return Some(Card { suit, rank });
593 }
594 if self.fwd == self.bwd {
595 self.fwd = 4;
596 return None;
597 }
598 self.fwd -= 1;
599 }
600 }
601
602 fn size_hint(&self) -> (usize, Option<usize>) {
603 let count = self.len();
604 (count, Some(count))
605 }
606
607 fn count(self) -> usize {
608 self.len()
609 }
610}
611
612impl DoubleEndedIterator for HandIter {
613 fn next_back(&mut self) -> Option<Self::Item> {
614 loop {
615 if self.fwd > 3 {
616 return None;
617 }
618 let suit = Suit::ASC[self.bwd as usize];
619 if let Some(rank) = self.suits[self.bwd as usize].next_back() {
620 return Some(Card { suit, rank });
621 }
622 if self.fwd == self.bwd {
623 self.fwd = 4;
624 return None;
625 }
626 self.bwd += 1;
627 }
628 }
629}
630
631impl ExactSizeIterator for HandIter {
632 fn len(&self) -> usize {
633 if self.fwd > 3 {
634 return 0;
635 }
636 (self.bwd as usize..=self.fwd as usize)
637 .map(|i| self.suits[i].len())
638 .sum()
639 }
640}
641
642impl FusedIterator for HandIter {}
643
644impl ops::Index<Suit> for Hand {
645 type Output = Holding;
646
647 #[inline]
648 fn index(&self, suit: Suit) -> &Holding {
649 &self.0[suit as usize]
650 }
651}
652
653impl ops::IndexMut<Suit> for Hand {
654 #[inline]
655 fn index_mut(&mut self, suit: Suit) -> &mut Holding {
656 &mut self.0[suit as usize]
657 }
658}
659
660impl Hand {
661 #[must_use]
663 #[inline]
664 pub const fn to_bits(self) -> u64 {
665 unsafe { core::mem::transmute(self.0) }
666 }
667
668 #[must_use]
670 #[inline]
671 pub const fn from_bits_retain(bits: u64) -> Self {
672 unsafe { core::mem::transmute(bits) }
673 }
674
675 #[must_use]
677 #[inline]
678 pub const fn contains_unknown_bits(self) -> bool {
679 self.to_bits() & Self::ALL.to_bits() != self.to_bits()
680 }
681
682 #[must_use]
684 #[inline]
685 pub const fn from_bits(bits: u64) -> Option<Self> {
686 if bits & Self::ALL.to_bits() == bits {
687 Some(Self::from_bits_retain(bits))
688 } else {
689 None
690 }
691 }
692
693 #[must_use]
695 #[inline]
696 pub const fn from_bits_truncate(bits: u64) -> Self {
697 Self::from_bits_retain(bits & Self::ALL.to_bits())
698 }
699
700 #[must_use]
702 #[inline]
703 pub const fn new(clubs: Holding, diamonds: Holding, hearts: Holding, spades: Holding) -> Self {
704 Self([clubs, diamonds, hearts, spades])
705 }
706
707 pub const EMPTY: Self = Self([Holding::EMPTY; 4]);
709
710 pub const ALL: Self = Self([Holding::ALL; 4]);
712
713 #[must_use]
715 #[inline]
716 pub const fn len(self) -> usize {
717 self.to_bits().count_ones() as usize
718 }
719
720 #[must_use]
722 #[inline]
723 pub const fn is_empty(self) -> bool {
724 self.to_bits() == 0
725 }
726
727 #[must_use]
729 #[inline]
730 pub fn contains(self, card: Card) -> bool {
731 self[card.suit].contains(card.rank)
732 }
733
734 #[inline]
736 pub fn insert(&mut self, card: Card) -> bool {
737 self[card.suit].insert(card.rank)
738 }
739
740 #[inline]
742 pub fn remove(&mut self, card: Card) -> bool {
743 self[card.suit].remove(card.rank)
744 }
745
746 #[inline]
748 pub fn toggle(&mut self, card: Card) -> bool {
749 self[card.suit].toggle(card.rank)
750 }
751
752 #[inline]
754 pub fn set(&mut self, card: Card, condition: bool) {
755 self[card.suit].set(card.rank, condition);
756 }
757
758 #[inline]
760 #[must_use]
761 pub const fn iter(self) -> HandIter {
762 HandIter {
763 suits: [
764 self.0[0].iter(),
765 self.0[1].iter(),
766 self.0[2].iter(),
767 self.0[3].iter(),
768 ],
769 fwd: 3,
770 bwd: 0,
771 }
772 }
773}
774
775impl IntoIterator for Hand {
776 type Item = Card;
777 type IntoIter = HandIter;
778
779 #[inline]
780 fn into_iter(self) -> HandIter {
781 self.iter()
782 }
783}
784
785impl FromIterator<Card> for Hand {
786 fn from_iter<I: IntoIterator<Item = Card>>(iter: I) -> Self {
787 iter.into_iter().fold(Self::EMPTY, |mut hand, card| {
788 hand.insert(card);
789 hand
790 })
791 }
792}
793
794impl fmt::Display for Hand {
798 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
799 if self.is_empty() {
800 return f.write_char('-');
801 }
802
803 self[Suit::Spades].fmt(f)?;
804 f.write_char('.')?;
805
806 self[Suit::Hearts].fmt(f)?;
807 f.write_char('.')?;
808
809 self[Suit::Diamonds].fmt(f)?;
810 f.write_char('.')?;
811
812 self[Suit::Clubs].fmt(f)
813 }
814}
815
816impl FromStr for Hand {
817 type Err = ParseHandError;
818
819 fn from_str(s: &str) -> Result<Self, Self::Err> {
820 if s.len() > 52 + 4 + 3 {
822 return Err(ParseHoldingError::TooManyCards.into());
823 }
824
825 if s == "-" {
826 return Ok(Self::EMPTY);
827 }
828
829 let holdings: Result<Vec<_>, _> = s.split('.').map(Holding::from_str).rev().collect();
830
831 Ok(Self(
832 holdings?
833 .try_into()
834 .map_err(|_| ParseHandError::NotFourSuits)?,
835 ))
836 }
837}
838
839impl ops::BitAnd for Hand {
840 type Output = Self;
841
842 #[inline]
843 fn bitand(self, rhs: Self) -> Self {
844 Self::from_bits_retain(self.to_bits() & rhs.to_bits())
845 }
846}
847
848impl ops::BitOr for Hand {
849 type Output = Self;
850
851 #[inline]
852 fn bitor(self, rhs: Self) -> Self {
853 Self::from_bits_retain(self.to_bits() | rhs.to_bits())
854 }
855}
856
857impl ops::BitXor for Hand {
858 type Output = Self;
859
860 #[inline]
861 fn bitxor(self, rhs: Self) -> Self {
862 Self::from_bits_retain(self.to_bits() ^ rhs.to_bits())
863 }
864}
865
866impl ops::Not for Hand {
867 type Output = Self;
868
869 #[inline]
870 fn not(self) -> Self {
871 Self::from_bits_truncate(!self.to_bits())
872 }
873}
874
875impl ops::Sub for Hand {
876 type Output = Self;
877
878 #[inline]
879 fn sub(self, rhs: Self) -> Self {
880 Self::from_bits_retain(self.to_bits() & !rhs.to_bits())
881 }
882}
883
884impl ops::BitAndAssign for Hand {
885 #[inline]
886 fn bitand_assign(&mut self, rhs: Self) {
887 *self = *self & rhs;
888 }
889}
890
891impl ops::BitOrAssign for Hand {
892 #[inline]
893 fn bitor_assign(&mut self, rhs: Self) {
894 *self = *self | rhs;
895 }
896}
897
898impl ops::BitXorAssign for Hand {
899 #[inline]
900 fn bitxor_assign(&mut self, rhs: Self) {
901 *self = *self ^ rhs;
902 }
903}
904
905impl ops::SubAssign for Hand {
906 #[inline]
907 fn sub_assign(&mut self, rhs: Self) {
908 *self = *self - rhs;
909 }
910}