openpql_prelude/card/
hand_n.rs

1use super::{Card, Card64, Deref, HandIter, Hash, Index, Into, fmt};
2
3/// Fixed-size hand representation.
4///
5/// Represents exactly N cards. Cards are stored sorted and deduplicated.
6#[derive(
7    Copy,
8    Clone,
9    derive_more::Debug,
10    PartialEq,
11    Eq,
12    Hash,
13    PartialOrd,
14    Ord,
15    Into,
16    Deref,
17    Index,
18)]
19#[debug("Hand<{}>({})", N, self)]
20pub struct HandN<const N: usize>(pub(crate) [Card; N]);
21
22impl<const N: usize> HandN<N> {
23    #[cfg_attr(coverage_nightly, coverage(off))]
24    pub(crate) fn new(array: [Card; N]) -> Self {
25        debug_assert!(N > 1, "HandN should have at least two cards.");
26        debug_assert!(
27            array.is_sorted(),
28            "Hand initialized from unsorted array {array:?}"
29        );
30        Self(array)
31    }
32
33    /// Creates a sorted hand from a slice.
34    #[cfg_attr(coverage_nightly, coverage(off))]
35    pub fn from_slice(cs: &[Card]) -> Self {
36        debug_assert!(
37            cs.len() >= N,
38            "from_slice: not enough cards for Hand<{}> slice has {} elements",
39            N,
40            cs.len()
41        );
42
43        let mut cards = [Card::default(); N];
44        cards.copy_from_slice(&cs[..N]);
45        cards.sort_unstable();
46
47        Self(cards)
48    }
49
50    /// Returns an iterator over all possible N-card hands (card combination)
51    pub fn iter_all<const SD: bool>() -> HandIter<SD, N> {
52        HandIter::default()
53    }
54}
55
56impl<const N: usize> fmt::Display for HandN<N> {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        self.0.iter().try_for_each(|card| write!(f, "{card}"))
59    }
60}
61
62impl<const N: usize> From<HandN<N>> for Card64 {
63    fn from(hand: HandN<N>) -> Self {
64        hand.iter().copied().collect()
65    }
66}
67
68#[cfg(test)]
69#[cfg_attr(coverage_nightly, coverage(off))]
70mod tests {
71    use super::*;
72    use crate::*;
73
74    #[test]
75    #[should_panic(expected = "Hand initialized from unsorted array")]
76    #[cfg(debug_assertions)]
77    fn test_new_err() {
78        let unsorted = [
79            Card::from_str("Ad").unwrap(),
80            Card::from_str("2s").unwrap(),
81            Card::from_str("Kc").unwrap(),
82        ];
83
84        let _hand = HandN::new(unsorted);
85    }
86
87    #[test]
88    #[should_panic(expected = "not enough cards for Hand")]
89    #[cfg(debug_assertions)]
90    fn test_from_slice_err() {
91        let cards = cards!("2s Kc");
92
93        let _hand: HandN<3> = HandN::from_slice(&cards);
94    }
95
96    #[test]
97    fn test_from_slice() {
98        let cards = cards!("2s Kc Ad Kh");
99        let [c1, c2, c3, c4] = cards.clone().try_into().unwrap();
100
101        assert_eq!(HandN::<3>::from_slice(&cards).0, [c1, c2, c3]);
102        assert_eq!(HandN::<4>::from_slice(&cards).0, [c1, c4, c2, c3]);
103    }
104
105    #[test]
106    fn test_display() {
107        let hand: HandN<3> = HandN::<3>::from_slice(&cards!("2s Ad Kc"));
108
109        assert_eq!(format!("{hand}"), "2sKcAd");
110        assert_eq!(format!("{hand:?}"), "Hand<3>(2sKcAd)");
111    }
112
113    #[test]
114    fn test_ord() {
115        let unsorted_cards = cards!("Ad Kc 2s");
116        let hand: HandN<3> = HandN::from_slice(&unsorted_cards);
117
118        assert_eq!(hand[0], cards!("2s")[0]);
119        assert_eq!(hand[1], cards!("Kc")[0]);
120        assert_eq!(hand[2], cards!("Ad")[0]);
121    }
122
123    #[quickcheck]
124    fn test_to_card64(cards: CardN<7>) {
125        let cs: Vec<_> = cards.into_iter().collect();
126        let c64 = Card64::from(cs.as_slice());
127        let hand = HandN::<7>::from_slice(&cs);
128
129        assert_eq!(c64, Card64::from(hand));
130    }
131}