//! Level 10 — dynamic values. The vault ledger accepts numbers and //! dates in many forms. //! //! Paired design note: `l10.md`. //! //! The target is canonical (decimal ints, decimal float, ISO date //! string). The lesson is that the player can write equivalent values //! in hex / octal / exponent forms — all parse to the same number. use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; use serde::Serialize; use serde_yaml::{Mapping, Value}; use crate::describe::Describer; use super::{Generated, Level}; pub struct Dynamic; #[derive(Serialize)] struct DescCtx { gold_dec: i64, gold_hex: String, silver_dec: i64, silver_oct: String, experience: f64, date: String, } impl Level for Dynamic { fn id(&self) -> u8 { 10 } fn name(&self) -> &'static str { "Vault Ledger" } fn generate(&self, seed: u64) -> Generated { let mut rng = ChaCha8Rng::seed_from_u64(seed ^ 0x0000_0000_0000_000A); let gold = rng.gen_range(0x100..=0xFFFi64); let silver = rng.gen_range(0o100..=0o777i64); // Fractional float so the canonical serialisation keeps the `.5`. let experience = rng.gen_range(10..=99) as f64 + 0.5; let year = rng.gen_range(1100..=2050i64); let month = rng.gen_range(1..=12i64); let day = rng.gen_range(1..=28i64); let date = format!("{year:04}-{month:02}-{day:02}"); let mut vault = Mapping::new(); vault.insert(Value::String("gold".to_string()), Value::from(gold)); vault.insert(Value::String("silver".to_string()), Value::from(silver)); vault.insert( Value::String("experience".to_string()), Value::from(experience), ); vault.insert( Value::String("date".to_string()), Value::String(date.clone()), ); let mut top = Mapping::new(); top.insert(Value::String("vault".to_string()), Value::Mapping(vault)); let target_yaml = serde_yaml::to_string(&Value::Mapping(top)).expect("serialise mapping"); let mut d = Describer::new(); d.register( "l10", "The vault ledger demands its values:\n\ gold: {{ gold_dec }} (try hex: 0x{{ gold_hex }})\n\ silver: {{ silver_dec }} (try octal: 0o{{ silver_oct }})\n\ experience: {{ experience }} (any equivalent float form passes)\n\ date: {{ date }} (an ISO-8601 date string)\n\ 💡 Any equivalent numeric form passes — what matters is the parsed value.", ) .expect("register template"); let description = d .render( "l10", &DescCtx { gold_dec: gold, gold_hex: format!("{:X}", gold), silver_dec: silver, silver_oct: format!("{:o}", silver), experience, date, }, ) .expect("render template"); Generated { target_yaml, description, flavor: "🪙 A vault ledger awaits in many ciphers.".to_string(), } } }