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