//! Levels — hand-written Rust generators paired with design notes. //! //! Each level is implemented in `l_.rs` and is the authoritative //! source of truth for what target YAML the player must reproduce and how //! the description is rendered. //! //! The paired `l.md` file is a **design note only**: it documents the //! intended scene and a minimal example of the target YAML. `.md` files //! are *not* loaded at runtime — there is no `include_str!` and no //! markdown parser. If a `.md` and its paired `.rs` ever disagree, the //! `.rs` wins. pub mod l01_minimum; pub mod l02_kv; pub mod l03_dict; pub mod l04_list; pub mod l05_dict_list; pub mod l06_anchors; pub mod l07_complex; pub mod l08_tags; pub mod l09_operators; pub mod l10_dynamic; pub mod l11_adv_anchors; use serde::{Deserialize, Serialize}; /// Awarded per level based on the grade score. Replaces the old /// game-wide difficulty tier. Thresholds: /// - Gold ≥ 95 % /// - Silver ≥ 80 % /// - Bronze ≥ 70 % /// - Below 70 % → no nugget (retry). #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum Nugget { Bronze, Silver, Gold, } impl Nugget { /// Map a grade ratio (0.0..=1.0) to a nugget. `None` means the /// player didn't clear the level — retry without advancing. pub fn from_score(score: f64) -> Option { if score >= 0.95 { Some(Nugget::Gold) } else if score >= 0.80 { Some(Nugget::Silver) } else if score >= 0.70 { Some(Nugget::Bronze) } else { None } } pub fn emoji(self) -> &'static str { match self { Self::Bronze => "🥉", Self::Silver => "🥈", Self::Gold => "🥇", } } pub fn name(self) -> &'static str { match self { Self::Bronze => "Bronze", Self::Silver => "Silver", Self::Gold => "Gold", } } } /// What `Level::generate` returns: the canonical target YAML to grade /// against, the player-facing description (already rendered), and the /// dungeon flavor line. pub struct Generated { pub target_yaml: String, pub description: String, pub flavor: String, } /// One level's generator. Implementations live in `l_.rs`. pub trait Level { fn id(&self) -> u8; fn name(&self) -> &'static str; fn generate(&self, seed: u64) -> Generated; } /// Ordered registry of all levels. `registry()[0]` is level 1. pub fn registry() -> Vec> { vec![ Box::new(l01_minimum::Minimum), Box::new(l02_kv::KeyValue), Box::new(l03_dict::Dict), Box::new(l04_list::List), Box::new(l05_dict_list::DictList), Box::new(l06_anchors::Anchors), Box::new(l07_complex::Complex), Box::new(l08_tags::Tags), Box::new(l09_operators::Operators), Box::new(l10_dynamic::Dynamic), Box::new(l11_adv_anchors::AdvAnchors), ] }