149 lines
4.4 KiB
Rust
149 lines
4.4 KiB
Rust
//! Level 3 — dictionaries. Each direction leads to a feature with its
|
|
//! own type + one characteristic property.
|
|
//!
|
|
//! Paired design note: `l03.md`.
|
|
|
|
use rand::seq::SliceRandom;
|
|
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 Dict;
|
|
|
|
const DIRECTIONS: &[&str] = &["left", "right", "straight", "back", "up", "down"];
|
|
|
|
enum Feature {
|
|
Door,
|
|
Tunnel,
|
|
Pit,
|
|
Stairs,
|
|
Wall,
|
|
Altar,
|
|
}
|
|
|
|
impl Feature {
|
|
fn name(&self) -> &'static str {
|
|
match self {
|
|
Feature::Door => "door",
|
|
Feature::Tunnel => "tunnel",
|
|
Feature::Pit => "pit",
|
|
Feature::Stairs => "stairs",
|
|
Feature::Wall => "wall",
|
|
Feature::Altar => "altar",
|
|
}
|
|
}
|
|
|
|
fn random(rng: &mut ChaCha8Rng) -> Self {
|
|
match rng.gen_range(0..6) {
|
|
0 => Feature::Door,
|
|
1 => Feature::Tunnel,
|
|
2 => Feature::Pit,
|
|
3 => Feature::Stairs,
|
|
4 => Feature::Wall,
|
|
_ => Feature::Altar,
|
|
}
|
|
}
|
|
|
|
/// One characteristic property: (key, value).
|
|
fn property(&self, rng: &mut ChaCha8Rng) -> (&'static str, Value) {
|
|
match self {
|
|
Feature::Door => ("locked", Value::Bool(rng.gen_bool(0.5))),
|
|
Feature::Tunnel => ("depth", Value::from(rng.gen_range(5..=30i64))),
|
|
Feature::Pit => ("depth", Value::from(rng.gen_range(5..=30i64))),
|
|
Feature::Stairs => {
|
|
let going = if rng.gen_bool(0.5) { "up" } else { "down" };
|
|
("going", Value::String(going.to_string()))
|
|
}
|
|
Feature::Wall => ("cracked", Value::Bool(rng.gen_bool(0.5))),
|
|
Feature::Altar => ("blessed", Value::Bool(rng.gen_bool(0.5))),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct DescCtx {
|
|
entries: Vec<DescEntry>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct DescEntry {
|
|
direction: String,
|
|
feature: String,
|
|
prop_name: String,
|
|
prop_value: String,
|
|
}
|
|
|
|
impl Level for Dict {
|
|
fn id(&self) -> u8 {
|
|
3
|
|
}
|
|
|
|
fn name(&self) -> &'static str {
|
|
"Dictionaries"
|
|
}
|
|
|
|
fn generate(&self, seed: u64) -> Generated {
|
|
// Per-level constant so the same `current_seed` produces different
|
|
// content per level.
|
|
let mut rng = ChaCha8Rng::seed_from_u64(seed ^ 0x0000_0000_0000_0003);
|
|
let n = rng.gen_range(2..=3);
|
|
let directions: Vec<&'static str> =
|
|
DIRECTIONS.choose_multiple(&mut rng, n).copied().collect();
|
|
|
|
let mut top = Mapping::new();
|
|
let mut entries = Vec::with_capacity(directions.len());
|
|
for d in &directions {
|
|
let feature = Feature::random(&mut rng);
|
|
let (prop_name, prop_value) = feature.property(&mut rng);
|
|
|
|
let mut inner = Mapping::new();
|
|
inner.insert(
|
|
Value::String("type".to_string()),
|
|
Value::String(feature.name().to_string()),
|
|
);
|
|
inner.insert(
|
|
Value::String(prop_name.to_string()),
|
|
prop_value.clone(),
|
|
);
|
|
top.insert(Value::String((*d).to_string()), Value::Mapping(inner));
|
|
|
|
let prop_value_str = match &prop_value {
|
|
Value::Bool(b) => b.to_string(),
|
|
Value::Number(n) => n.to_string(),
|
|
Value::String(s) => s.clone(),
|
|
_ => String::new(),
|
|
};
|
|
entries.push(DescEntry {
|
|
direction: (*d).to_string(),
|
|
feature: feature.name().to_string(),
|
|
prop_name: prop_name.to_string(),
|
|
prop_value: prop_value_str,
|
|
});
|
|
}
|
|
|
|
let target_yaml =
|
|
serde_yaml::to_string(&Value::Mapping(top)).expect("serialise mapping");
|
|
|
|
let mut d = Describer::new();
|
|
d.register(
|
|
"l03",
|
|
"{% for e in entries %}- {{ e.direction }} → {{ e.feature }} ({{ e.prop_name }}: {{ e.prop_value }})\n{% endfor %}\n💡 Each feature is a dictionary — give it a `type:` key plus its property.",
|
|
)
|
|
.expect("register template");
|
|
let description = d
|
|
.render("l03", &DescCtx { entries })
|
|
.expect("render template");
|
|
|
|
Generated {
|
|
target_yaml,
|
|
description,
|
|
flavor: "🧭 You stand at a junction. Each path reveals its own detail.".to_string(),
|
|
}
|
|
}
|
|
}
|