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 {
/// A recognizable multi-room dungeon: a 64×40 canvas with each pass at its
/// own sensible default.
/// A recognizable, interconnected multi-room dungeon on a 64×40 canvas.
///
/// The per-pass defaults are mutually consistent: with `bsp.min_leaf = 6`,
/// `rooms.margin = 1`, and `rooms.min_size = 4`, the smallest possible leaf
/// (6 cells on a side) deflates to a 4×4 interior — exactly `min_size` — so
/// every leaf carves a room and none is skipped (the inequality
/// `min_leaf - 2*margin >= min_size` holds: `6 - 2 >= 4`).
/// The recipe deliberately picks a richer flavor than the passes' own
/// primitive defaults: a denser partition with larger rooms (`min_leaf 8`,
/// `max_depth 5`, rooms `5..=11`) yields ~1518 rooms, and
/// `extra_edge_ratio 0.30` adds loop edges so the connectivity graph isn't a
/// 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 {
DungeonConfig {
width: 64,
height: 40,
bsp: BspConfig::default(),
rooms: RoomConfig::default(),
connect: ConnectConfig::default(),
bsp: BspConfig {
min_leaf: 8,
max_depth: 5,
},
rooms: RoomConfig {
min_size: 5,
max_size: 11,
margin: 1,
},
connect: ConnectConfig {
extra_edge_ratio: 0.30,
},
doors: DoorConfig::default(),
}
}
@ -400,12 +414,28 @@ mod tests {
assert_eq!(dungeon(cfg).err(), Some(ConfigError::RoomLargerThanLeaf));
}
/// The exact boundary of the inequality is accepted: `min_leaf - 2*margin ==
/// min_size` is fine (the default config sits right on this edge).
/// The exact boundary of the inequality is accepted: a config where
/// `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]
fn boundary_config_is_accepted() {
// 6 - 2*1 == 4 == min_size: must be Ok.
assert!(DungeonConfig::default().validate().is_ok());
// 6 - 2*1 == 4 == min_size: exactly on the edge, must be 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());
}