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());
}