diff --git a/src/levels/l07_complex.rs b/src/levels/l07_complex.rs new file mode 100644 index 0000000..5bb7280 --- /dev/null +++ b/src/levels/l07_complex.rs @@ -0,0 +1,142 @@ +//! Level 7 — complex data structures. The full map of a single floor. +//! +//! Paired design note: `l07.md`. + +use rand::seq::SliceRandom; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; +use serde::Serialize; +use serde_yaml::{Mapping, Sequence, Value}; + +use crate::describe::Describer; + +use super::{Generated, Level}; + +pub struct Complex; + +const EXITS: &[&str] = &["north", "south", "east", "west", "up", "down"]; +const CONTENTS: &[&str] = &[ + "torch", "bench", "gold", "ruby", "scroll", "tome", "dagger", "shield", +]; +const ROOMS: &[(&str, &str)] = &[ + ("entrance", "hall"), + ("treasury", "vault"), + ("library", "study"), + ("kitchen", "scullery"), +]; + +#[derive(Serialize)] +struct DescCtx { + floor: i64, + rooms: Vec, +} + +#[derive(Serialize)] +struct RoomDesc { + name: String, + kind: String, + locked: bool, + exits: Vec, + contents: Vec, +} + +impl Level for Complex { + fn id(&self) -> u8 { + 7 + } + + fn name(&self) -> &'static str { + "The Floor Map" + } + + fn generate(&self, seed: u64) -> Generated { + let mut rng = ChaCha8Rng::seed_from_u64(seed ^ 0x0000_0000_0000_0007); + let floor = rng.gen_range(1..=5i64); + let chosen: Vec<&(&'static str, &'static str)> = + ROOMS.choose_multiple(&mut rng, 2).collect(); + + let mut rooms_map = Mapping::new(); + let mut desc_rooms = Vec::new(); + for (name, kind) in &chosen { + let exits_n = rng.gen_range(1..=3); + let exits: Vec<&'static str> = + EXITS.choose_multiple(&mut rng, exits_n).copied().collect(); + let contents_n = rng.gen_range(1..=3); + let contents: Vec<&'static str> = CONTENTS + .choose_multiple(&mut rng, contents_n) + .copied() + .collect(); + let locked = rng.gen_bool(0.5); + + let mut room = Mapping::new(); + room.insert( + Value::String("type".to_string()), + Value::String((*kind).to_string()), + ); + room.insert(Value::String("locked".to_string()), Value::Bool(locked)); + let exits_seq: Sequence = exits + .iter() + .map(|e| Value::String((*e).to_string())) + .collect(); + let contents_seq: Sequence = contents + .iter() + .map(|c| Value::String((*c).to_string())) + .collect(); + room.insert( + Value::String("exits".to_string()), + Value::Sequence(exits_seq), + ); + room.insert( + Value::String("contents".to_string()), + Value::Sequence(contents_seq), + ); + rooms_map.insert(Value::String((*name).to_string()), Value::Mapping(room)); + + desc_rooms.push(RoomDesc { + name: (*name).to_string(), + kind: (*kind).to_string(), + locked, + exits: exits.iter().map(|s| s.to_string()).collect(), + contents: contents.iter().map(|s| s.to_string()).collect(), + }); + } + + let mut top = Mapping::new(); + top.insert(Value::String("floor".to_string()), Value::from(floor)); + top.insert( + Value::String("rooms".to_string()), + Value::Mapping(rooms_map), + ); + + let target_yaml = + serde_yaml::to_string(&Value::Mapping(top)).expect("serialise mapping"); + + let mut d = Describer::new(); + d.register( + "l07", + "Floor {{ floor }}.\n\ + {% for r in rooms %}\n\ + {{ r.name }} — a {{ r.kind }} (locked: {{ r.locked }})\n\ + \texits: {% for e in r.exits %}{{ e }}{% if not loop.last %}, {% endif %}{% endfor %}\n\ + \tcontents: {% for c in r.contents %}{{ c }}{% if not loop.last %}, {% endif %}{% endfor %}\n\ + {% endfor %}\n\ + 💡 Combine maps, lists, and scalars — `floor:` is an int, each room is a dict with two lists.", + ) + .expect("register template"); + let description = d + .render( + "l07", + &DescCtx { + floor, + rooms: desc_rooms, + }, + ) + .expect("render template"); + + Generated { + target_yaml, + description, + flavor: "🗺 The map of this floor is rich with detail.".to_string(), + } + } +} diff --git a/src/levels/mod.rs b/src/levels/mod.rs index 0a04c8b..fa55f6d 100644 --- a/src/levels/mod.rs +++ b/src/levels/mod.rs @@ -16,6 +16,7 @@ pub mod l03_dict; pub mod l04_list; pub mod l05_dict_list; pub mod l06_anchors; +pub mod l07_complex; use serde::{Deserialize, Serialize};