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