Eighth pass (after RoomCarver, before MstConnect): re-sculpts some rooms into
organic caves.
- CaveShaper/CaveConfig (cave_chance, min_room, fill, steps): fills the room
rect with noise, runs the classic 4-5 cellular automaton `steps` times, keeps
the LARGEST 4-connected floor component, and re-carves the room to that blob
(cells + bounds updated; the rest of the footprint reverts to Wall). Too-small
results leave the room clean. Caves stay Room regions — their organic outline
reads as a cavern, so the renderer needs no change.
- CorridorCarver now anchors to the room cell NEAREST bounds.center() instead of
the center point itself. Output-preserving for convex/centered shapes (the
center cell is already floor), but robust for irregular cave blobs whose AABB
center may fall in rock — so a cavernized room is always reachable.
- DungeonConfig gains `caves`; recipe inserts CaveShaper after RoomCarver.
Core: 129 tests green (4 new: cave is connected floor within room, small/zero
skip, determinism); connectivity holds with caves in the default recipe. clippy
clean. Pipeline is now 8 passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>