open_pql/base/
hand_iter.rs

1use super::{Card, HandN};
2
3/// Iterator for generating all possible combinations of N cards
4///
5/// Generates all unique combinations of N cards from either the full deck (52 cards)
6/// or the short deck (36 cards, 6-A only). The generic parameter SD determines
7/// which deck to use: true for short deck, false for full deck.
8///
9/// # Examples
10///
11/// ```
12/// use open_pql::{HandIter, HandN};
13///
14/// let mut iter = HandN::<2>::iter_all();
15/// let first_hand = iter.next().unwrap();
16/// assert_eq!(first_hand.len(), 2);
17/// ```
18#[derive(Debug, Clone)]
19pub struct HandIter<const SD: bool, const N: usize> {
20    indices: [u8; N],
21    done: bool,
22}
23
24impl<const SD: bool, const N: usize> HandIter<SD, N> {
25    /// Returns the array of all cards for the current deck type
26    ///
27    /// Returns either all 52 cards or the 36-card short deck (6-A only)
28    /// depending on the SD generic parameter.
29    const fn all_cards() -> &'static [Card] {
30        if SD {
31            Card::ARR_ALL_SHORT.as_slice()
32        } else {
33            Card::ARR_ALL.as_slice()
34        }
35    }
36}
37
38#[allow(clippy::cast_possible_truncation)]
39impl<const SD: bool, const N: usize> Default for HandIter<SD, N> {
40    /// Creates a new `HandIter` starting with the first combination
41    fn default() -> Self {
42        let mut indices = [0; N];
43        for i in 0..N as u8 {
44            indices[i as usize] = i;
45        }
46        Self {
47            indices,
48            done: N == 0,
49        }
50    }
51}
52
53impl<const SD: bool, const N: usize> Iterator for HandIter<SD, N> {
54    type Item = HandN<N>;
55
56    /// Generates the next unique combination of N cards
57    ///
58    /// Returns `Some(HandN<N>)` containing the next combination, or `None`
59    /// when all combinations have been generated. Uses a combination algorithm
60    /// to ensure each hand is unique and generated in lexicographic order.
61    fn next(&mut self) -> Option<Self::Item> {
62        if self.done {
63            return None;
64        }
65
66        let all = Self::all_cards();
67        let max_i = all.len();
68
69        let mut cards = [Card::default(); N];
70        for i in 0..N {
71            cards[i] = all[self.indices[i] as usize];
72        }
73
74        let mut pos = N - 1;
75        self.indices[pos] += 1;
76
77        while self.indices[pos] as usize >= max_i - (N - 1 - pos) {
78            if pos == 0 {
79                self.done = true;
80                return Some(HandN::new(cards));
81            }
82
83            pos -= 1;
84            self.indices[pos] += 1;
85        }
86
87        for i in (pos + 1)..N {
88            self.indices[i] = self.indices[i - 1] + 1;
89        }
90
91        Some(HandN::new(cards))
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use crate::*;
99
100    // #[coverage(off)]
101    #[test]
102    fn test_hand_iter_empty() {
103        let mut iter = HandIter::<false, 0>::default();
104        assert!(
105            iter.next().is_none(),
106            "Empty hand iterator should return None"
107        );
108    }
109
110    #[test]
111    fn test_hand_iter_single_card() {
112        let mut iter = HandN::<1>::iter_all();
113
114        for i in 0..Card::ARR_ALL.len() {
115            assert_eq!(iter.next(), Some([Card::ARR_ALL[i]].into()));
116        }
117        assert_eq!(iter.next(), None);
118    }
119
120    #[test]
121    fn test_hand_iter_shortdeck() {
122        let iter = HandN::<2>::iter_all_short();
123
124        let expected = 36 * 35 / 2;
125        assert_eq!(iter.count(), expected);
126    }
127}