iocaine 2.1.0

The deadliest poison known to AI
Documentation
// SPDX-FileCopyrightText: 2025 Gergely Nagy
// SPDX-FileContributor: Gergely Nagy
//
// SPDX-License-Identifier: MIT

use minijinja::{
    value::{Kwargs, Value},
    Error, State,
};
use rand_pcg::Pcg64;
use rand_seeder::Seeder;

#[derive(Debug)]
pub struct GobbledyGook {
    pub gen: 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(fallback_group.to_string());

        let temp = &format!("rng_pos_{}", group);
        let counter = state
            .get_temp(temp)
            .unwrap_or(Value::from(0usize))
            .as_usize()
            .unwrap();

        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").unwrap();
        let static_seed = static_seed.as_str().unwrap();

        let rng = Seeder::from(format!("iocaine://{}/{}#{}", static_seed, group, pos)).into_rng();

        Ok(Self {
            gen: rng,
            min: options.get::<Option<u32>>("min")?.unwrap_or(1),
            max: options.get("max")?,
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use minijinja::{
        value::{Kwargs, Value},
        Environment, State,
    };
    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.gen.random_range(1..=42);

        let mut rng2 = prepare(&state, [("max", Value::from(42))].to_vec());
        let n2 = rng2.gen.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.gen.random_range(1..=42);

        let mut rng2 = prepare(
            &state,
            [("max", Value::from(42)), ("pos", Value::from(69))].to_vec(),
        );
        let n2 = rng2.gen.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.gen.random_range(1..=42);

        let mut rng2 = prepare(
            &state,
            [("max", Value::from(42)), ("pos", Value::from(42))].to_vec(),
        );
        let n2 = rng2.gen.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.gen.random_range(1..=42);

        let mut rng2 = prepare(
            &state,
            [("max", Value::from(42)), ("pos", Value::from(0))].to_vec(),
        );
        let n2 = rng2.gen.random_range(1..=42);

        let mut rng3 = prepare(&state, [("max", Value::from(42))].to_vec());
        let n3 = rng3.gen.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.gen.random_range(1..=42);

        let mut rng2 = prepare(&state, [("max", Value::from(42))].to_vec());
        let n2 = rng2.gen.random_range(1..=42);

        let mut rng3 = prepare(&state, [("max", Value::from(42))].to_vec());
        let n3 = rng3.gen.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.gen.random_range(1..=42);

        let mut rng2 = prepare(&state, [("max", Value::from(42))].to_vec());
        let n2 = rng2.gen.random_range(1..=42);

        let mut rng3 = prepare(&state, [("max", Value::from(42))].to_vec());
        let n3 = rng3.gen.random_range(1..=42);

        assert_ne!(n1, n2);
        assert_ne!(n2, n3);
        assert_ne!(n1, n3);
    }
}