Files
yamlabyrinth/src/levels/mod.rs
Simonas Kareiva 07f82a7086 Add level 4: The Chest (lists)
Top-level `chest:` key with a 3-5 item sequence drawn from a small pool.
Per-seed deterministic via ChaCha8Rng XOR'd with 0x..04. Description
rendered with tera; flavor uses a 📦 emoji.

Not wired into levels::registry() yet — integration belongs to a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:51:35 +03:00

89 lines
2.5 KiB
Rust

//! Levels — hand-written Rust generators paired with design notes.
//!
//! Each level is implemented in `l<XX>_<name>.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<XX>.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;
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<Self> {
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<XX>_<name>.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<Box<dyn Level>> {
vec![
Box::new(l01_minimum::Minimum),
Box::new(l02_kv::KeyValue),
Box::new(l03_dict::Dict),
]
}