]> git.scottworley.com Git - pluta-lesnura/blob - src/lib.rs
NUM_ usize
[pluta-lesnura] / src / lib.rs
1 use rand::Rng;
2
3 pub const NUM_RANKS: usize = 13;
4 pub const NUM_SUITS: usize = 4;
5 pub const NUM_JOKERS: usize = 2;
6 pub const NUM_CARDS: usize = NUM_RANKS * NUM_SUITS + NUM_JOKERS;
7
8 #[derive(Clone, Copy, Eq, PartialEq)]
9 pub struct Rank(u8);
10 impl Rank {
11 #[must_use]
12 pub fn value(&self) -> u8 {
13 self.0 + 1
14 }
15 }
16
17 #[derive(Clone, Copy, Eq, PartialEq)]
18 pub struct Suit(u8);
19
20 #[derive(Clone, Copy, Eq, PartialEq)]
21 pub struct Card(u8);
22 impl Card {
23 #[must_use]
24 pub fn is_joker(&self) -> bool {
25 usize::from(self.0) >= NUM_RANKS * NUM_SUITS
26 }
27 #[must_use]
28 pub fn rank(&self) -> Option<Rank> {
29 (!self.is_joker()).then_some(Rank(self.0 >> 2))
30 }
31 #[must_use]
32 pub fn suit(&self) -> Option<Suit> {
33 (!self.is_joker()).then_some(Suit(self.0 & 3))
34 }
35 }
36
37 #[derive(Clone, Copy)]
38 pub enum WithOrWithoutJokers {
39 WithJokers,
40 WithoutJokers,
41 }
42
43 #[must_use]
44 pub fn deck(j: WithOrWithoutJokers) -> Vec<Card> {
45 let limit = u8::try_from(match j {
46 WithOrWithoutJokers::WithJokers => NUM_CARDS,
47 WithOrWithoutJokers::WithoutJokers => NUM_SUITS * NUM_RANKS,
48 })
49 .expect("Too many cards?");
50 (0..limit).map(Card).collect()
51 }
52
53 #[derive(Clone, Copy)]
54 pub struct PathLength(Rank);
55
56 #[derive(Clone, Copy, Default)]
57 pub struct PathLengthInfo(u16);
58 impl PathLengthInfo {
59 #[must_use]
60 pub fn is_showing(&self, i: Rank) -> bool {
61 (self.0 >> i.0) & 1 == 1
62 }
63 fn reveal(&mut self, i: Rank) {
64 self.0 |= 1 << i.0;
65 }
66 pub fn reveal_random(&mut self, true_length: PathLength) -> Option<Rank> {
67 let showing = usize::try_from(self.0.count_ones()).expect("There aren't that many bits");
68 let not_showing = NUM_RANKS - showing;
69 if not_showing <= 1 {
70 return None;
71 }
72
73 let mut show = rand::thread_rng().gen_range(0..not_showing - 1);
74 for i in 0..NUM_RANKS {
75 let r = Rank(u8::try_from(i).expect("Too many cards?"));
76 if !self.is_showing(r) && r != true_length.0 {
77 if show == 0 {
78 self.reveal(r);
79 return Some(r);
80 }
81 show -= 1;
82 }
83 }
84 unreachable!()
85 }
86 }
87
88 #[cfg(test)]
89 mod tests {
90 use super::*;
91
92 #[test]
93 fn path_length_info_random_reveal() {
94 let length = PathLength(Rank(7));
95 let mut pli = PathLengthInfo::default();
96 for _ in 0..12 {
97 let old_pli = PathLengthInfo::clone(&pli);
98 match pli.reveal_random(length) {
99 None => panic!("Nothing revealed?"),
100 Some(r) => {
101 assert!(!old_pli.is_showing(r));
102 assert!(pli.is_showing(r));
103 }
104 }
105 assert_eq!(pli.0.count_ones(), 1 + old_pli.0.count_ones());
106 }
107 assert!(pli.reveal_random(length).is_none());
108 }
109
110 #[test]
111 fn test_deck() {
112 use WithOrWithoutJokers::*;
113 let d = deck(WithoutJokers);
114 let rank_sum: u32 = d
115 .iter()
116 .map(Card::rank)
117 .flatten()
118 .map(|r| u32::from(r.value()))
119 .sum();
120 assert_eq!(rank_sum, 364);
121 let _dj = deck(WithJokers);
122 }
123 }