3.4 KiB
3.4 KiB
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 (Mapcontract), §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 forMap. - Modify:
reikhelm-core/src/lib.rs— appendpub 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; derivesCopy, Eq.type RegionIdas a newtypeRegionId(usize). Invariant:RegionId(n)always equals the indexnof that region inctx.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> }— fieldspub(read across modules by passes and viz).struct Edge { a: RegionId, b: RegionId, at: Option<Point> }— fieldspub(constructed byMstConnect,atwritten byDoorPlacer, read by viz).struct ConnGraph { edges: Vec<Edge> }—new,add_edge(a, b) -> ()(pushesat: None),edges() -> &[Edge],edges_mut() -> &mut [Edge](soDoorPlacercan populateat),neighbors(RegionId) -> impl Iterator<Item = RegionId>.struct Map { tiles: Grid<Tile>, regions: Vec<Region>, graph: ConnGraph, seed: u64, width: u32, height: u32 }— fieldspub(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
Mapserializes to JSON and deserializes back equal (serde round-trip). ConnGraph::add_edgestoresat: None;neighborsreturns both endpoints' counterparts.Map::to_asciion 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;
Mapcarries no rendering data. Edge.atisOption<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"