openpql_prelude/rng/
card_gen.rs

1use super::{Card, Card64, Vec};
2
3#[derive(Clone, Debug, Default)]
4pub struct CardGen {
5    init: Vec<Card>,
6    used: Vec<Card>,
7    unused: Vec<Card>,
8}
9
10impl CardGen {
11    pub fn new<const SD: bool>(dead_cards: Card64) -> Self {
12        let mut unused = vec![];
13
14        for card in Card64::all::<SD>().iter() {
15            if !dead_cards.contains_card(card) {
16                unused.push(card);
17            }
18        }
19
20        Self {
21            init: unused.clone(),
22            used: vec![],
23            unused,
24        }
25    }
26
27    pub fn deal(&mut self, rng: &mut impl rand::Rng) -> Option<Card> {
28        let n = self.unused.len();
29        if n == 0 {
30            return None;
31        }
32
33        let idx = rng.random_range(0..n);
34        let card = self.unused.remove(idx);
35        self.used.push(card);
36
37        Some(card)
38    }
39
40    pub fn unset(&mut self, c64: Card64) {
41        for c in c64.iter() {
42            self.unset_card(c);
43        }
44    }
45
46    pub fn unset_card(&mut self, card: Card) {
47        debug_assert!(!self.unused.contains(&card));
48
49        self.used.retain(|&c| c != card);
50        self.unused.push(card);
51    }
52
53    pub fn reset(&mut self) {
54        self.unused = self.init.clone();
55    }
56}
57
58#[cfg(test)]
59#[cfg_attr(coverage_nightly, coverage(off))]
60pub mod tests {
61    use super::*;
62
63    fn mk_exhausted_card_gen() -> CardGen {
64        let mut g = CardGen::new::<false>(Card64::default());
65        (g.used, g.unused) = (g.unused, g.used);
66
67        g
68    }
69
70    fn deal_all_to_c64(rng: &mut impl rand::Rng, g: &mut CardGen) -> Card64 {
71        (0..Card::N_CARDS).filter_map(|_| g.deal(rng)).collect()
72    }
73
74    #[test]
75    fn test_deal_all() {
76        const SD: bool = false;
77        let mut rng = rand::rng();
78        let mut g = CardGen::new::<SD>(Card64::default());
79        let mut dealt = Card64::default();
80
81        for _ in 0..Card::N_CARDS {
82            dealt.set(g.deal(&mut rng).unwrap());
83        }
84
85        assert_eq!(dealt, Card64::all::<SD>());
86    }
87
88    #[test]
89    fn test_deal_all_sd() {
90        const SD: bool = true;
91        let mut rng = rand::rng();
92        let mut g = CardGen::new::<SD>(Card64::default());
93        let mut dealt = Card64::default();
94
95        for _ in 0..Card::N_CARDS_SD {
96            dealt.set(g.deal(&mut rng).unwrap());
97        }
98
99        assert_eq!(dealt, Card64::all::<SD>());
100    }
101
102    #[quickcheck]
103    fn test_unset(cards: Card64) {
104        let mut rng = rand::rng();
105
106        let mut g = mk_exhausted_card_gen();
107
108        g.unset(cards);
109
110        assert_eq!(deal_all_to_c64(&mut rng, &mut g), cards);
111
112        let mut g = mk_exhausted_card_gen();
113
114        for card in cards.iter() {
115            g.unset_card(card);
116        }
117
118        assert_eq!(deal_all_to_c64(&mut rng, &mut g), cards);
119    }
120
121    #[quickcheck]
122    fn test_dead_and_reset(available: Card64) {
123        let mut rng = rand::rng();
124
125        let mut g = CardGen::new::<false>(!available);
126
127        assert_eq!(deal_all_to_c64(&mut rng, &mut g), available);
128        assert_eq!(deal_all_to_c64(&mut rng, &mut g), Card64::default());
129
130        g.reset();
131
132        assert_eq!(deal_all_to_c64(&mut rng, &mut g), available);
133    }
134}