chiark / gitweb /
changelog: document further make-release changes
[otter.git] / support / fake-rng.rs
1 // Copyright 2020-2021 Ian Jackson and contributors to Otter
2 // SPDX-License-Identifier: AGPL-3.0-or-later
3 // There is NO WARRANTY.
4
5 use crate::prelude::*;
6
7 use parking_lot::Mutex;
8 use rand::distributions::uniform::SampleUniform;
9
10 #[derive(Deserialize,Debug,Clone,Default)]
11 #[serde(transparent)]
12 pub struct FakeRngSpec(Option<Vec<String>>);
13
14 #[derive(Serialize,Deserialize,Error,Debug,Clone)]
15 #[error("RNG is real")]
16 pub struct RngIsReal;
17
18 impl FakeRngSpec {
19   pub fn make_game_rng(self) -> RngWrap { RngWrap( match self.0 {
20     None => None,
21     Some(ents) => Some(Mutex::new(FakeRng {
22       i: 0,
23       ents,
24     })) }
25   )}
26 }
27
28 #[derive(Debug)]
29 pub struct RngWrap (
30   Option<Mutex<FakeRng>>
31 );
32
33 #[derive(Debug)]
34 struct FakeRng {
35   i: usize,
36   ents: Vec<String>,
37 }
38
39 impl RngWrap {
40   pub fn is_fake(&self) -> bool { self.0.is_some() }
41
42   #[throws(RngIsReal)]
43   pub fn set_fake(&self, v: Vec<String>, _: AuthorisationSuperuser) {
44     let mut fake = self.0.as_ref().ok_or(RngIsReal)?.lock();
45     fake.i = 0;
46     fake.ents = v;
47   }
48
49   #[throws(as Option)]
50   fn next_fake(&self) -> String {
51     let mut fake = self.0.as_ref()?.lock();
52     let e = fake.ents.get(fake.i)?.clone();
53     fake.i += 1;
54     fake.i %= fake.ents.len();
55     e
56   }
57
58   pub fn shuffle<T:Copy>(&self, slice: &mut [T]) { match self.next_fake() {
59     None => {
60       let mut rng = thread_rng();
61       slice.shuffle(&mut rng);
62     },
63     Some(s) => {
64       let l = slice.len();
65       let n: usize = s.parse().unwrap_or(0);
66       let front = slice[0..n].to_owned();
67       slice.copy_within(n.., 0);
68       slice[l-n..].copy_from_slice(&front);
69     },
70   } }
71
72   pub fn range<T>(&self, range: std::ops::Range<T>) -> T
73   where T: SampleUniform + FromStr + Ord + Default
74   {
75     match self.next_fake() {
76       None => {
77         let mut rng = thread_rng();
78         rng.gen_range(range)
79       },
80       Some(s) => (||{
81         let n: T = s.parse().ok()?;
82         range.contains(&n).then(|| n)
83       })().unwrap_or_default(),
84     }
85   }
86