open_pql/base/
hand_iter.rs1use super::{Card, HandN};
2
3#[derive(Debug, Clone)]
4pub struct HandIter<const SD: bool, const N: usize> {
5 indices: [u8; N],
6 done: bool,
7}
8
9impl<const SD: bool, const N: usize> HandIter<SD, N> {
10 const fn all_cards() -> &'static [Card] {
11 if SD {
12 Card::ARR_ALL_SHORT.as_slice()
13 } else {
14 Card::ARR_ALL.as_slice()
15 }
16 }
17}
18
19#[allow(clippy::cast_possible_truncation)]
20impl<const SD: bool, const N: usize> Default for HandIter<SD, N> {
21 fn default() -> Self {
22 let mut indices = [0; N];
23 for i in 0..N as u8 {
24 indices[i as usize] = i;
25 }
26 Self {
27 indices,
28 done: N == 0,
29 }
30 }
31}
32
33impl<const SD: bool, const N: usize> Iterator for HandIter<SD, N> {
34 type Item = HandN<N>;
35
36 fn next(&mut self) -> Option<Self::Item> {
37 if self.done {
38 return None;
39 }
40
41 let all = Self::all_cards();
42 let max_i = all.len();
43
44 let mut cards = [Card::default(); N];
45 for i in 0..N {
46 cards[i] = all[self.indices[i] as usize];
47 }
48
49 let mut pos = N - 1;
50 self.indices[pos] += 1;
51
52 while self.indices[pos] as usize >= max_i - (N - 1 - pos) {
53 if pos == 0 {
54 self.done = true;
55 return Some(HandN::new(cards));
56 }
57
58 pos -= 1;
59 self.indices[pos] += 1;
60 }
61
62 for i in (pos + 1)..N {
63 self.indices[i] = self.indices[i - 1] + 1;
64 }
65
66 Some(HandN::new(cards))
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_hand_iter_empty() {
76 let mut iter = HandIter::<false, 0>::default();
77 assert!(
78 iter.next().is_none(),
79 "Empty hand iterator should return None"
80 );
81 }
82
83 #[test]
84 fn test_hand_iter_single_card() {
85 let mut iter = HandN::<1>::iter_all();
86
87 for i in 0..Card::ARR_ALL.len() {
88 assert_eq!(iter.next(), Some([Card::ARR_ALL[i]].into()));
89 }
90 assert_eq!(iter.next(), None);
91 }
92
93 #[test]
94 fn test_hand_iter_shortdeck() {
95 let iter = HandN::<2>::iter_all_short();
96
97 let expected = 36 * 35 / 2;
98 assert_eq!(iter.count(), expected);
99 }
100}