reikhelm/.dev/2026-05-28-procgen-map-core/tasks/05-data-model.md
Parley Hatch 08e99e38a3 chore: scaffold reikhelm cargo workspace (core + viz crates)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 20:56:24 -06:00

47 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Task 5: Data Model & Output Contract (Tile, Region, Map, ASCII)
**Depends on:** Task 2, Task 3
## Context Files
- `.dev/2026-05-28-procgen-map-core/spec/design.md` — §3 (`Map` contract), §4.3 (Tile), §4.5 (Region & connectivity), §4.9 (ASCII renderer).
- `reikhelm-core/src/geometry.rs``Point`, `Rect`.
- `reikhelm-core/src/grid.rs``Grid<T>`.
- `reikhelm-core/src/lib.rs` — append module declarations here.
## Files
- Create: `reikhelm-core/src/region.rs``RegionId`, `RegionKind`, `Region`, `Edge`, `ConnGraph`.
- Create: `reikhelm-core/src/map.rs``Tile`, `Map`.
- Create: `reikhelm-core/src/ascii.rs` — ASCII renderer for `Map`.
- Modify: `reikhelm-core/src/lib.rs` — append `pub mod region;`, `pub mod map;`, `pub mod ascii;`.
## What to Build
The semantic data layer and the project's central output contract, `Map` (spec §3). `Map` is plain data describing *what is where***no colors, sprites, or rendering info**. Every type derives `serde::{Serialize, Deserialize}` (spec §3) so a `Map` can round-trip to JSON. The ASCII renderer (spec §4.9) lives in core because it has no external deps and is the workhorse for downstream tests.
Note the `Edge.at` field is `Option<Point>` (refines spec §4.5's `Point`): the door location is unknown until `DoorPlacer` runs, so `MstConnect` creates edges with `at: None`.
## Interface Dependencies
- **Consumes:** `Point`, `Rect` (Task 2), `Grid<T>` (Task 3).
- **Produces:**
- `enum Tile { Wall, Floor, Door }``Default` = `Wall`; derives `Copy, Eq`.
- `type RegionId` as a newtype `RegionId(usize)`. **Invariant:** `RegionId(n)` always equals the index `n` of that region in `ctx.regions`/`Map::regions`. Regions are append-only — never removed or reordered — so this id-as-index relationship holds for the life of the map (edges and lookups rely on it).
- `enum RegionKind { Room, Corridor }` (extensible).
- `struct Region { id: RegionId, kind: RegionKind, bounds: Rect, cells: Vec<Point> }`**fields `pub`** (read across modules by passes and viz).
- `struct Edge { a: RegionId, b: RegionId, at: Option<Point> }`**fields `pub`** (constructed by `MstConnect`, `at` written by `DoorPlacer`, read by viz).
- `struct ConnGraph { edges: Vec<Edge> }``new`, `add_edge(a, b) -> ()` (pushes `at: None`), `edges() -> &[Edge]`, **`edges_mut() -> &mut [Edge]`** (so `DoorPlacer` can populate `at`), `neighbors(RegionId) -> impl Iterator<Item = RegionId>`.
- `struct Map { tiles: Grid<Tile>, regions: Vec<Region>, graph: ConnGraph, seed: u64, width: u32, height: u32 }` — fields `pub` (the output contract, spec §3).
- `impl Map { fn to_ascii(&self) -> String; fn print_ascii(&self); }``Wall``#`, `Floor``.`, `Door``+`, one row per line.
## Tests
- A small hand-built `Map` serializes to JSON and deserializes back equal (serde round-trip).
- `ConnGraph::add_edge` stores `at: None`; `neighbors` returns both endpoints' counterparts.
- `Map::to_ascii` on a hand-built 3×3 map with a known tile layout produces the exact expected string (including a door `+`).
## Success Criteria
- [ ] All types exist with the signatures above and derive serde; `Map` carries no rendering data.
- [ ] `Edge.at` is `Option<Point>`.
- [ ] ASCII output matches the documented glyph mapping.
- [ ] Module declarations added to `lib.rs`.
- [ ] All tests pass: `cargo test -p reikhelm-core` (region/map/ascii)
## Commit
`"feat(core): data model + Map output contract + ASCII renderer"`