use minijinja::{
Error, ErrorKind, State,
value::{Kwargs, Value},
};
use rand_pcg::Pcg64;
use rand_seeder::Seeder;
#[derive(Debug)]
pub struct GobbledyGook {
pub generator: Pcg64,
pub min: u32,
pub max: u32,
}
impl GobbledyGook {
pub fn from(state: &State, options: &Kwargs, fallback_group: &str) -> Result<Self, Error> {
let group = options
.get::<Option<String>>("group")?
.unwrap_or_else(|| fallback_group.to_owned());
let temp = &format!("rng_pos_{group}");
let counter = state
.get_temp(temp)
.unwrap_or_else(|| Value::from(0usize))
.as_usize()
.ok_or_else(|| Error::new(ErrorKind::CannotDeserialize, temp.to_owned()))?;
let pos = options.get::<Option<usize>>("pos")?.unwrap_or(counter);
if pos >= counter {
state.set_temp(temp, Value::from(pos + 1));
}
let static_seed = state
.lookup("static_seed")
.ok_or_else(|| Error::new(ErrorKind::CannotDeserialize, "static_seed"))?;
let static_seed = static_seed
.as_str()
.ok_or_else(|| Error::new(ErrorKind::CannotDeserialize, "static_seed"))?;
let rng = Seeder::from(format!("iocaine://{static_seed}/{group}#{pos}")).into_rng();
Ok(Self {
generator: rng,
min: options.get::<Option<u32>>("min")?.unwrap_or(1),
max: options.get("max")?,
})
}
}
#[cfg(test)]
mod test {
use super::*;
use minijinja::{
Environment, State,
value::{Kwargs, Value},
};
use rand::Rng;
fn prepare(state: &State, options: Vec<(&str, Value)>) -> GobbledyGook {
let options = Kwargs::from_iter(options);
GobbledyGook::from(state, &options, "test").unwrap()
}
#[test]
fn test_pos_auto_advance() {
let mut env = Environment::new();
env.add_global("static_seed", Value::from(""));
let state = env.empty_state();
let mut rng1 = prepare(&state, [("max", Value::from(42))].to_vec());
let n1 = rng1.generator.random_range(1..=42);
let mut rng2 = prepare(&state, [("max", Value::from(42))].to_vec());
let n2 = rng2.generator.random_range(1..=42);
assert_ne!(n1, n2);
}
#[test]
fn test_pos_explicit_same() {
let mut env = Environment::new();
env.add_global("static_seed", Value::from(""));
let state = env.empty_state();
let mut rng1 = prepare(
&state,
[("max", Value::from(42)), ("pos", Value::from(69))].to_vec(),
);
let n1 = rng1.generator.random_range(1..=42);
let mut rng2 = prepare(
&state,
[("max", Value::from(42)), ("pos", Value::from(69))].to_vec(),
);
let n2 = rng2.generator.random_range(1..=42);
assert_eq!(n1, n2);
}
#[test]
fn test_pos_explicit_different() {
let mut env = Environment::new();
env.add_global("static_seed", Value::from(""));
let state = env.empty_state();
let mut rng1 = prepare(
&state,
[("max", Value::from(42)), ("pos", Value::from(69))].to_vec(),
);
let n1 = rng1.generator.random_range(1..=42);
let mut rng2 = prepare(
&state,
[("max", Value::from(42)), ("pos", Value::from(42))].to_vec(),
);
let n2 = rng2.generator.random_range(1..=42);
assert_ne!(n1, n2);
}
#[test]
fn test_pos_mixed_lookback() {
let mut env = Environment::new();
env.add_global("static_seed", Value::from(""));
let state = env.empty_state();
let mut rng1 = prepare(&state, [("max", Value::from(42))].to_vec());
let n1 = rng1.generator.random_range(1..=42);
let mut rng2 = prepare(
&state,
[("max", Value::from(42)), ("pos", Value::from(0))].to_vec(),
);
let n2 = rng2.generator.random_range(1..=42);
let mut rng3 = prepare(&state, [("max", Value::from(42))].to_vec());
let n3 = rng3.generator.random_range(1..=42);
assert_eq!(n1, n2);
assert_ne!(n3, n1);
assert_ne!(n3, n2);
}
#[test]
fn test_pos_mixed_current() {
let mut env = Environment::new();
env.add_global("static_seed", Value::from(""));
let state = env.empty_state();
let mut rng1 = prepare(
&state,
[("max", Value::from(42)), ("pos", Value::from(0))].to_vec(),
);
let n1 = rng1.generator.random_range(1..=42);
let mut rng2 = prepare(&state, [("max", Value::from(42))].to_vec());
let n2 = rng2.generator.random_range(1..=42);
let mut rng3 = prepare(&state, [("max", Value::from(42))].to_vec());
let n3 = rng3.generator.random_range(1..=42);
assert_ne!(n1, n2);
assert_ne!(n3, n1);
assert_ne!(n3, n2);
}
#[test]
fn test_pos_mixed_lookahead() {
let mut env = Environment::new();
env.add_global("static_seed", Value::from(""));
let state = env.empty_state();
let mut rng1 = prepare(
&state,
[("max", Value::from(42)), ("pos", Value::from(1))].to_vec(),
);
let n1 = rng1.generator.random_range(1..=42);
let mut rng2 = prepare(&state, [("max", Value::from(42))].to_vec());
let n2 = rng2.generator.random_range(1..=42);
let mut rng3 = prepare(&state, [("max", Value::from(42))].to_vec());
let n3 = rng3.generator.random_range(1..=42);
assert_ne!(n1, n2);
assert_ne!(n2, n3);
assert_ne!(n1, n3);
}
}