Pass one seeded generator through the layout builder
Create a seeded pseudo-random generator and pass it into each generation function rather than calling Math.random throughout unrelated helpers. MDN notes that Math.random's initial seed cannot be chosen or reset by the user, so it cannot on its own provide a replayable seed. Store the seed alongside a generated level or its bug report. If you add or reorder random draws later, the same seed may produce a different layout; version the generator or record the generated map when exact long-term replay matters.
Separate choices by purpose when possible: one stream for room shape, another for decoration, and another for enemy placement. Then adding a decorative draw will not automatically shift the enemy pattern.
Generate, validate, then present
After building a room, run structural checks before showing it: entry and exit exist, required keys fit on walkable tiles, and a pathfinder can reach the exit while collecting mandatory items. If the layout fails, retry with a bounded number of new seeds, then use a known fallback room or show a clear generation failure. Log the rejected seed and failed invariant so an unexpected layout can be replayed locally.
Test a small set of deliberately selected seeds, including boundary-like values accepted by your seed parser, plus a sample of ordinary seeds. This is not proof every possible seed is valid. It is a practical way to exercise both deterministic reproduction and the generator's rejection path.
Keep the seed separate from difficulty claims
A reproducible room is not automatically a fair room. Play generated examples and ask whether hazards are visible and whether the intended route leaves enough time to react. Keep that review separate from distribution analysis in a fairness review. When a defect is found, the seed lets the team discuss one exact layout instead of describing an approximate arrangement from memory.
Questions about seeded random level generation
Can I reproduce a level with Math.random and a saved number?
Not by resetting Math.random's built-in state; MDN says its initial seed cannot be chosen or reset. Use a seeded generator under your control and route generation choices through it.
Does a seed guarantee every generated level is solvable?
No. A seed reproduces a choice sequence; it does not validate the resulting map. Check reachability and required-object rules, then reject or repair layouts that fail.