feat(core): richer default dungeon — denser rooms + loop edges

Default was a pure MST (extra_edge_ratio 0.0), which reads as a linear
chain of rooms. Retune DungeonConfig::default to min_leaf 8 / max_depth 5,
rooms 5..=11, and extra_edge_ratio 0.30 — ~17 interconnected rooms with
loops. Validation inequality still holds (8 - 2 >= 5); boundary test now
uses an explicit edge-case config since the default no longer sits on it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Parley Hatch 2026-05-28 22:53:03 -06:00
parent 5f195fdb0b
commit ab195306a5

View file

@ -63,21 +63,35 @@ pub struct DungeonConfig {
} }
impl Default for DungeonConfig { impl Default for DungeonConfig {
/// A recognizable multi-room dungeon: a 64×40 canvas with each pass at its /// A recognizable, interconnected multi-room dungeon on a 64×40 canvas.
/// own sensible default.
/// ///
/// The per-pass defaults are mutually consistent: with `bsp.min_leaf = 6`, /// The recipe deliberately picks a richer flavor than the passes' own
/// `rooms.margin = 1`, and `rooms.min_size = 4`, the smallest possible leaf /// primitive defaults: a denser partition with larger rooms (`min_leaf 8`,
/// (6 cells on a side) deflates to a 4×4 interior — exactly `min_size` — so /// `max_depth 5`, rooms `5..=11`) yields ~1518 rooms, and
/// every leaf carves a room and none is skipped (the inequality /// `extra_edge_ratio 0.30` adds loop edges so the connectivity graph isn't a
/// `min_leaf - 2*margin >= min_size` holds: `6 - 2 >= 4`). /// strict tree — a pure MST (the `ConnectConfig` primitive default) reads as a
/// linear chain of rooms, which is duller to explore. `door_chance` stays at
/// 0.5 (an even mix of doors and open archways).
///
/// These stay consistent so RoomCarver never skips a leaf: the validation
/// inequality `min_leaf - 2*margin >= min_size` holds with room to spare
/// (`8 - 2 >= 5`). See [`validate`](DungeonConfig::validate).
fn default() -> Self { fn default() -> Self {
DungeonConfig { DungeonConfig {
width: 64, width: 64,
height: 40, height: 40,
bsp: BspConfig::default(), bsp: BspConfig {
rooms: RoomConfig::default(), min_leaf: 8,
connect: ConnectConfig::default(), max_depth: 5,
},
rooms: RoomConfig {
min_size: 5,
max_size: 11,
margin: 1,
},
connect: ConnectConfig {
extra_edge_ratio: 0.30,
},
doors: DoorConfig::default(), doors: DoorConfig::default(),
} }
} }
@ -400,12 +414,28 @@ mod tests {
assert_eq!(dungeon(cfg).err(), Some(ConfigError::RoomLargerThanLeaf)); assert_eq!(dungeon(cfg).err(), Some(ConfigError::RoomLargerThanLeaf));
} }
/// The exact boundary of the inequality is accepted: `min_leaf - 2*margin == /// The exact boundary of the inequality is accepted: a config where
/// min_size` is fine (the default config sits right on this edge). /// `min_leaf - 2*margin == min_size` (the tightest legal fit) validates Ok,
/// guarding the `>=` (not `>`) in the check. The shipped default sits safely
/// inside this bound (`8 - 2 >= 5`), so we build a boundary config explicitly.
#[test] #[test]
fn boundary_config_is_accepted() { fn boundary_config_is_accepted() {
// 6 - 2*1 == 4 == min_size: must be Ok. // 6 - 2*1 == 4 == min_size: exactly on the edge, must be Ok.
assert!(DungeonConfig::default().validate().is_ok()); let boundary = DungeonConfig {
bsp: BspConfig {
min_leaf: 6,
max_depth: 4,
},
rooms: RoomConfig {
min_size: 4,
max_size: 10,
margin: 1,
},
..DungeonConfig::default()
};
assert!(boundary.validate().is_ok());
assert!(dungeon(boundary).is_ok());
// The shipped default validates too.
assert!(dungeon(DungeonConfig::default()).is_ok()); assert!(dungeon(DungeonConfig::default()).is_ok());
} }