2025-12-13
Procedural maps in 200 lines

The map generator is in. Fifteen floors, four to six columns wide, every node has a kind (battle, elite, campfire, shop, event, treasure, boss), and every node has typed edges into the next floor. Same seed → same map.
The shape I converged on:
- Sample a count of nodes per floor (3-5).
- For each floor, draw 1-2 edges from each node to the next floor, biased toward nodes near in column.
- Fix orphans by adding a guaranteed edge to the closest node above.
- Force floor 14 (the floor before boss) to funnel into a single boss node.
- Reserve floor 8 for a treasure room, floor 5 and 11 for campfires.
The rules sit in generateMap(seed). It returns a record of MapNode objects. Layout (x, y, edge curves) lives in the React component — a Bezier from each parent to each child, animated with stroke-dashoffset when the path is "active".
What I wish I'd done sooner: write a tiny ASCII renderer for the test suite so I can eyeball a generated map without starting Next. Took me an afternoon but caught two layout bugs in five minutes.
Next is loot. Reward selection is currently "pick a random card from a flat pool." The right answer is rarity buckets with per-floor weights, and rerolling out of cards you already have. Holiday-week project.
End-of-year reflective post coming up.