← Back to devlog

2025-12-13

Procedural maps in 200 lines

The branching dungeon map
Fifteen floors, branching every step.

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:

  1. Sample a count of nodes per floor (3-5).
  2. For each floor, draw 1-2 edges from each node to the next floor, biased toward nodes near in column.
  3. Fix orphans by adding a guaranteed edge to the closest node above.
  4. Force floor 14 (the floor before boss) to funnel into a single boss node.
  5. 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.