reikhelm/reikhelm-core/examples/sample_dungeon.rs
Parley Hatch 277c158378 feat(deepfall): board-game design + balance-by-bot simulator
DEEPFALL: a tabletop game mashing up 9 loved games (SmashUp, Dragon
Rampage, Blood Rage, Catan, EverQuest, Daggerfall, Eye of the Beholder,
Quarriors, SmallWorld). Core fusion = SmallWorld decline + Blood Rage
glorious death -> three exits (Cash Out / Press On / Worthy End) under a
rising Maw that collapses a reikhelm dungeon floor-by-floor.

- deepfall-core: pure deterministic engine (vendored ChaCha8, 11 tests)
- deepfall-sim: balance-by-bot sweeps -> CSV/JSON
- analysis/analyze.py: stdlib ASCII heatmap (no deps)
- DESIGN.md (rules + post-critique revisions), FINDINGS.md (9-iteration
  balance journey), README.md
- reikhelm-core/examples/sample_dungeon.rs: dumps a real generated board

Method: adversarial design critics -> build -> simulate -> tune. 9
iterations compressed combo spread 77.8 -> 32.5 pts (all 36 combos
viable). Fixed deep-content lockout (value rises with the Maw), the
multi-collapse feel-bad (1 collapse/round cap; feel-bad now 0.00/game),
and dead combos. Open finding: Cash-Out and Worthy-End are substitutes
for a glory-maximizing bot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 22:49:33 -06:00

84 lines
2.5 KiB
Rust

//! Generates ONE dungeon for a fixed seed and prints its ASCII map plus a
//! per-region table (id, kind, theme, cell count, bounds) and the entity list.
//!
//! A throwaway viewer used to produce a concrete example board for tabletop
//! game design (DEEPFALL). Deterministic: same seed → same dungeon.
//!
//! Usage:
//! ```text
//! cargo run --example sample_dungeon -p reikhelm-core [seed] [width] [height]
//! ```
use std::env;
use reikhelm_core::recipes::dungeon::{dungeon, DungeonConfig};
fn main() {
let mut args = env::args().skip(1);
let seed: u64 = args.next().and_then(|s| s.parse().ok()).unwrap_or(42);
let width: u32 = args.next().and_then(|s| s.parse().ok()).unwrap_or(48);
let height: u32 = args.next().and_then(|s| s.parse().ok()).unwrap_or(32);
let cfg = DungeonConfig {
width,
height,
..DungeonConfig::default()
};
let pipeline = match dungeon(cfg) {
Ok(p) => p,
Err(e) => {
eprintln!("config rejected: {e}");
std::process::exit(1);
}
};
let map = pipeline.run(seed);
println!("=== reikhelm dungeon — seed {seed}, {width}x{height} ===");
println!();
println!("Legend: # wall . floor + door o pillar ~ water ! lava");
println!();
println!("{}", map.to_ascii());
println!();
// Per-region table. Only real rooms carry a theme; corridors stay None.
println!("--- regions (id | kind | theme | cells | bounds) ---");
let mut rooms = 0usize;
let mut corridors = 0usize;
for r in &map.regions {
if r.cells.is_empty() {
continue;
}
let kind = format!("{:?}", r.kind);
let theme = match r.theme {
Some(t) => format!("{t:?}"),
None => "-".to_string(),
};
let b = r.bounds;
println!(
"{:>3} | {:<8} | {:<10} | {:>4} | ({},{}) {}x{}",
r.id.0,
kind,
theme,
r.cells.len(),
b.x,
b.y,
b.w,
b.h,
);
match r.kind {
reikhelm_core::region::RegionKind::Room => rooms += 1,
reikhelm_core::region::RegionKind::Corridor => corridors += 1,
}
}
println!();
println!("totals: {rooms} rooms, {corridors} corridors");
println!();
// Entities (entrance / exit / treasure / monsters) live as an overlay.
println!("--- entities (kind @ x,y) ---");
for e in &map.entities {
println!("{:?} @ {},{}", e.kind, e.at.x, e.at.y);
}
}