hidden/
dispenser.rs

1//! An emitter of choices that shuffles its order internally afterwards.
2//!
3//! The recommended way of using this module is with it's [`Dispenser`](Dispenser) struct:
4//!
5//! ```
6//! use hidden::dispenser::Dispenser;
7//!
8//! let elements = vec!['a', 'b', 'c', 'd', 'e', 'f'];
9//! let mut dispenser = Dispenser::new(elements.len());
10//!
11//! let za_hando = dispenser.make_hand(&elements).expect("created with the same elements slice");
12//! let star_finger = dispenser.make_hand(&elements).expect("created with the same elements slice");
13//!
14//! let hando_choice = za_hando.choose(1).unwrap();
15//! let star_choice = star_finger.choose(1).unwrap();
16//!
17//! // At this point, it's possible that hando_choice and star_choice are or aren't the same,
18//! // by design of random shuffling.
19//!
20//! // However, choosing from the same index, per hand, *is* guaranteed to be the same.
21//! assert_eq!(hando_choice, za_hando.choose(1).unwrap());
22//! assert_eq!(star_choice, star_finger.choose(1).unwrap());
23//! ```
24//!
25//! Upon every call to [`make_hand`](Dispenser::make_hand), and upon [creation](Dispenser::new),
26//! the dispenser shuffles it's internal state, so that it becomes an internal state it may
27//! "dispense", and then change, which stays that way until the next "dispensing".
28use rand::prelude::ThreadRng;
29use rand::seq::SliceRandom;
30use rand::thread_rng;
31
32/// A struct that holds a hidden variable, dispenses [`Hand`s](Hand) with a lock on a
33/// state, and shuffles afterward.
34#[derive(Debug)]
35pub struct Dispenser {
36    seq: Vec<usize>,
37    rng: ThreadRng,
38}
39
40impl Dispenser {
41    /// Creates a new [`Dispenser`](Dispenser), initializing it with choices for a slice of a given
42    /// `len`.
43    pub fn new(len: usize) -> Self {
44        let mut disp = Self {
45            seq: (0..len).collect(),
46            rng: thread_rng(),
47        };
48        disp.shuffle();
49        disp
50    }
51
52    /// Returns the effective `len` argument given to [`new`](Dispenser::new) for this object.
53    pub fn len(&self) -> usize {
54        self.seq.len()
55    }
56
57    /// Creates a [`Hand`](Hand) from `deck` and an internal variable, this shuffles the variable afterwards.
58    ///
59    /// This makes first sure that all possible choices are possible for `deck`, it does this by
60    /// checking the length of `deck`, and effectively comparing it to the `len` given when the
61    /// [`Dispenser`](Dispenser) [was created](Dispenser::new).
62    ///
63    /// This function returns [`None`](None) when it's possible that elements in `deck` cannot be chosen, or
64    /// there are more possible choices than that `deck` has. (`deck.len() != len`)
65    ///
66    /// This function returns [`Some`](Some) when checks pass, and all choices has a corresponding element
67    /// in `deck`.
68    pub fn make_hand<'h, T>(&mut self, deck: &'h [T]) -> Option<Hand<'h, T>> {
69        // Quickly checking equal sizes.
70        // self.seq is (0..len), which means that getting the original len is self.seq.len()
71        //
72        // This check is needed to check hand-making, to make sure that the range of choices and
73        // the slice which is chosen
74        if deck.len() != self.len() {
75            None
76        } else {
77            Some(self.make_hand_unchecked(deck))
78        }
79    }
80
81    /// Creates a [`Hand`](Hand), similar to [`make_hand`](Dispenser::make_hand), but without the
82    /// check to see if all choices can land, and all elements are choosable.
83    pub fn make_hand_unchecked<'h, T>(&mut self, deck: &'h [T]) -> Hand<'h, T> {
84        let b: Box<[usize]> = self.seq.clone().into_boxed_slice();
85        self.shuffle();
86        Hand::new(b, deck)
87    }
88
89    fn shuffle(&mut self) {
90        self.seq.shuffle(&mut self.rng);
91    }
92}
93
94/// A lock on a slice of choices, with a slice of elements to match them.
95#[derive(Debug)]
96pub struct Hand<'h, T>(Box<[usize]>, &'h [T]);
97
98impl<'h, T> Hand<'h, T> {
99    /// Creates a new hand from a slice of choices, and a slice of elements.
100    ///
101    /// Slice length equivalence isn't checked.
102    pub fn new(choices: Box<[usize]>, elements: &'h [T]) -> Hand<'h, T> {
103        Hand(choices, elements)
104    }
105
106    /// Pick from a series of choices by index, which then picks a corresponding element from the list.
107    ///
108    /// This function uses the internal (frozen) slice of choices to choose an element out of the
109    /// slice of elements.
110    ///
111    /// With a choice slice of `[2,3,1,0,4]`, and an element slice of `[A,B,C,D,E]`, picking choice
112    /// `1` (as an index) would use choice `4` to pick `D`.
113    ///
114    /// `1 -> [..., 4, ...] -> [..., D, ...]`
115    ///
116    /// ```
117    /// use hidden::dispenser::Hand;
118    ///
119    /// // Note, this is not how you'd go about getting a Hand
120    /// // Please use a Dispenser instead
121    /// let choices = Box::from([2,3,1,0,4]);
122    /// let elements = &['a','b','c','d','e'];
123    /// let hand = Hand::new(choices, elements);
124    ///
125    /// assert_eq!(hand.choose(1).unwrap(), &'d'); // idx 1 -> choice 3 -> element d
126    /// assert_eq!(hand.choose(2).unwrap(), &'b'); // idx 2 -> choice 1 -> element b
127    /// ```
128    ///
129    /// Returns [`None`](None) if `idx` exceeds amount of choices, or if choice can't correspond to
130    /// an element in the list.
131    ///
132    /// Returns [`Some`](Some) with a reference to an element if the choosing succeeds.
133    pub fn choose(&self, idx: usize) -> Option<&T> {
134        if let Some(u) = self.0.get(idx) {
135            if let Some(t) = self.1.get(u.to_owned()) {
136                return Some(t);
137            }
138        }
139        None
140    }
141
142    /// Returns the amount of choices that this hand has.
143    pub fn len(&self) -> usize {
144        self.0.len()
145    }
146}
147
148// Tests
149
150#[test]
151fn numbers() {
152    let choices = (1..10).collect::<Vec<u8>>();
153    let mut dispenser = Dispenser::new(choices.len());
154    let hand1 = dispenser.make_hand_unchecked(&choices);
155    let hand2 = dispenser.make_hand_unchecked(&choices);
156    let hand3 = dispenser.make_hand_unchecked(&choices);
157
158    let choice1 = hand1.choose(0).unwrap();
159    assert!(choices.contains(choice1));
160
161    let choice2 = hand2.choose(0).unwrap();
162    assert!(choices.contains(choice2));
163
164    let choice3 = hand1.choose(0).unwrap();
165    assert!(choices.contains(choice3));
166
167    dbg!(hand1, hand2, hand3);
168}
169
170#[test]
171fn enums() {
172    #[derive(Eq, PartialEq, Debug)]
173    enum Something {
174        A,
175        B,
176        C,
177        D,
178    }
179
180    let choices: Vec<Something> = vec![Something::A, Something::B, Something::C, Something::D];
181
182    let mut dispenser = Dispenser::new(choices.len());
183    let hand1 = dispenser.make_hand_unchecked(&choices);
184    let hand2 = dispenser.make_hand_unchecked(&choices);
185
186    let choice1 = hand1.choose(0).unwrap();
187    let choice2 = hand2.choose(0).unwrap();
188
189    assert!(choices.contains(choice1));
190    assert!(choices.contains(choice2));
191
192    dbg!(choice1, choice2);
193    dbg!(hand1, hand2);
194}
195
196#[test]
197fn some_and_none() {
198    let choices = vec![0];
199    let mut dispenser = Dispenser::new(choices.len());
200    let za_hando = dispenser.make_hand_unchecked(&choices);
201
202    assert!(za_hando.choose(0).is_some());
203    assert!(za_hando.choose(1).is_none());
204}