1use crate::Suit;
21use core::fmt::{self, Write as _};
22use core::iter::FusedIterator;
23use core::num::NonZero;
24use core::ops;
25use core::str::FromStr;
26use thiserror::Error;
27
28#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
33#[error("{0} is not a valid rank (1..=13)")]
34pub struct InvalidRank(pub u8);
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
45#[cfg_attr(
46 feature = "serde",
47 derive(serde::Serialize, serde::Deserialize),
48 serde(try_from = "u8", into = "u8")
49)]
50#[repr(transparent)]
51pub struct Rank(NonZero<u8>);
52
53impl Rank {
54 pub const A: Self = Self(NonZero::new(1).unwrap());
56
57 pub const T: Self = Self(NonZero::new(10).unwrap());
59
60 pub const J: Self = Self(NonZero::new(11).unwrap());
62
63 pub const Q: Self = Self(NonZero::new(12).unwrap());
65
66 pub const K: Self = Self(NonZero::new(13).unwrap());
68
69 #[must_use]
76 #[inline]
77 pub const fn new(rank: u8) -> Self {
78 match Self::try_new(rank) {
79 Ok(r) => r,
80 Err(_) => panic!("rank must be in 1..=13"),
81 }
82 }
83
84 #[inline]
90 pub const fn try_new(rank: u8) -> Result<Self, InvalidRank> {
91 match NonZero::new(rank) {
92 Some(nonzero) if rank <= 13 => Ok(Self(nonzero)),
93 _ => Err(InvalidRank(rank)),
94 }
95 }
96
97 #[must_use]
99 #[inline]
100 pub const fn get(self) -> u8 {
101 self.0.get()
102 }
103
104 #[must_use]
106 #[inline]
107 pub const fn letter(self) -> char {
108 b"A23456789TJQK"[self.get() as usize - 1] as char
109 }
110
111 #[must_use]
114 #[inline]
115 pub const fn deadwood(self) -> u8 {
116 if self.get() > 10 { 10 } else { self.get() }
117 }
118}
119
120impl From<Rank> for u8 {
121 #[inline]
122 fn from(rank: Rank) -> Self {
123 rank.get()
124 }
125}
126
127impl TryFrom<u8> for Rank {
128 type Error = InvalidRank;
129
130 #[inline]
131 fn try_from(rank: u8) -> Result<Self, InvalidRank> {
132 Self::try_new(rank)
133 }
134}
135
136impl fmt::Display for Rank {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 f.write_char(self.letter())
139 }
140}
141
142#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
144#[error("Invalid rank: expected A, 2-10, T, J, Q, K")]
145pub struct ParseRankError;
146
147impl FromStr for Rank {
148 type Err = ParseRankError;
149 fn from_str(s: &str) -> Result<Self, Self::Err> {
150 match s.to_ascii_uppercase().as_str() {
151 "A" => Ok(Self::A),
152 "K" => Ok(Self::K),
153 "Q" => Ok(Self::Q),
154 "J" => Ok(Self::J),
155 "T" | "10" => Ok(Self::T),
156 "9" => Ok(Self::new(9)),
157 "8" => Ok(Self::new(8)),
158 "7" => Ok(Self::new(7)),
159 "6" => Ok(Self::new(6)),
160 "5" => Ok(Self::new(5)),
161 "4" => Ok(Self::new(4)),
162 "3" => Ok(Self::new(3)),
163 "2" => Ok(Self::new(2)),
164 _ => Err(ParseRankError),
165 }
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
171#[cfg_attr(
172 feature = "serde",
173 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
174)]
175pub struct Card {
176 pub suit: Suit,
178 pub rank: Rank,
180}
181
182impl fmt::Display for Card {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 write!(f, "{}{}", self.rank, self.suit)
188 }
189}
190
191#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
193#[non_exhaustive]
194pub enum ParseCardError {
195 #[error("Invalid suit in card: expected one of C, D, H, S, ♣, ♦, ♥, ♠, ♧, ♢, ♡, ♤")]
197 Suit,
198 #[error("Invalid rank in card: expected A, 2-10, T, J, Q, K")]
200 Rank,
201}
202
203impl FromStr for Card {
204 type Err = ParseCardError;
205 fn from_str(s: &str) -> Result<Self, Self::Err> {
213 let build = |rank: &str, suit: &str| -> Result<Self, ParseCardError> {
214 Ok(Self {
215 suit: suit.parse().map_err(|_| ParseCardError::Suit)?,
216 rank: rank.parse().map_err(|_| ParseCardError::Rank)?,
217 })
218 };
219 let lead = if s.starts_with("10") { 2 } else { 1 };
221 if let Some((rank, suit)) = s.split_at_checked(lead)
222 && let Ok(card) = build(rank, suit)
223 {
224 return Ok(card);
225 }
226 let trail = if s.ends_with("10") {
228 2
229 } else {
230 s.chars().next_back().map_or(0, char::len_utf8)
231 };
232 let (suit, rank) = s.split_at(s.len().saturating_sub(trail));
233 build(rank, suit)
234 }
235}
236
237#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
239#[cfg_attr(
240 feature = "serde",
241 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
242)]
243#[repr(transparent)]
244pub struct Holding(u16);
245
246#[derive(Debug, Clone, PartialEq, Eq)]
249pub struct HoldingIter {
250 rest: u16,
251 cursor: u8,
252}
253
254impl Iterator for HoldingIter {
255 type Item = Rank;
256
257 fn next(&mut self) -> Option<Self::Item> {
258 if self.rest == 0 {
259 return None;
260 }
261
262 let step = self.rest.trailing_zeros() as u8 + 1;
265 self.rest >>= step;
266 self.cursor += step;
267
268 Some(Rank(unsafe { NonZero::new_unchecked(self.cursor - 1) }))
270 }
271
272 fn size_hint(&self) -> (usize, Option<usize>) {
273 let count = self.rest.count_ones() as usize;
274 (count, Some(count))
275 }
276
277 fn count(self) -> usize {
278 self.rest.count_ones() as usize
279 }
280}
281
282impl DoubleEndedIterator for HoldingIter {
283 fn next_back(&mut self) -> Option<Self::Item> {
284 if self.rest == 0 {
285 return None;
286 }
287
288 let pos = 15 - self.rest.leading_zeros() as u8;
290 self.rest &= !(1u16 << pos);
291
292 Some(Rank(unsafe { NonZero::new_unchecked(self.cursor + pos) }))
294 }
295}
296
297impl ExactSizeIterator for HoldingIter {
298 fn len(&self) -> usize {
299 self.rest.count_ones() as usize
300 }
301}
302
303impl FusedIterator for HoldingIter {}
304
305impl Holding {
306 pub const EMPTY: Self = Self(0);
308
309 pub const ALL: Self = Self(0x3FFE);
311
312 #[must_use]
314 #[inline]
315 pub const fn len(self) -> usize {
316 self.0.count_ones() as usize
317 }
318
319 #[must_use]
321 #[inline]
322 pub const fn is_empty(self) -> bool {
323 self.0 == 0
324 }
325
326 #[must_use]
328 #[inline]
329 pub const fn contains(self, rank: Rank) -> bool {
330 self.0 & 1 << rank.get() != 0
331 }
332
333 #[inline]
335 pub const fn insert(&mut self, rank: Rank) -> bool {
336 let insertion = 1 << rank.get() & Self::ALL.0;
337 let inserted = insertion & !self.0 != 0;
338 self.0 |= insertion;
339 inserted
340 }
341
342 #[inline]
344 pub const fn remove(&mut self, rank: Rank) -> bool {
345 let removed = self.contains(rank);
346 self.0 &= !(1 << rank.get());
347 removed
348 }
349
350 #[inline]
352 pub const fn toggle(&mut self, rank: Rank) -> bool {
353 self.0 ^= 1 << rank.get() & Self::ALL.0;
354 self.contains(rank)
355 }
356
357 #[inline]
359 pub const fn set(&mut self, rank: Rank, condition: bool) {
360 let flag = 1 << rank.get();
361 let mask = (condition as u16).wrapping_neg();
362 self.0 = (self.0 & !flag) | (mask & flag);
363 }
364
365 #[inline]
370 #[must_use]
371 pub const fn iter(self) -> HoldingIter {
372 HoldingIter {
373 rest: self.0 & Self::ALL.0,
374 cursor: 0,
375 }
376 }
377
378 #[must_use]
380 #[inline]
381 pub const fn to_bits(self) -> u16 {
382 self.0
383 }
384
385 #[must_use]
387 #[inline]
388 pub const fn from_bits_retain(bits: u16) -> Self {
389 Self(bits)
390 }
391
392 #[must_use]
394 #[inline]
395 pub const fn contains_unknown_bits(self) -> bool {
396 self.0 & Self::ALL.0 != self.0
397 }
398
399 #[must_use]
401 #[inline]
402 pub const fn from_bits(bits: u16) -> Option<Self> {
403 if bits & Self::ALL.0 == bits {
404 Some(Self(bits))
405 } else {
406 None
407 }
408 }
409
410 #[must_use]
412 #[inline]
413 pub const fn from_bits_truncate(bits: u16) -> Self {
414 Self(bits & Self::ALL.0)
415 }
416
417 #[must_use]
419 #[inline]
420 pub const fn from_rank(rank: Rank) -> Self {
421 Self(1 << rank.get())
422 }
423}
424
425impl IntoIterator for Holding {
426 type Item = Rank;
427 type IntoIter = HoldingIter;
428
429 fn into_iter(self) -> HoldingIter {
430 self.iter()
431 }
432}
433
434impl ops::BitAnd for Holding {
435 type Output = Self;
436
437 #[inline]
438 fn bitand(self, rhs: Self) -> Self {
439 Self(self.0 & rhs.0)
440 }
441}
442
443impl ops::BitOr for Holding {
444 type Output = Self;
445
446 #[inline]
447 fn bitor(self, rhs: Self) -> Self {
448 Self(self.0 | rhs.0)
449 }
450}
451
452impl ops::BitXor for Holding {
453 type Output = Self;
454
455 #[inline]
456 fn bitxor(self, rhs: Self) -> Self {
457 Self(self.0 ^ rhs.0)
458 }
459}
460
461impl ops::Not for Holding {
462 type Output = Self;
463
464 #[inline]
465 fn not(self) -> Self {
466 Self::from_bits_truncate(!self.0)
467 }
468}
469
470impl ops::Sub for Holding {
471 type Output = Self;
472
473 #[inline]
474 fn sub(self, rhs: Self) -> Self {
475 Self(self.0 & !rhs.0)
476 }
477}
478
479impl ops::BitAndAssign for Holding {
480 #[inline]
481 fn bitand_assign(&mut self, rhs: Self) {
482 *self = *self & rhs;
483 }
484}
485
486impl ops::BitOrAssign for Holding {
487 #[inline]
488 fn bitor_assign(&mut self, rhs: Self) {
489 *self = *self | rhs;
490 }
491}
492
493impl ops::BitXorAssign for Holding {
494 #[inline]
495 fn bitxor_assign(&mut self, rhs: Self) {
496 *self = *self ^ rhs;
497 }
498}
499
500impl ops::SubAssign for Holding {
501 #[inline]
502 fn sub_assign(&mut self, rhs: Self) {
503 *self = *self - rhs;
504 }
505}
506
507impl fmt::Display for Holding {
513 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
514 for rank in 1u8..14 {
515 if self.0 & 1 << rank != 0 {
516 f.write_char(b"A23456789TJQK"[rank as usize - 1] as char)?;
517 }
518 }
519 Ok(())
520 }
521}
522
523#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
525#[non_exhaustive]
526pub enum ParseHoldingError {
527 #[error("Ranks are not all valid or in ascending order")]
529 InvalidRanks,
530
531 #[error("The same rank appears more than once")]
533 RepeatedRank,
534
535 #[error("A suit contains more than 13 cards")]
537 TooManyCards,
538}
539
540#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
542#[non_exhaustive]
543pub enum ParseHandError {
544 #[error(transparent)]
546 Holding(#[from] ParseHoldingError),
547
548 #[error("The hand does not contain 4 suits")]
550 NotFourSuits,
551}
552
553impl FromStr for Holding {
554 type Err = ParseHoldingError;
555
556 fn from_str(s: &str) -> Result<Self, Self::Err> {
557 if s.len() > 14 {
559 return Err(ParseHoldingError::TooManyCards);
560 }
561
562 let bytes = s.as_bytes();
563 let mut i = 0;
564 let mut prev_rank: u8 = 0;
565 let mut holding = Self::EMPTY;
566
567 while i < bytes.len() {
568 let c = bytes[i].to_ascii_uppercase();
569 let rank: u8 = match c {
570 b'A' => 1,
571 b'K' => 13,
572 b'Q' => 12,
573 b'J' => 11,
574 b'T' => 10,
575 b'1' => {
576 if bytes.get(i + 1) != Some(&b'0') {
577 return Err(ParseHoldingError::InvalidRanks);
578 }
579 i += 1;
580 10
581 }
582 b'2'..=b'9' => c - b'0',
583 _ => return Err(ParseHoldingError::InvalidRanks),
584 };
585
586 if rank == prev_rank {
587 return Err(ParseHoldingError::RepeatedRank);
588 }
589 if rank < prev_rank {
590 return Err(ParseHoldingError::InvalidRanks);
591 }
592 prev_rank = rank;
593
594 holding.insert(Rank(unsafe { NonZero::new_unchecked(rank) }));
596 i += 1;
597 }
598
599 Ok(holding)
600 }
601}
602
603#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
608#[cfg_attr(
609 feature = "serde",
610 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
611)]
612pub struct Hand([Holding; 4]);
613
614const _: () = assert!(size_of::<Hand>() == 8);
615const _: () = assert!(Hand::ALL.to_bits() == 0x3FFE_3FFE_3FFE_3FFE);
616
617#[derive(Debug, Clone, PartialEq, Eq)]
620pub struct HandIter {
621 suits: [HoldingIter; 4],
622 fwd: u8,
623 bwd: u8,
624}
625
626impl Iterator for HandIter {
627 type Item = Card;
628
629 fn next(&mut self) -> Option<Self::Item> {
630 loop {
631 if self.fwd > 3 {
632 return None;
633 }
634 let suit = Suit::ASC[self.fwd as usize];
635 if let Some(rank) = self.suits[self.fwd as usize].next() {
636 return Some(Card { suit, rank });
637 }
638 if self.fwd == self.bwd {
639 self.fwd = 4;
640 return None;
641 }
642 self.fwd += 1;
643 }
644 }
645
646 fn size_hint(&self) -> (usize, Option<usize>) {
647 let count = self.len();
648 (count, Some(count))
649 }
650
651 fn count(self) -> usize {
652 self.len()
653 }
654}
655
656impl DoubleEndedIterator for HandIter {
657 fn next_back(&mut self) -> Option<Self::Item> {
658 loop {
659 if self.fwd > 3 {
660 return None;
661 }
662 let suit = Suit::ASC[self.bwd as usize];
663 if let Some(rank) = self.suits[self.bwd as usize].next_back() {
664 return Some(Card { suit, rank });
665 }
666 if self.fwd == self.bwd {
667 self.fwd = 4;
668 return None;
669 }
670 self.bwd -= 1;
671 }
672 }
673}
674
675impl ExactSizeIterator for HandIter {
676 fn len(&self) -> usize {
677 if self.fwd > 3 {
678 return 0;
679 }
680 (self.fwd as usize..=self.bwd as usize)
681 .map(|i| self.suits[i].len())
682 .sum()
683 }
684}
685
686impl FusedIterator for HandIter {}
687
688impl ops::Index<Suit> for Hand {
689 type Output = Holding;
690
691 #[inline]
692 fn index(&self, suit: Suit) -> &Holding {
693 &self.0[suit as usize]
694 }
695}
696
697impl ops::IndexMut<Suit> for Hand {
698 #[inline]
699 fn index_mut(&mut self, suit: Suit) -> &mut Holding {
700 &mut self.0[suit as usize]
701 }
702}
703
704impl Hand {
705 #[must_use]
712 #[inline]
713 pub const fn to_bits(self) -> u64 {
714 self.0[0].to_bits() as u64
715 | (self.0[1].to_bits() as u64) << 16
716 | (self.0[2].to_bits() as u64) << 32
717 | (self.0[3].to_bits() as u64) << 48
718 }
719
720 #[must_use]
723 #[inline]
724 pub const fn from_bits_retain(bits: u64) -> Self {
725 Self([
726 Holding::from_bits_retain(bits as u16),
727 Holding::from_bits_retain((bits >> 16) as u16),
728 Holding::from_bits_retain((bits >> 32) as u16),
729 Holding::from_bits_retain((bits >> 48) as u16),
730 ])
731 }
732
733 #[must_use]
735 #[inline]
736 pub const fn contains_unknown_bits(self) -> bool {
737 self.to_bits() & Self::ALL.to_bits() != self.to_bits()
738 }
739
740 #[must_use]
742 #[inline]
743 pub const fn from_bits(bits: u64) -> Option<Self> {
744 if bits & Self::ALL.to_bits() == bits {
745 Some(Self::from_bits_retain(bits))
746 } else {
747 None
748 }
749 }
750
751 #[must_use]
753 #[inline]
754 pub const fn from_bits_truncate(bits: u64) -> Self {
755 Self::from_bits_retain(bits & Self::ALL.to_bits())
756 }
757
758 #[must_use]
760 #[inline]
761 pub const fn new(clubs: Holding, diamonds: Holding, hearts: Holding, spades: Holding) -> Self {
762 Self([clubs, diamonds, hearts, spades])
763 }
764
765 #[must_use]
767 #[inline]
768 pub const fn from_card(card: Card) -> Self {
769 let mut hand = Self::EMPTY;
770 hand.0[card.suit as usize] = Holding::from_rank(card.rank);
771 hand
772 }
773
774 pub const EMPTY: Self = Self([Holding::EMPTY; 4]);
776
777 pub const ALL: Self = Self([Holding::ALL; 4]);
779
780 #[must_use]
782 #[inline]
783 pub const fn len(self) -> usize {
784 self.to_bits().count_ones() as usize
785 }
786
787 #[must_use]
789 #[inline]
790 pub const fn is_empty(self) -> bool {
791 self.to_bits() == 0
792 }
793
794 #[must_use]
796 #[inline]
797 pub fn contains(self, card: Card) -> bool {
798 self[card.suit].contains(card.rank)
799 }
800
801 #[inline]
803 pub fn insert(&mut self, card: Card) -> bool {
804 self[card.suit].insert(card.rank)
805 }
806
807 #[inline]
809 pub fn remove(&mut self, card: Card) -> bool {
810 self[card.suit].remove(card.rank)
811 }
812
813 #[inline]
815 pub fn toggle(&mut self, card: Card) -> bool {
816 self[card.suit].toggle(card.rank)
817 }
818
819 #[inline]
821 pub fn set(&mut self, card: Card, condition: bool) {
822 self[card.suit].set(card.rank, condition);
823 }
824
825 #[inline]
827 #[must_use]
828 pub const fn iter(self) -> HandIter {
829 HandIter {
830 suits: [
831 self.0[0].iter(),
832 self.0[1].iter(),
833 self.0[2].iter(),
834 self.0[3].iter(),
835 ],
836 fwd: 0,
837 bwd: 3,
838 }
839 }
840}
841
842impl IntoIterator for Hand {
843 type Item = Card;
844 type IntoIter = HandIter;
845
846 #[inline]
847 fn into_iter(self) -> HandIter {
848 self.iter()
849 }
850}
851
852impl FromIterator<Card> for Hand {
853 fn from_iter<I: IntoIterator<Item = Card>>(iter: I) -> Self {
854 iter.into_iter().fold(Self::EMPTY, |mut hand, card| {
855 hand.insert(card);
856 hand
857 })
858 }
859}
860
861impl From<Card> for Hand {
862 #[inline]
863 fn from(card: Card) -> Self {
864 Self::from_card(card)
865 }
866}
867
868impl fmt::Display for Hand {
876 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
877 if self.is_empty() {
878 return f.write_char('-');
879 }
880
881 self[Suit::Clubs].fmt(f)?;
882 f.write_char('.')?;
883
884 self[Suit::Diamonds].fmt(f)?;
885 f.write_char('.')?;
886
887 self[Suit::Hearts].fmt(f)?;
888 f.write_char('.')?;
889
890 self[Suit::Spades].fmt(f)
891 }
892}
893
894impl FromStr for Hand {
895 type Err = ParseHandError;
896
897 fn from_str(s: &str) -> Result<Self, Self::Err> {
898 if s.len() > 52 + 4 + 3 {
900 return Err(ParseHoldingError::TooManyCards.into());
901 }
902
903 if s == "-" {
904 return Ok(Self::EMPTY);
905 }
906
907 let holdings: Result<Vec<_>, _> = s.split('.').map(Holding::from_str).collect();
908
909 Ok(Self(
910 holdings?
911 .try_into()
912 .map_err(|_| ParseHandError::NotFourSuits)?,
913 ))
914 }
915}
916
917impl ops::BitAnd for Hand {
918 type Output = Self;
919
920 #[inline]
921 fn bitand(self, rhs: Self) -> Self {
922 Self::from_bits_retain(self.to_bits() & rhs.to_bits())
923 }
924}
925
926impl ops::BitOr for Hand {
927 type Output = Self;
928
929 #[inline]
930 fn bitor(self, rhs: Self) -> Self {
931 Self::from_bits_retain(self.to_bits() | rhs.to_bits())
932 }
933}
934
935impl ops::BitXor for Hand {
936 type Output = Self;
937
938 #[inline]
939 fn bitxor(self, rhs: Self) -> Self {
940 Self::from_bits_retain(self.to_bits() ^ rhs.to_bits())
941 }
942}
943
944impl ops::Not for Hand {
945 type Output = Self;
946
947 #[inline]
948 fn not(self) -> Self {
949 Self::from_bits_truncate(!self.to_bits())
950 }
951}
952
953impl ops::Sub for Hand {
954 type Output = Self;
955
956 #[inline]
957 fn sub(self, rhs: Self) -> Self {
958 Self::from_bits_retain(self.to_bits() & !rhs.to_bits())
959 }
960}
961
962impl ops::BitAndAssign for Hand {
963 #[inline]
964 fn bitand_assign(&mut self, rhs: Self) {
965 *self = *self & rhs;
966 }
967}
968
969impl ops::BitOrAssign for Hand {
970 #[inline]
971 fn bitor_assign(&mut self, rhs: Self) {
972 *self = *self | rhs;
973 }
974}
975
976impl ops::BitXorAssign for Hand {
977 #[inline]
978 fn bitxor_assign(&mut self, rhs: Self) {
979 *self = *self ^ rhs;
980 }
981}
982
983impl ops::SubAssign for Hand {
984 #[inline]
985 fn sub_assign(&mut self, rhs: Self) {
986 *self = *self - rhs;
987 }
988}
989
990#[cfg(test)]
991mod tests {
992 use super::*;
993
994 #[test]
995 fn rank_letters_and_deadwood() {
996 let letters: Vec<char> = (1..=13).map(|r| Rank::new(r).letter()).collect();
997 assert_eq!(letters.iter().collect::<String>(), "A23456789TJQK");
998
999 let values: Vec<u8> = (1..=13).map(|r| Rank::new(r).deadwood()).collect();
1000 assert_eq!(values, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10]);
1001
1002 assert!(Rank::A < Rank::new(2));
1003 assert!(Rank::T < Rank::J);
1004 assert_eq!(Rank::try_new(0), Err(InvalidRank(0)));
1005 assert_eq!(Rank::try_new(14), Err(InvalidRank(14)));
1006 }
1007
1008 #[test]
1009 fn holding_bit_ops() {
1010 let mut holding = Holding::EMPTY;
1011 assert!(holding.insert(Rank::A));
1012 assert!(!holding.insert(Rank::A));
1013 assert!(holding.insert(Rank::K));
1014 assert_eq!(holding.len(), 2);
1015 assert!(holding.contains(Rank::A));
1016 assert!(!holding.contains(Rank::Q));
1017
1018 assert!(holding.remove(Rank::A));
1019 assert!(!holding.remove(Rank::A));
1020 assert!(holding.toggle(Rank::Q));
1021 assert!(!holding.toggle(Rank::Q));
1022
1023 holding.set(Rank::T, true);
1024 holding.set(Rank::K, false);
1025 assert_eq!(holding.iter().collect::<Vec<_>>(), [Rank::T]);
1026
1027 assert_eq!(!Holding::EMPTY, Holding::ALL);
1028 assert_eq!(Holding::ALL.len(), 13);
1029 assert_eq!(Holding::ALL - Holding::ALL, Holding::EMPTY);
1030 }
1031
1032 #[test]
1033 fn holding_iteration_is_ascending() {
1034 let holding: Holding = "A23TK".parse().unwrap();
1035 let ranks: Vec<u8> = holding.iter().map(Rank::get).collect();
1036 assert_eq!(ranks, [1, 2, 3, 10, 13]);
1037
1038 let reversed: Vec<u8> = holding.iter().rev().map(Rank::get).collect();
1039 assert_eq!(reversed, [13, 10, 3, 2, 1]);
1040
1041 let mut iter = holding.iter();
1042 assert_eq!(iter.len(), 5);
1043 assert_eq!(iter.next().map(Rank::get), Some(1));
1044 assert_eq!(iter.next_back().map(Rank::get), Some(13));
1045 assert_eq!(iter.len(), 3);
1046 assert_eq!(iter.next().map(Rank::get), Some(2));
1047 assert_eq!(iter.next_back().map(Rank::get), Some(10));
1048 assert_eq!(iter.next().map(Rank::get), Some(3));
1049 assert_eq!(iter.next(), None);
1050 assert_eq!(iter.next_back(), None);
1051 }
1052
1053 #[test]
1054 fn invalid_bits_are_not_iterated() {
1055 let holding = Holding::from_bits_retain(0x8001);
1056 assert!(holding.contains_unknown_bits());
1057 assert_eq!(holding.iter().count(), 0);
1058 assert_eq!(Holding::from_bits(0x8001), None);
1059 assert_eq!(Holding::from_bits_truncate(0x8001), Holding::EMPTY);
1060 }
1061
1062 #[test]
1063 fn hand_layout() {
1064 let mut hand = Hand::EMPTY;
1065 assert!(hand.insert(Card {
1066 suit: Suit::Clubs,
1067 rank: Rank::A,
1068 }));
1069 assert_eq!(hand.to_bits(), 2);
1070
1071 assert!(hand.insert(Card {
1072 suit: Suit::Spades,
1073 rank: Rank::K,
1074 }));
1075 assert_eq!(hand.to_bits(), 2 | 1 << 61);
1076
1077 assert_eq!(Hand::from_bits_retain(hand.to_bits()), hand);
1078 assert_eq!(hand.len(), 2);
1079 }
1080
1081 #[test]
1082 fn hand_iteration_is_ascending() {
1083 let hand: Hand = "A23.456.789.T".parse().unwrap();
1084 assert_eq!(hand.len(), 10);
1085
1086 let cards: Vec<Card> = hand.iter().collect();
1087 assert_eq!(cards.first().map(ToString::to_string), Some("A♣".into()));
1088 assert_eq!(cards.last().map(ToString::to_string), Some("T♠".into()));
1089 assert!(cards.windows(2).all(|w| {
1090 let (a, b) = (w[0], w[1]);
1091 a.suit < b.suit || (a.suit == b.suit && a.rank < b.rank)
1092 }));
1093
1094 assert_eq!(hand.iter().rev().count(), 10);
1095 assert_eq!(Hand::from_iter(cards), hand);
1096 }
1097}