use std::hash::{BuildHasher, RandomState};
#[derive(Clone, Copy, Debug)]
pub struct Rng(u64);
impl Default for Rng {
fn default() -> Self {
Self(RandomState::new().hash_one(0u64))
}
}
impl Rng {
#[cfg(test)]
pub fn seeded(seed: u64) -> Self {
Self(seed)
}
fn next(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9e37_79b9_7f4a_7c15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
z ^ (z >> 31)
}
pub fn below(&mut self, n: usize) -> usize {
if n == 0 {
return 0;
}
(self.next() % n as u64) as usize
}
pub fn shuffle<T>(&mut self, items: &mut [T]) {
for i in (1..items.len()).rev() {
items.swap(i, self.below(i + 1));
}
}
pub fn round<T>(&mut self, mut items: Vec<T>, last: impl Fn(&T) -> bool) -> Vec<T> {
self.shuffle(&mut items);
if items.len() > 1 && last(&items[0]) {
let first = items.remove(0);
items.push(first);
}
items
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_shuffle_keeps_every_item_once_and_moves_them() {
let mut rng = Rng::seeded(7);
let mut items: Vec<u32> = (0..50).collect();
rng.shuffle(&mut items);
assert_ne!(items, (0..50).collect::<Vec<_>>(), "moved");
items.sort_unstable();
assert_eq!(items, (0..50).collect::<Vec<_>>(), "each once");
assert!((0..1000).all(|_| rng.below(3) < 3));
assert_eq!(rng.below(0), 0);
}
#[test]
fn a_copy_gives_the_same_order_again() {
let mut rng = Rng::default();
let mut peek = rng;
let shown = peek.round((0..20).collect::<Vec<u32>>(), |_| false);
assert_eq!(rng.round((0..20).collect(), |_| false), shown);
}
#[test]
fn a_round_never_starts_with_what_just_played() {
for seed in 0..200 {
let round = Rng::seeded(seed).round(vec![1, 2, 3], |&n| n == 2);
assert_ne!(round[0], 2, "seed {seed}");
let mut sorted = round.clone();
sorted.sort_unstable();
assert_eq!(sorted, [1, 2, 3]);
}
assert_eq!(Rng::seeded(1).round(vec![2], |&n| n == 2), [2], "alone");
}
}