]> git.scottworley.com Git - pluta-lesnura/blob - src/lib.rs
a5148a0ac6ca147d22e4c699c0a5e9d4230d3caf
[pluta-lesnura] / src / lib.rs
1 use rand::Rng;
2
3 pub const NUM_RANKS: u8 = 13;
4
5 #[derive(Clone, Copy, Eq, PartialEq)]
6 pub struct Rank(u8);
7
8 #[derive(Clone, Copy)]
9 pub struct PathLength(Rank);
10
11 #[derive(Clone, Copy, Default)]
12 pub struct PathLengthInfo(u16);
13 impl PathLengthInfo {
14 #[must_use]
15 pub fn is_showing(&self, i: Rank) -> bool {
16 (self.0 >> i.0) & 1 == 1
17 }
18 fn reveal(&mut self, i: Rank) {
19 self.0 |= 1 << i.0;
20 }
21 pub fn reveal_random(&mut self, true_length: PathLength) -> Option<Rank> {
22 let showing = u8::try_from(self.0.count_ones()).expect("There aren't that many bits");
23 let not_showing = NUM_RANKS - showing;
24 if not_showing <= 1 {
25 return None;
26 }
27
28 let mut show = rand::thread_rng().gen_range(0..not_showing - 1);
29 for i in 0..NUM_RANKS {
30 let r = Rank(i);
31 if !self.is_showing(r) && r != true_length.0 {
32 if show == 0 {
33 self.reveal(r);
34 return Some(r);
35 }
36 show -= 1;
37 }
38 }
39 unreachable!()
40 }
41 }
42
43 #[cfg(test)]
44 mod tests {
45 use super::*;
46
47 #[test]
48 fn path_length_info_random_reveal() {
49 let length = PathLength(Rank(7));
50 let mut pli = PathLengthInfo::default();
51 for _ in 0..12 {
52 let old_pli = PathLengthInfo::clone(&pli);
53 match pli.reveal_random(length) {
54 None => panic!("Nothing revealed?"),
55 Some(r) => {
56 assert!(!old_pli.is_showing(r));
57 assert!(pli.is_showing(r));
58 }
59 }
60 assert_eq!(pli.0.count_ones(), 1 + old_pli.0.count_ones());
61 }
62 assert!(pli.reveal_random(length).is_none());
63 }
64 }