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>
84 lines
2.5 KiB
Rust
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);
|
|
}
|
|
}
|