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}