Commit graph

9 commits

Author SHA1 Message Date
0aec5e927d feat(core): room theming — RoomThemer classifier + themed renderer (10th pass)
Adds RegionTheme + RoomThemer: a pure classifier (no tile changes) that
labels each room (terrain-driven Forge/Cistern/Hall, special Throne/
Threshold, weighted Crypt/Den/Library/Vault/Stone). Renderer tints themed
rooms; EntityPlacer biases contents by theme. Entrance/exit now chosen
RNG-free from geometry so themer & placer agree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:03:53 -06:00
00502153f3 feat(core): items & creatures — entity layer + EntityPlacer (9th pass)
The roadmap capstone: the dungeon is now populated.

- New entity layer: src/entity.rs (Entity { kind, at }, EntityKind {Entrance,
  Exit, Treasure, Monster}); Map gains an `entities: Vec<Entity>` overlay field
  (serde round-tripped). Entities aren't terrain, so no new Tile and the frozen
  viz is untouched.
- EntityPlacer/EntityConfig: places one Entrance (random room), one Exit (the
  room farthest from it — a traversal goal), per-room Treasure (treasure_chance)
  and Monsters (monster_chance, 1..=max, never in the entrance room). All on
  plain Floor only, never two per cell, fully deterministic.
- Handoff via the Blackboard (the designed side channel for non-tile/region/edge
  data) under entity::BLACKBOARD_KEY, harvested by Pipeline into Map.entities —
  so GenContext's 25 literals stay untouched.
- Renderer draws iconic markers on the final stage: green beacon (entrance),
  cyan portal (exit), gold diamond (treasure), crimson eyed-blob (monster), each
  with a soft glow. New entities toggle + treasure/monster sliders + readout.

Core: 135 tests green (6 new: one entrance/one exit on distinct floor, entrance
room monster-free, determinism, empty-map, recipe populated-check). clippy
clean. Pipeline is now 9 passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 01:11:13 -06:00
381a52c310 feat(core): cellular-automata caverns (CaveShaper) + robust corridor anchoring
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>
2026-05-31 00:46:33 -06:00
e7e8bb9187 feat(core): water & lava pools (PoolDecorator) + lava lighting
Seventh pass (before pillars): floods an organic blob of Water or Lava into
some larger rooms via randomized BFS growth.

- New Tile::Water / Tile::Lava (impassable hazards). Exhaustive matches updated
  (ascii '~'/'!', frozen viz colors, wasm codes 4/5).
- PoolDecorator/PoolConfig (water_chance, lava_chance, min_room): blob is
  interior-only (>= 2 from edge, never the center), and a connectivity guard
  verifies the room's remaining floor stays 4-connected with the center intact
  before committing — so a pool never orphans part of a room.
- DungeonConfig gains `pools`; recipe inserts PoolDecorator after DoorPlacer.
- Renderer: water as cool reflective pools; lava as molten rock that ALSO
  throws warm light onto nearby floor (BFS light field, wall-blocked) — lava
  chambers visibly glow. New water/lava sliders.

Core: 125 tests green (4 new: interior placement, floor stays connected,
zero/small-room skip, determinism); connectivity holds with pools in the
default recipe. clippy clean (core + viz).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 00:41:48 -06:00
b1449248f8 feat(core): pillars — PillarPlacer decorator + Tile::Pillar
A finishing decorator that scatters free-standing pillar columns into larger
rooms on a spaced interior grid. Sixth pass in the dungeon recipe.

- New Tile::Pillar (impassable like Wall, drawn distinctly). Exhaustive matches
  updated: ascii ('o'), frozen macroquad viz (still compiles), wasm code 3.
- PillarPlacer/PillarConfig (room_chance, min_room, spacing): places isolated
  single-cell pillars only on interior room floor (>= 2 from the edge, never the
  center), on a grid with spacing >= 2 — so the floor stays 4-connected by
  construction and a room is never orphaned. Respects non-rect room shapes
  (pillars land only on actual carved floor).
- DungeonConfig gains `pillars`; recipe appends PillarPlacer after DoorPlacer.
- Renderer draws pillars as lit stone columns with a contact shadow; new
  "pillars" density slider.

Core: 121 tests green (5 new: interior-only placement, floor stays connected,
zero-chance/small-room skip, determinism). clippy clean (core + viz).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 00:25:37 -06:00
a1d2a63937 feat(core): polygonal room shapes (rect/octagon/ellipse/plus)
RoomCarver now rolls a per-room shape within its chosen rectangle, weighted by
a new RoomConfig.shapes (ShapeWeights). The rect is still picked first and
unchanged, so room centers — and the corridors wired between them — are
identical to before; only the carved interior differs.

- Shapes built from per-row contiguous spans → guaranteed 4-connected and
  convex, so flood-fill never strands a cell.
- Hard invariant: every shape contains rect.center() (the corridor target),
  with a fallback to a full rect for tiny rooms or any non-covering shape —
  keeps MstConnect/CorridorCarver correct.
- ShapeWeights::default() is rect-only (preserves old behavior + existing
  tests); the dungeon recipe opts into ShapeWeights::varied().
- Configs lose Eq (now carry f64 weights); serde unaffected.

Core: 116 tests green (new forced-shape invariants test: in-bounds, center
carved, non-rectangular, 4-connected); connectivity + determinism + door tests
all pass with varied shapes active. Surfaced as 4 shape-weight sliders in the
web playground.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 00:16:45 -06:00
f849d82d31 feat(wasm+web): live browser dungeon playground (WASM core + atmospheric renderer)
Phase 0+1 of the browser viz effort:
- core: drop unused `rand` dep (removes getrandom/libc → clean wasm32 build);
  derive serde on DungeonConfig + 5 sub-configs so the bridge can (de)serialize
- reikhelm-wasm: thin wasm-bindgen cdylib exposing generate(seed, config_json)
  -> JSON envelope (tiles as int codes, regions, edges, per-pass snapshots) and
  default_config_json(); holds no generation logic
- workspace: add reikhelm-wasm member; default-members keeps host cargo
  test/clippy on core+viz only
- reikhelm-web: vanilla-JS + Canvas 2D renderer running the real core live in
  the browser. Atmospheric look — lit stone floor, raised stone walls with
  rim-lit carved edges + top bevel, room light-pooling, amber door thresholds,
  vignette. Controls: seed/reroll, 9 config sliders, stage scrubber, semantic
  overlays (region outlines / graph / grid). window.reikhelm hooks for
  Playwright-driven iteration.

Core stays 115 tests green. Driven + screenshotted via Playwright MCP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 00:07:24 -06:00
ab195306a5 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>
2026-05-28 22:53:03 -06:00
4e54d3b7ba feat(core): dungeon recipe + end-to-end integration tests
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:24:53 -06:00