open_pql/base/
hand_n.rs

1use super::{
2    Card, Card64, CardCount, From, HandIter, Hash, Index, Into, Rank, Vec, fmt,
3    iter, mem, slice,
4};
5
6/// A hand of N cards
7///
8/// Represents a fixed-size collection of N cards that are automatically sorted
9/// upon creation. Cards are maintained in sorted order for consistent comparison
10/// and hashing. This type is used for poker hands, flops, and other fixed-size
11/// card collections.
12///
13/// # Examples
14///
15/// ```
16/// use open_pql::{Card, HandN, Rank::*, Suit::*};
17///
18/// let cards = [Card::new(RA, S), Card::new(RK, H), Card::new(RQ, D)];
19/// let hand: HandN<3> = HandN::from_slice(&cards);
20///
21/// assert_eq!(hand.len(), 3);
22/// assert!(!hand.is_empty());
23/// assert_eq!(hand[0], Card::new(RQ, D)); // Cards are sorted
24/// ```
25#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, From, Into)]
26pub struct HandN<const N: usize>(pub(crate) [Card; N]);
27
28impl<const N: usize> HandN<N> {
29    /// Creates a new hand from a sorted array of cards
30    pub(crate) fn new(array: [Card; N]) -> Self {
31        debug_assert!(
32            array.is_sorted(),
33            "Hand initialized from unsorted array {array:?}"
34        );
35        Self(array)
36    }
37
38    /// Creates a hand from a slice of cards
39    pub fn from_slice(cs: &[Card]) -> Self {
40        debug_assert!(
41            cs.len() >= N,
42            "from_slice: not enough cards for Hand<{}> slice has {} elements",
43            N,
44            cs.len()
45        );
46
47        let mut cards = [Card::default(); N];
48        cards.copy_from_slice(&cs[..N]);
49        cards.sort_unstable();
50
51        Self(cards)
52    }
53
54    /// Returns the underlying card array
55    pub const fn as_slice(&self) -> &[Card] {
56        &self.0
57    }
58
59    pub fn to_vec(&self) -> Vec<Card> {
60        self.0.to_vec()
61    }
62
63    /// Returns an iterator over the cards in the hand
64    pub fn iter(&self) -> impl Iterator<Item = Card> + '_ {
65        self.0.iter().copied()
66    }
67
68    /// Returns the number of cards in the hand
69    pub const fn len(&self) -> usize {
70        N
71    }
72
73    /// Returns true if the hand is empty
74    pub const fn is_empty(&self) -> bool {
75        N == 0
76    }
77
78    pub fn iter_all_short() -> HandIter<true, N> {
79        HandIter::default()
80    }
81
82    pub fn iter_all() -> HandIter<false, N> {
83        HandIter::default()
84    }
85}
86
87#[allow(unused)]
88impl HandN<2> {
89    pub(crate) const fn to_u16(&self) -> u16 {
90        (self.0[0].to_u8() as u16) | (self.0[1].to_u8() as u16) << 8
91    }
92
93    pub(crate) fn from_u16(v: u16) -> Self {
94        let [c0, c1] = v.to_le_bytes();
95        Self::from_slice(&[Card::from_u8(c0), Card::from_u8(c1)])
96    }
97}
98
99impl HandN<3> {
100    pub(crate) fn count_by_rank(self, rank: Rank) -> CardCount {
101        CardCount::from(self[0].rank == rank)
102            + CardCount::from(self[1].rank == rank)
103            + CardCount::from(self[2].rank == rank)
104    }
105
106    pub(crate) fn sorted_ranks(self) -> (Rank, Rank, Rank) {
107        let (mut x, mut y, mut z) = (self[0].rank, self[1].rank, self[2].rank);
108
109        if x < y {
110            mem::swap(&mut x, &mut y);
111        }
112
113        if y < z {
114            mem::swap(&mut y, &mut z);
115        }
116
117        if x < y {
118            mem::swap(&mut x, &mut y);
119        }
120
121        (x, y, z)
122    }
123}
124
125impl<const N: usize> Index<usize> for HandN<N> {
126    type Output = Card;
127
128    fn index(&self, index: usize) -> &Self::Output {
129        &self.0[index]
130    }
131}
132
133impl<'a, const N: usize> IntoIterator for &'a HandN<N> {
134    type Item = Card;
135    type IntoIter = iter::Copied<slice::Iter<'a, Card>>;
136
137    fn into_iter(self) -> Self::IntoIter {
138        self.0.iter().copied()
139    }
140}
141
142impl<const N: usize> fmt::Debug for HandN<N> {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        write!(f, "Hand<")?;
145        for c in &self.0 {
146            write!(f, "{c}")?;
147        }
148        write!(f, ">")
149    }
150}
151
152impl<const N: usize> fmt::Display for HandN<N> {
153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154        for card in &self.0 {
155            write!(f, "{card}")?;
156        }
157
158        Ok(())
159    }
160}
161
162impl<const N: usize> From<HandN<N>> for Card64 {
163    fn from(hand: HandN<N>) -> Self {
164        let mut card64 = Self::default();
165        for card in hand.iter() {
166            card64 |= Self::from(card);
167        }
168        card64
169    }
170}
171
172impl From<(Card, Card, Card)> for HandN<3> {
173    fn from(cs: (Card, Card, Card)) -> Self {
174        Self::from_slice(&[cs.0, cs.1, cs.2])
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181    use crate::*;
182
183    #[test]
184    fn test_hand() {
185        let cards = cards!("2s Kc Ad Kh");
186        let [c1, c2, c3, c4] = cards.clone().try_into().unwrap();
187
188        let hand: HandN<3> = HandN::from_slice(&cards);
189
190        assert_eq!(hand.as_slice(), &[c1, c2, c3]);
191
192        let hand: HandN<4> = HandN::from_slice(&cards);
193
194        assert_eq!(hand.as_slice(), &[c1, c4, c2, c3]);
195    }
196
197    #[test]
198    #[should_panic(expected = "not enough")]
199    fn test_hand_n3_not_enough_cards() {
200        let cards = cards!("2s Kc");
201
202        let _hand: HandN<3> = HandN::from_slice(&cards);
203    }
204
205    #[test]
206    fn test_hand_iter() {
207        let cards = cards!("2s Kc Ad");
208        let hand: HandN<3> = HandN::from_slice(&cards);
209
210        let collected: Vec<Card> = hand.iter().collect();
211        assert_eq!(collected, cards!("2s Kc Ad"));
212    }
213
214    #[test]
215    fn test_hand_into_iter() {
216        let cards = cards!("2s Kc Ad");
217        let hand: HandN<3> = HandN::from_slice(&cards);
218
219        let collected: Vec<Card> = (&hand).into_iter().collect();
220        assert_eq!(collected, cards!("2s Kc Ad"));
221    }
222
223    #[test]
224    fn test_hand_index() {
225        let cards = cards!("2s Kc Ad");
226        let hand: HandN<3> = HandN::from_slice(&cards);
227
228        assert_eq!(hand[0], cards!("2s")[0]);
229        assert_eq!(hand[1], cards!("Kc")[0]);
230        assert_eq!(hand[2], cards!("Ad")[0]);
231    }
232
233    #[test]
234    fn test_hand_len_and_is_empty() {
235        let cards = cards!("2s Kc Ad");
236        let hand3: HandN<3> = HandN::from_slice(&cards);
237        assert_eq!(hand3.len(), 3);
238        assert!(!hand3.is_empty());
239
240        let empty_cards = cards!("2s Kc Ad");
241        let hand0: HandN<0> = HandN::from_slice(&empty_cards);
242        assert_eq!(hand0.len(), 0);
243        assert!(hand0.is_empty());
244    }
245
246    #[test]
247    fn test_hand_debug_and_display() {
248        let cards = cards!("2s Kc Ad");
249        let hand: HandN<3> = HandN::from_slice(&cards);
250
251        // Test Debug implementation
252        let debug_str = format!("{hand:?}");
253        assert_eq!(debug_str, "Hand<2sKcAd>");
254    }
255
256    #[test]
257    fn test_hand_sorting() {
258        // Test that cards are sorted when creating a hand
259        let unsorted_cards = cards!("Ad Kc 2s");
260        let hand: HandN<3> = HandN::from_slice(&unsorted_cards);
261
262        // Cards should be sorted by rank and suit
263        assert_eq!(hand[0], cards!("2s")[0]);
264        assert_eq!(hand[1], cards!("Kc")[0]);
265        assert_eq!(hand[2], cards!("Ad")[0]);
266    }
267
268    #[test]
269    fn test_hand_equality() {
270        let cards1 = cards!("2s Kc Ad");
271        let cards2 = cards!("Ad Kc 2s"); // Same cards but different order
272
273        let hand1: HandN<3> = HandN::from_slice(&cards1);
274        let hand2: HandN<3> = HandN::from_slice(&cards2);
275
276        // Hands should be equal because they contain the same cards
277        // and Hand sorts the cards internally
278        assert_eq!(hand1, hand2);
279
280        // Different hands should not be equal
281        let cards3 = cards!("2s Kc Ah");
282        let hand3: HandN<3> = HandN::from_slice(&cards3);
283        assert_ne!(hand1, hand3);
284    }
285
286    #[quickcheck]
287    fn test_hand2_to_u16((c0, c1): (Card, Card)) {
288        if c0 != c1 {
289            let hand = HandN::<2>::from_slice(&<[Card; 2]>::from((c0, c1)));
290            assert_eq!(hand, HandN::<2>::from_u16(hand.to_u16()));
291        }
292    }
293
294    #[test]
295    #[should_panic(expected = "Hand initialized from unsorted array")]
296    #[cfg(debug_assertions)]
297    fn test_new_unsorted_array_debug_assert() {
298        // Create an unsorted array of cards
299        let unsorted = [
300            Card::from_str("Ad").unwrap(),
301            Card::from_str("2s").unwrap(),
302            Card::from_str("Kc").unwrap(),
303        ];
304
305        // This should panic in debug mode because the array is not sorted
306        let _hand = HandN::new(unsorted);
307    }
308
309    #[test]
310    #[should_panic(expected = "not enough cards for Hand")]
311    #[cfg(debug_assertions)]
312    fn test_from_slice_not_enough_cards_debug_assert() {
313        let cards = cards!("2s Kc"); // Only 2 cards
314
315        // This should panic in debug mode because we need 3 cards for Hand<3>
316        let _hand: HandN<3> = HandN::from_slice(&cards);
317    }
318
319    #[test]
320    fn test_from_slice_sorts_cards() {
321        let unsorted_cards = cards!("Ad Kc 2s");
322        let hand: HandN<3> = HandN::from_slice(&unsorted_cards);
323
324        // Verify that the cards are sorted in the resulting hand
325        let expected_order = cards!("2s Kc Ad");
326        for (i, card) in expected_order.iter().enumerate() {
327            assert_eq!(hand[i], *card);
328        }
329    }
330
331    #[quickcheck]
332    fn test_hand_to_card64(cards: CardN<7>) {
333        let cs: Vec<_> = cards.into_iter().collect();
334        let c64 = Card64::from(cs.as_slice());
335        let hand = HandN::<7>::from_slice(&cs);
336
337        assert_eq!(c64, Card64::from(hand));
338    }
339
340    #[test]
341    fn test_hand_to_vec() {
342        let cs = cards!("AsKhQd");
343        assert_eq!(HandN::<3>::from_slice(&cs).to_vec(), cards!("QdKhAs"));
344    }
345
346    #[test]
347    fn test_hand_display() {
348        let hand = HandN::<2>::from_slice(&cards!("AsKh"));
349        assert_eq!(format!("{hand}"), "KhAs");
350    }
351}