# Task 8: RoomCarver Pass **Depends on:** Task 6 ## Context Files - `.dev/2026-05-28-procgen-map-core/spec/design.md` — §4.7 (Shaper role), §5 (data flow). - `.dev/2026-05-28-procgen-map-core/README.md` — inter-pass data contract (RoomCarver row). - `reikhelm-core/src/pass.rs` — `Pass`, `GenContext` (Task 6). - `reikhelm-core/src/region.rs`, `reikhelm-core/src/map.rs` — `Region`, `RegionKind::Room`, `Tile::Floor`. - `reikhelm-core/src/passes/mod.rs` — append the module declaration here. ## Files - Create: `reikhelm-core/src/passes/room.rs` — `RoomConfig`, `RoomCarver`. - Modify: `reikhelm-core/src/passes/mod.rs` — append `pub mod room;` and re-export `RoomCarver`, `RoomConfig`. ## What to Build A shaper (spec §4.7) that turns BSP leaves into actual rooms. For each existing `Room` region (the placeholders from `BspPartition`), it picks a room rectangle that fits inside the leaf `bounds` (random size within `RoomConfig` limits, leaving a margin from the leaf edge so corridors and walls have room), carves `Tile::Floor` into `ctx.tiles` over that rectangle, and updates the region's `bounds` to the actual room rect and fills `cells` with the carved points. A leaf too small to hold a `min_size` room is skipped gracefully: its region is **left in place with empty `cells` (never removed)** — removal would break the `RegionId`-as-index invariant (Task 5). A Room region with empty `cells` denotes "not actually a room"; downstream room consumers (MstConnect, CorridorCarver) treat only Room regions with non-empty `cells` as real rooms. (In the assembled dungeon recipe this case never fires — Task 12's config validation guarantees every leaf fits a room — but the pass must handle it without panicking per spec §8.) This pass reads/writes `ctx.regions` and `ctx.tiles` only; it does **not** depend on `BspPartition`'s code, only on the documented "placeholder Room regions" precondition — so tests construct that precondition directly. ## Interface Dependencies - **Consumes:** `Pass`, `GenContext`, `Rng` (Task 6); `Region`, `Tile` (Task 5). - **Produces:** - `struct RoomConfig { min_size: i32, max_size: i32, margin: i32 }` (+ `Default`). - `struct RoomCarver { /* holds RoomConfig */ }` — `new(cfg) -> Self`; `impl Pass` with `name() == "room_carver"`. - **Writes to context:** `Floor` tiles; finalized Room region `bounds`/`cells`. ## Tests - Given a `GenContext` with two placeholder Room regions over a `Wall` grid, after running: each carved room's `bounds` lies within its original leaf bounds and within the map; the floor cell count matches `bounds` area. - Carved floors do not extend outside the originating leaf (margin respected). - A leaf smaller than `min_size` is skipped without panicking. - Determinism: same seed/sub-stream → identical carved rooms. ## Success Criteria - [ ] `RoomCarver` carves `Floor` rooms inside leaf bounds, respecting size/margin, and finalizes region `bounds`/`cells`. - [ ] Undersized leaves are handled gracefully (no panic). - [ ] `pub mod room;` appended to `passes/mod.rs`. - [ ] Tests pass: `cargo test -p reikhelm-core room` ## Commit `"feat(core): RoomCarver pass"`