]> git.scottworley.com Git - pluta-lesnura/blame - src/lib.rs
Add jokers
[pluta-lesnura] / src / lib.rs
CommitLineData
57f490a0
SW
1use rand::Rng;
2
3pub const NUM_RANKS: u8 = 13;
09822a98 4pub const NUM_SUITS: u8 = 4;
10e7da7b
SW
5pub const NUM_JOKERS: u8 = 2;
6pub const NUM_CARDS: u8 = NUM_RANKS * NUM_SUITS + NUM_JOKERS;
57f490a0
SW
7
8#[derive(Clone, Copy, Eq, PartialEq)]
9pub struct Rank(u8);
10
09822a98
SW
11#[derive(Clone, Copy, Eq, PartialEq)]
12pub struct Suit(u8);
13
14#[derive(Clone, Copy, Eq, PartialEq)]
15pub struct Card(u8);
16impl Card {
17 #[must_use]
10e7da7b
SW
18 pub fn is_joker(&self) -> bool {
19 self.0 >= NUM_RANKS * NUM_SUITS
09822a98
SW
20 }
21 #[must_use]
10e7da7b
SW
22 pub fn rank(&self) -> Option<Rank> {
23 (!self.is_joker()).then_some(Rank(self.0 >> 2))
09822a98 24 }
10e7da7b
SW
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)]
32pub enum WithOrWithoutJokers {
33 WithJokers,
34 WithoutJokers,
09822a98
SW
35}
36
37#[must_use]
10e7da7b
SW
38pub 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()
09822a98
SW
44}
45
57f490a0
SW
46#[derive(Clone, Copy)]
47pub struct PathLength(Rank);
48
49#[derive(Clone, Copy, Default)]
50pub struct PathLengthInfo(u16);
51impl 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 }
754e9730
SW
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
57f490a0
SW
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());
754e9730 101 }
09822a98
SW
102
103 #[test]
104 fn test_deck() {
10e7da7b
SW
105 use WithOrWithoutJokers::*;
106 let _d = deck(WithoutJokers);
107 let _dj = deck(WithJokers);
09822a98 108 }
754e9730 109}