mirror of
https://github.com/tiennm99/nntv.git
synced 2026-10-16 08:15:26 +00:00
docs(plans): add level redesign brainstorm and 11-phase plan
Fixes L2 unsolvable bug and widens grids across all 12 levels. L12 preserved as unsolvable-by-normal-play (easter egg).
This commit is contained in:
1 parent
692d90cafb
commit
6e2114162f
13 files changed
+772
No files matched your search
@@ -0,0 +1,64 @@
|
||||
# Phase 1: Scrollable Viewport
|
||||
|
||||
## Context
|
||||
- Brainstorm: `plans/reports/brainstorm-260421-2042-level-redesign-solvability.md` §5
|
||||
- Board component: `src/components/GameBoard.svelte` (90 LOC)
|
||||
- Game scene: `src/scenes/Game.svelte` (409 LOC)
|
||||
|
||||
## Overview
|
||||
- **Priority:** CRITICAL — blocks all grid >9x9 redesigns
|
||||
- **Status:** pending
|
||||
- Prereq for Acts 3–6 (grids 10x10–13x13). Desktop-first; no mobile handling.
|
||||
|
||||
## Key Insights
|
||||
- Current rendering assumes full grid fits on screen → unplayable at 12x12+.
|
||||
- Simple solution: CSS scroll container + `scrollIntoView` on player cell each turn.
|
||||
- Svelte 5 runes: `$effect(() => ...)` to react to player position.
|
||||
|
||||
## Requirements
|
||||
- Viewport shows ~9x9 cells. Grid larger than viewport scrolls.
|
||||
- After each player move, scroll so player cell is visible (prefer centered).
|
||||
- Smooth scroll (CSS `scroll-behavior: smooth`).
|
||||
- No layout break at grids ≤8x8 (viewport = grid size, no scroll).
|
||||
|
||||
## Architecture
|
||||
- Wrap `GameBoard.svelte` grid in a scrollable container `<div class="viewport">`.
|
||||
- Fixed viewport size (e.g., `min(80vh, 720px)` square).
|
||||
- Inner grid expands naturally based on rows × cols × cellSize.
|
||||
- `$effect` triggers `cell_element.scrollIntoView({ block: 'center', inline: 'center', behavior: 'smooth' })` on `player.row`/`player.col` change.
|
||||
|
||||
## Related Files
|
||||
- **Modify:** `src/components/GameBoard.svelte` (add viewport wrapper, scroll effect)
|
||||
- **Modify:** `src/styles/theme.css` (viewport CSS vars if needed)
|
||||
- **Read for context:** `src/scenes/Game.svelte`, `src/lib/game/player.js`
|
||||
|
||||
## Implementation Steps
|
||||
1. Add `<div class="viewport">` wrapping the `.grid` element in `GameBoard.svelte`.
|
||||
2. CSS: `.viewport { max-width: 80vh; max-height: 80vh; overflow: auto; scroll-behavior: smooth; }`.
|
||||
3. Bind `ref` to the player's cell element (use Svelte action or id).
|
||||
4. Add `$effect` watching `player.row`, `player.col` → call `scrollIntoView` on the player cell.
|
||||
5. Test with temporary 12x12 dummy level (don't commit dummy level).
|
||||
6. Verify no regression at 6x6, 8x8.
|
||||
7. Run `npm run build` to check compile.
|
||||
|
||||
## Todo List
|
||||
- [ ] Add viewport wrapper + CSS in `GameBoard.svelte`
|
||||
- [ ] Wire player cell ref + `$effect` scrollIntoView
|
||||
- [ ] Manual test: 6x6 (no scroll), 12x12 (scroll follows)
|
||||
- [ ] `npm run build` passes
|
||||
- [ ] `npm run dev` smoke test on L1 + temp 12x12
|
||||
|
||||
## Success Criteria
|
||||
- 12x12 grid fully playable; camera follows player.
|
||||
- 6x6/8x8 grids render identically to before (no scrollbars when grid fits).
|
||||
- Smooth scroll animation on each move.
|
||||
|
||||
## Risk Assessment
|
||||
- **Risk:** `scrollIntoView` with smooth animation may lag on fast moves. **Mitigation:** test with held arrow key; switch to instant scroll if lag visible.
|
||||
- **Risk:** Svelte 5 reactivity on ref may not fire. **Mitigation:** use `bind:this` + derived runes or simple DOM query.
|
||||
|
||||
## Security Considerations
|
||||
N/A — pure UI.
|
||||
|
||||
## Next Steps
|
||||
Phase 2 (solver) can run in parallel with this phase — no file overlap.
|
||||
@@ -0,0 +1,79 @@
|
||||
# Phase 2: Level Solver (BFS)
|
||||
|
||||
## Context
|
||||
- Brainstorm: `plans/reports/brainstorm-260421-2042-level-redesign-solvability.md` §4
|
||||
- Existing engine: `src/lib/game/grid-system.js`, `guards.js`, `turn-manager.js`, `player.js`
|
||||
|
||||
## Overview
|
||||
- **Priority:** CRITICAL — blocks Phase 3 (tests) and all redesigns.
|
||||
- **Status:** pending
|
||||
- Pure-JS BFS solver that replays the real turn simulation to prove reachability.
|
||||
|
||||
## Key Insights
|
||||
- **Must import guard AI from `guards.js`** — do NOT reimplement, or solver disagrees with runtime.
|
||||
- Chaser is stateful → include positions in state key.
|
||||
- Mirror + rotating beams are pure function of rotating direction + mirror positions → not part of state key (recomputed each turn).
|
||||
- Cycle length = `LCM(4 [rotating], 2 [blinking], patrol_path_lengths...)`. Typical 4–24.
|
||||
- L12 expanding-wave mechanic lives in `princess-mechanic.js` — solver must hook in `waveRadius` as state dim.
|
||||
|
||||
## Requirements
|
||||
- Function `solveLevel(levelDef, { maxStates = 10_000_000 } = {})` → `{ solvable: boolean, path?: string[], states_explored: number }`.
|
||||
- `path` is array of actions: `'up' | 'down' | 'left' | 'right' | 'wait'`.
|
||||
- BFS guarantees shortest path.
|
||||
- Hard cap on state count; returns `solvable: false, reason: 'budget_exhausted'` if exceeded.
|
||||
- Returns `solvable: false, reason: 'no_path'` if BFS completes without reaching goal.
|
||||
|
||||
## Architecture
|
||||
|
||||
State key:
|
||||
```
|
||||
key = `${pRow},${pCow}|${chaserPositions.map(c => c.row+','+c.col).join(';')}|${turn % cycle}|${waveRadius ?? 0}`
|
||||
```
|
||||
|
||||
Per step:
|
||||
1. Apply action (move or wait); validate against walls + bounds.
|
||||
2. Run `turnManager.nextTurn()` equivalent on a cloned grid + guards.
|
||||
3. If player cell is lit after guard update → prune.
|
||||
4. If player at goal → return path.
|
||||
5. Enqueue new state if unseen.
|
||||
|
||||
## Related Files
|
||||
- **Create:** `src/lib/game/level-solver.js` (main BFS + state cloning helpers)
|
||||
- **Create:** `src/lib/game/level-solver.test.js` (unit tests for solver itself)
|
||||
- **Read for context:** `src/lib/game/guards.js`, `turn-manager.js`, `grid-system.js`, `princess-mechanic.js`
|
||||
|
||||
## Implementation Steps
|
||||
1. Extract or reuse guard instantiation from `level-manager.js` so solver can build guards from a level def.
|
||||
2. Write `cloneState(grid, guards, player)` — deep copies cells, guard positions, player.
|
||||
3. Write `stateKey(player, guards, turn, cycle, waveRadius)`.
|
||||
4. Write `computeCycle(level)` → LCM of per-guard periods.
|
||||
5. Implement BFS loop with visited Set and state budget.
|
||||
6. Hook expanding-wave: include `waveRadius` in state when level has `isFinalLevel` + princess mechanic.
|
||||
7. Return `{ solvable, path, states_explored, par }`.
|
||||
|
||||
## Todo List
|
||||
- [ ] Create `level-solver.js` scaffolding + exports
|
||||
- [ ] Implement state cloning helpers
|
||||
- [ ] Implement `computeCycle`
|
||||
- [ ] Implement BFS core loop with state budget
|
||||
- [ ] Wire chaser positions into state key
|
||||
- [ ] Wire princess-mechanic `waveRadius` into state key
|
||||
- [ ] Unit tests: toy 3x3 level (trivial solve), 4x4 blocked level (no path), cycle computation
|
||||
- [ ] `npm test` passes
|
||||
- [ ] Solver finishes L1 in <5s; L11 in <60s on dev machine
|
||||
|
||||
## Success Criteria
|
||||
- Solver correctly solves L1 (current) returning a path ≤ par.
|
||||
- Solver returns `solvable: false` for current L2 (matches known bug).
|
||||
- Solver returns `solvable: false` for current L12 (preserves invariant).
|
||||
- Unit tests pass.
|
||||
|
||||
## Risk Assessment
|
||||
- **Risk:** State explosion on 12x12 + 2 chasers. **Mitigation:** profile; if >10M states, tighten cycle (shorter patrol paths) or add heuristic dedup (e.g., ignore chaser when >N cells away).
|
||||
- **Risk:** Chaser simulation non-deterministic (random tiebreak in pathfinding). **Mitigation:** ensure `guards.js` chaser AI is deterministic; if not, make it so (sorted tiebreak).
|
||||
|
||||
## Security Considerations
|
||||
N/A — pure compute in test harness.
|
||||
|
||||
## Next Steps
|
||||
Phase 3 writes the Vitest suite using this solver.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Phase 3: Solvability Test Suite + CI Wiring
|
||||
|
||||
## Context
|
||||
- Brainstorm §4.2
|
||||
- Depends on: Phase 2 (solver)
|
||||
|
||||
## Overview
|
||||
- **Priority:** HIGH — gate for all redesign phases.
|
||||
- **Status:** pending
|
||||
- Vitest suite that asserts invariants on every PR touching `levels.js`.
|
||||
|
||||
## Requirements
|
||||
- For L1, L3–L11: assert `solveLevel(level).solvable === true`.
|
||||
- For L2: known fail (baseline); test marked `.skip` with TODO to unskip after Phase 4.
|
||||
- For L12: assert `solveLevel(level).solvable === false` (unsolvable invariant).
|
||||
- Each passing level also asserts `par = path.length + 2` (or logs mismatch for manual par tuning).
|
||||
|
||||
## Architecture
|
||||
- One describe block per level; `test.each(LEVELS)` pattern.
|
||||
- Long-running: set Vitest timeout to 120s per test.
|
||||
- Separate suite file so dev can skip fast unit tests vs full solvability run.
|
||||
|
||||
## Related Files
|
||||
- **Create:** `src/lib/levels/levels.solvability.test.js`
|
||||
- **Modify:** `package.json` — add script `"test:solvability": "vitest run src/lib/levels/levels.solvability.test.js"`
|
||||
- **Modify:** `.github/workflows/*.yml` if CI exists (else note for later)
|
||||
|
||||
## Implementation Steps
|
||||
1. Create test file, import `LEVELS` and `solveLevel`.
|
||||
2. Loop levels, assert expected solvability per hard rules.
|
||||
3. Add par validation (warn only, don't fail yet — par tuning happens in redesign phases).
|
||||
4. Add `test.each` table for grid size validation (L1-2 = 8x8, L3-4 = 9x9, etc.) — initially all fail; phase-by-phase unskips.
|
||||
5. Add npm script.
|
||||
6. If `.github/workflows` exists, add step: `npm run test:solvability`.
|
||||
|
||||
## Todo List
|
||||
- [ ] Write solvability test file
|
||||
- [ ] Skip L2 initially with TODO
|
||||
- [ ] Add par-delta warning (console.warn, not fail)
|
||||
- [ ] Add grid-size table (initially all skipped; unskip per phase)
|
||||
- [ ] Add npm script
|
||||
- [ ] CI integration (if CI present)
|
||||
- [ ] Confirm local: L1/3-11/12 assertions pass
|
||||
|
||||
## Success Criteria
|
||||
- `npm run test:solvability` passes with L2 skipped and L12 asserted-unsolvable.
|
||||
- CI (if present) runs solvability on PRs.
|
||||
|
||||
## Risk Assessment
|
||||
- **Risk:** Solver too slow for CI. **Mitigation:** run solvability only on PRs that touch `levels.js` or `level-solver.js` (path-filter in CI).
|
||||
|
||||
## Next Steps
|
||||
Phase 4 redesigns L1-L2. After L2 passes, unskip its assertion here.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Phase 4: Act 1 Redesign (L1–L2, 8x8)
|
||||
|
||||
## Context
|
||||
- Brainstorm §3.1, §6 (migration)
|
||||
- Depends on: Phase 3 (tests)
|
||||
|
||||
## Overview
|
||||
- **Priority:** HIGH (fixes L2 unsolvability bug).
|
||||
- **Status:** pending
|
||||
- Act 1 teaches movement + static guards.
|
||||
|
||||
## Requirements
|
||||
- Both levels: 8x8 grid, preserved `id`/`name`/`storyKey`.
|
||||
- **L1 "Garden Path":** 0 guards (gentle intro, pure movement/walls); par ~12.
|
||||
- **L2 "The Watchtower":** 3 static guards, ≥2 viable paths; par ~16.
|
||||
- L2 must be SOLVABLE this time.
|
||||
|
||||
## Implementation Steps
|
||||
1. Sketch L1 8x8 grid: start (0,0), goal (7,7), ~10 walls forming winding but connected path.
|
||||
2. Run solver on L1 → assert `solvable`; set par = path.length + 2.
|
||||
3. Sketch L2 8x8 grid: 3 static guards placed so ≥2 distinct paths exist; walls widen the arena.
|
||||
4. Run solver on L2 → assert solvable; set par = path.length + 2.
|
||||
5. Update `src/lib/levels/levels.js` — replace L1, L2 entries in place.
|
||||
6. Unskip L2 assertion in `levels.solvability.test.js`.
|
||||
7. Run `npm run test:solvability` → both pass.
|
||||
8. Manual playthrough via `npm run dev`.
|
||||
|
||||
## Related Files
|
||||
- **Modify:** `src/lib/levels/levels.js` (L1, L2 entries only)
|
||||
- **Modify:** `src/lib/levels/levels.solvability.test.js` (unskip L2, enable L1-2 grid-size check)
|
||||
|
||||
## Todo List
|
||||
- [ ] Draft L1 layout, verify with solver
|
||||
- [ ] Draft L2 layout with 3 guards + 2 paths, verify with solver
|
||||
- [ ] Replace entries in `levels.js`
|
||||
- [ ] Unskip L2 test
|
||||
- [ ] Solvability suite green
|
||||
- [ ] Manual playthrough L1 + L2 via `npm run dev`
|
||||
|
||||
## Success Criteria
|
||||
- L1 and L2 solvable per solver.
|
||||
- L2 has ≥2 distinct solutions (verify by running solver with different seeds or via manual BFS count).
|
||||
- Par values match `solver_optimal + 2`.
|
||||
|
||||
## Risks
|
||||
- **Overlapping wall+light** (original L2 bug). **Mitigation:** lint via assertion in solver — no wall cell should appear in any guard's `litCells` array.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Phase 5: Act 2 Redesign (L3–L4, 9x9)
|
||||
|
||||
## Context
|
||||
- Brainstorm §3.1
|
||||
- Depends on: Phase 4 (Act 1 done; solver + tests trusted)
|
||||
|
||||
## Overview
|
||||
- **Priority:** MEDIUM
|
||||
- **Status:** pending
|
||||
- Act 2 introduces **rotating** + **blinking** (blinking promoted from old Act 3).
|
||||
|
||||
## Requirements
|
||||
- Both levels: 9x9 grid, preserved metadata.
|
||||
- **L3 "Vegetable Patrol":** 4 guards total — mix of static + rotating (≥1 rotating); par ~18.
|
||||
- **L4 "The Searchlight":** 5 guards — static + rotating + 1 blinking; timing window required; par ~22.
|
||||
|
||||
## Implementation Steps
|
||||
1. Draft L3 9x9 with 4 guards; verify solver solvable + ≥2 paths.
|
||||
2. Draft L4 9x9 with 5 guards including blinking (introduce teaching level); verify solvable; ensure `wait` action is useful (solver path includes at least one `wait`).
|
||||
3. Replace L3, L4 in `levels.js`.
|
||||
4. Enable L3-4 grid-size assertion in test.
|
||||
5. `npm run test:solvability` green.
|
||||
6. Manual playthrough.
|
||||
|
||||
## Related Files
|
||||
- **Modify:** `src/lib/levels/levels.js` (L3, L4)
|
||||
- **Modify:** `src/lib/levels/levels.solvability.test.js`
|
||||
|
||||
## Todo List
|
||||
- [ ] L3 drafted + solver-verified
|
||||
- [ ] L4 drafted + solver-verified (blinking intro)
|
||||
- [ ] Entries replaced
|
||||
- [ ] Tests green
|
||||
- [ ] Manual playthrough
|
||||
|
||||
## Success Criteria
|
||||
- Solver proves both solvable with pars reasonable.
|
||||
- L4 solution requires a `wait` action (teaches mechanic).
|
||||
|
||||
## Risks
|
||||
- **Rotating guard cycle + blinking cycle interaction** may create tight windows. **Mitigation:** solver par check; if par >30, simplify.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Phase 6: Act 3 Redesign (L5–L6, 10x10)
|
||||
|
||||
## Context
|
||||
- Brainstorm §3.1
|
||||
- Depends on: Phase 1 (viewport for 10x10), Phase 5
|
||||
|
||||
## Overview
|
||||
- **Priority:** MEDIUM
|
||||
- **Status:** pending
|
||||
- Act 3 introduces **patrolling** (promoted from old Act 4).
|
||||
|
||||
## Requirements
|
||||
- Both levels: 10x10 grid, preserved metadata.
|
||||
- **L5 "Fortress Gate":** 6 guards — static + rotating + blinking + 1 patrolling (intro); par ~22.
|
||||
- **L6 "The Flickering Corridor":** 7 guards — mix, ≥2 patrollers OR 1 patroller with longer path; par ~28.
|
||||
|
||||
## Implementation Steps
|
||||
1. Draft L5 10x10 with 6 guards (1 patrolling); solver-verify.
|
||||
2. Draft L6 10x10 with 7 guards; solver-verify.
|
||||
3. Replace in `levels.js`.
|
||||
4. Enable 10x10 grid-size checks.
|
||||
5. Test green.
|
||||
6. Manual playthrough — verify viewport scrolls correctly (phase-1 integration smoke).
|
||||
|
||||
## Related Files
|
||||
- **Modify:** `src/lib/levels/levels.js` (L5, L6)
|
||||
- **Modify:** `src/lib/levels/levels.solvability.test.js`
|
||||
|
||||
## Todo List
|
||||
- [ ] L5 drafted + verified (patrolling intro)
|
||||
- [ ] L6 drafted + verified
|
||||
- [ ] Entries replaced
|
||||
- [ ] Tests green
|
||||
- [ ] Manual playthrough confirms viewport scroll works
|
||||
|
||||
## Success Criteria
|
||||
- Both solvable; ≥2 paths per level.
|
||||
- Patroller path length ≥4 cells (meaningful patrol).
|
||||
|
||||
## Risks
|
||||
- **Patrol path length inflates cycle** → solver state blowup. **Mitigation:** cap patrol path ≤6 cells in Act 3.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Phase 7: Act 4 Redesign (L7–L8, 11x11)
|
||||
|
||||
## Context
|
||||
- Brainstorm §3.1
|
||||
- Depends on: Phase 1 (viewport), Phase 6
|
||||
|
||||
## Overview
|
||||
- **Priority:** MEDIUM
|
||||
- **Status:** pending
|
||||
- Act 4 introduces **mirror** guards (promoted from old Act 5).
|
||||
|
||||
## Requirements
|
||||
- Both levels: 11x11 grid, preserved metadata.
|
||||
- **L7 "The Underground Passage":** 7 guards — mix + 1 mirror (intro); par ~26.
|
||||
- **L8 "The Gauntlet":** 8 guards — ≥2 mirrors + ≥2 patrolling + rotating; par ~32.
|
||||
|
||||
## Implementation Steps
|
||||
1. Draft L7 11x11 with mirror intro; solver-verify (requires solver to correctly model mirror deflection).
|
||||
2. Draft L8 11x11 with complex mirror setup; solver-verify.
|
||||
3. Replace in `levels.js`.
|
||||
4. Tests green.
|
||||
5. Manual playthrough.
|
||||
|
||||
## Related Files
|
||||
- **Modify:** `src/lib/levels/levels.js` (L7, L8)
|
||||
- **Modify:** `src/lib/levels/levels.solvability.test.js`
|
||||
|
||||
## Todo List
|
||||
- [ ] L7 drafted (mirror intro) + verified
|
||||
- [ ] L8 drafted + verified
|
||||
- [ ] Entries replaced
|
||||
- [ ] Tests green
|
||||
- [ ] Manual playthrough
|
||||
|
||||
## Success Criteria
|
||||
- Both solvable; mirror deflection creates a hazard that player must account for.
|
||||
- L8 has ≥3 guards of same dynamic type (patrol/rotating) — "gauntlet" flavor.
|
||||
|
||||
## Risks
|
||||
- **Solver mirror logic mismatch with runtime.** **Mitigation:** Phase 2 solver imports guards.js — single source of truth.
|
||||
- **11x11 + mirrors + many guards → state blowup.** **Mitigation:** profile; may need state cap bump or heuristic pruning.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Phase 8: Act 5 Redesign (L9–L10, 12x12)
|
||||
|
||||
## Context
|
||||
- Brainstorm §3.1
|
||||
- Depends on: Phase 1 (viewport), Phase 7
|
||||
|
||||
## Overview
|
||||
- **Priority:** MEDIUM
|
||||
- **Status:** pending
|
||||
- Act 5 introduces **chaser** guards.
|
||||
|
||||
## Requirements
|
||||
- Both levels: 12x12 grid, preserved metadata.
|
||||
- **L9 "The Decoy Path":** 8 guards — mix + 1 chaser (intro); par ~34.
|
||||
- **L10 "Hall of Mirrors":** 9 guards — chaser + multiple mirrors + rotating; par ~38.
|
||||
|
||||
## Implementation Steps
|
||||
1. Draft L9 12x12 with chaser intro; solver-verify (longest run; may take minutes).
|
||||
2. Draft L10 12x12 with mirror-heavy layout + chaser; solver-verify.
|
||||
3. Replace in `levels.js`.
|
||||
4. Tests green.
|
||||
5. Manual playthrough — verify viewport + camera follow smooth at 12x12.
|
||||
|
||||
## Related Files
|
||||
- **Modify:** `src/lib/levels/levels.js` (L9, L10)
|
||||
- **Modify:** `src/lib/levels/levels.solvability.test.js`
|
||||
|
||||
## Todo List
|
||||
- [ ] L9 drafted (chaser intro) + verified
|
||||
- [ ] L10 drafted + verified
|
||||
- [ ] Entries replaced
|
||||
- [ ] Tests green
|
||||
- [ ] Manual playthrough at 12x12
|
||||
|
||||
## Success Criteria
|
||||
- Both solvable; chaser creates genuine threat (solver proves shortest path avoids chaser).
|
||||
- Solver completes within 10min per level (or raise state cap).
|
||||
|
||||
## Risks
|
||||
- **Solver budget exhausted on 12x12 + chaser + long cycle.** **Mitigation:** shorten patrol paths; cap chaser detection radius at 3.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Phase 9: L11 Redesign — Throne Room (12x12)
|
||||
|
||||
## Context
|
||||
- Brainstorm §3.1
|
||||
- Depends on: Phase 1 (viewport), Phase 8
|
||||
|
||||
## Overview
|
||||
- **Priority:** MEDIUM
|
||||
- **Status:** pending
|
||||
- L11 is the climax before the impossible finale. All guard types combined.
|
||||
|
||||
## Requirements
|
||||
- 12x12 grid, preserved metadata (`id: 11`, `name: "The Throne Room"`, `storyKey: level11Story`).
|
||||
- 9 guards: ≥1 of each type (static, rotating, blinking, patrolling, mirror, chaser).
|
||||
- Must be solvable but hard (par ~42).
|
||||
- Multiple paths; each path tests a different mix of skills learned.
|
||||
|
||||
## Implementation Steps
|
||||
1. Draft 12x12 with all 6 guard types represented; solver-verify.
|
||||
2. Ensure par > L10 par (difficulty climb).
|
||||
3. Replace L11 in `levels.js`.
|
||||
4. Tests green.
|
||||
5. Manual playthrough — note difficulty feel; adjust if solver par drastically exceeds 42.
|
||||
|
||||
## Related Files
|
||||
- **Modify:** `src/lib/levels/levels.js` (L11)
|
||||
- **Modify:** `src/lib/levels/levels.solvability.test.js`
|
||||
|
||||
## Todo List
|
||||
- [ ] L11 drafted with all 6 guard types
|
||||
- [ ] Solver-verified
|
||||
- [ ] Par sensible (≤~50)
|
||||
- [ ] Tests green
|
||||
- [ ] Manual playthrough
|
||||
|
||||
## Success Criteria
|
||||
- Solvable.
|
||||
- Uses every guard type at least once.
|
||||
- Feels like a climactic combo of all mechanics.
|
||||
|
||||
## Risks
|
||||
- **Mirror + chaser + rotating interaction too chaotic.** **Mitigation:** if solver par >50 or feels unfair, reduce to 8 guards (drop one redundant type).
|
||||
@@ -0,0 +1,66 @@
|
||||
# Phase 10: L12 Redesign — Princess Chamber + Console Easter Egg (13x13)
|
||||
|
||||
## Context
|
||||
- Brainstorm §6, §10
|
||||
- Memory: `project_level12_unsolvable.md`
|
||||
- Depends on: Phase 9
|
||||
|
||||
## Overview
|
||||
- **Priority:** HIGH (preserves core narrative invariant).
|
||||
- **Status:** pending
|
||||
- L12 MUST remain unsolvable by normal play. Only console-based teleport can trigger the win screen.
|
||||
|
||||
## Hard Invariant
|
||||
Solver MUST return `solvable: false` for L12 after redesign. Test asserts this; CI blocks any change that makes L12 solvable.
|
||||
|
||||
## Requirements
|
||||
- 13x13 grid (biggest), preserved metadata (`id: 12`, `isFinalLevel: true`).
|
||||
- 10 guards + expanding-wave mechanic from `princess-mechanic.js`.
|
||||
- Console easter-egg hook exposed on `window` so dev-tools users can teleport to goal.
|
||||
- Normal win flow triggers regardless of how goal was reached.
|
||||
|
||||
## Architecture
|
||||
- **Guards:** all 6 types represented, packed aggressively around goal.
|
||||
- **Expanding wave:** intact from current impl — lights ring around goal after player within distance 4.
|
||||
- **Console hook:** expose `window.__nntvDev = { teleport(row, col) { ... }, reveal() { ... } }`.
|
||||
- `teleport` mutates the live game state via existing reactive store.
|
||||
- Documented in code comments only; NOT in README, NOT in guide UI.
|
||||
- **Win flow unchanged:** existing `isAtGoal()` check + `LevelCompletePopup.svelte` fire normally when player position = goal.
|
||||
|
||||
## Implementation Steps
|
||||
1. Draft 13x13 layout with 10 guards + wave mechanic.
|
||||
2. Solver-verify `solvable: false` within 10M state budget.
|
||||
3. Replace L12 in `levels.js`.
|
||||
4. Add `window.__nntvDev` hook in `src/main.js` (or `Game.svelte`) — gated behind Svelte store reference.
|
||||
5. Manual test:
|
||||
- Normal play → impossible confirmed.
|
||||
- `window.__nntvDev.teleport(goal.row, goal.col)` in console → win flow triggers.
|
||||
6. Add test in `levels.solvability.test.js`: assert L12 unsolvable.
|
||||
7. Manual playthrough.
|
||||
|
||||
## Related Files
|
||||
- **Modify:** `src/lib/levels/levels.js` (L12)
|
||||
- **Modify:** `src/main.js` or `src/scenes/Game.svelte` (expose dev hook)
|
||||
- **Modify:** `src/lib/levels/levels.solvability.test.js`
|
||||
- **Read:** `src/lib/game/princess-mechanic.js`
|
||||
|
||||
## Todo List
|
||||
- [ ] L12 drafted (13x13, 10 guards + wave)
|
||||
- [ ] Solver confirms unsolvable
|
||||
- [ ] Dev hook (`window.__nntvDev.teleport`) wired
|
||||
- [ ] Manual: normal play impossible, console win works
|
||||
- [ ] Solvability test asserts `!solvable`
|
||||
- [ ] Code comment documents easter egg
|
||||
|
||||
## Success Criteria
|
||||
- Solver asserts `solvable: false` — test green.
|
||||
- Pressing every direction + wait indefinitely never reaches goal (manual spot-check).
|
||||
- `window.__nntvDev.teleport(12, 12)` from console → LevelCompletePopup fires.
|
||||
- No in-game UI/readme hint about the easter egg.
|
||||
|
||||
## Risks
|
||||
- **Dev hook security.** **Mitigation:** non-sensitive (just game state); no auth/data exposure. Fine for a client-only game.
|
||||
- **Wave mechanic solvability.** **Mitigation:** solver includes `waveRadius` in state key (Phase 2).
|
||||
|
||||
## Security Considerations
|
||||
- `window.__nntvDev` is intentionally world-accessible in-browser. Acceptable — no server, no user data.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Phase 11: Docs + Full Smoke Test
|
||||
|
||||
## Context
|
||||
- Brainstorm §11
|
||||
- Depends on: all prior phases
|
||||
|
||||
## Overview
|
||||
- **Priority:** LOW–MEDIUM (final polish, CI gate).
|
||||
- **Status:** pending
|
||||
- Update `docs/`, verify full playthrough, tune pars if needed.
|
||||
|
||||
## Requirements
|
||||
- Update `docs/system-architecture.md` — add solver + viewport sections.
|
||||
- Update `docs/code-standards.md` — note "all level edits must run solvability suite".
|
||||
- README unchanged re: easter egg (keep hidden).
|
||||
- Full manual playthrough L1→L11; verify L12 is impossible but console-teleport wins.
|
||||
|
||||
## Implementation Steps
|
||||
1. Run full test suite: `npm test && npm run test:solvability`.
|
||||
2. Check `docs/` files exist — if not, create.
|
||||
3. Append solver architecture section to `docs/system-architecture.md`.
|
||||
4. Append level-authoring rule to `docs/code-standards.md`: "never commit `levels.js` without running `npm run test:solvability`".
|
||||
5. Add codebase-summary.md entry for `level-solver.js`.
|
||||
6. Manual playthrough L1 → L11 (screenshot key moments for regression visibility).
|
||||
7. Par tuning pass: if solver_par + 2 feels too loose/tight per actual play, adjust.
|
||||
8. `npm run build` produces clean dist.
|
||||
|
||||
## Related Files
|
||||
- **Modify:** `docs/system-architecture.md`
|
||||
- **Modify:** `docs/code-standards.md`
|
||||
- **Modify:** `docs/codebase-summary.md` (if exists)
|
||||
- **Modify (maybe):** `src/lib/levels/levels.js` (par tuning)
|
||||
|
||||
## Todo List
|
||||
- [ ] Full test suite green
|
||||
- [ ] docs/system-architecture.md updated
|
||||
- [ ] docs/code-standards.md updated
|
||||
- [ ] Manual playthrough L1→L11 complete, no blockers
|
||||
- [ ] L12 console win confirmed
|
||||
- [ ] Par tuning (if any) complete
|
||||
- [ ] `npm run build` clean
|
||||
|
||||
## Success Criteria
|
||||
- All 11 solvable levels playable end-to-end without test failures.
|
||||
- L12 impossible by normal play; console win works.
|
||||
- Docs reflect new solver + viewport architecture.
|
||||
|
||||
## Risks
|
||||
- **Par values feel off in real play.** **Mitigation:** dedicated tuning pass here; adjust before merge.
|
||||
|
||||
## Next Steps
|
||||
- Archive plan via `/ck:plan archive`.
|
||||
- Merge to main, tag release if versioned.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: Level Redesign & Solvability
|
||||
slug: level-redesign-solvability
|
||||
created: 2026-04-21
|
||||
status: pending
|
||||
blockedBy: []
|
||||
blocks: []
|
||||
source: plans/reports/brainstorm-260421-2042-level-redesign-solvability.md
|
||||
---
|
||||
|
||||
# Level Redesign & Solvability — Implementation Plan
|
||||
|
||||
Redesign all 12 levels of Night Ninja: Twilight Voyage. Fix L2 unsolvability. Widen grids (8x8→13x13). Add more guards per act. Introduce automated BFS solver + CI-enforced solvability tests. Preserve L12 as unsolvable (console easter-egg win only).
|
||||
|
||||
## Hard Rules
|
||||
|
||||
1. Exactly 12 levels, 6 acts. No adds/removes/renumbers.
|
||||
2. L12 must remain unsolvable by normal play.
|
||||
3. Desktop-first. Large grids via scrollable viewport that follows player.
|
||||
4. Preserve `id`, `name`, `storyKey`, `isFinalLevel` per level (no progress migration).
|
||||
|
||||
## Progression Target
|
||||
|
||||
| Act | Levels | Grid | Guard types (cumulative) | Guard count |
|
||||
|-----|--------|------|--------------------------|-------------|
|
||||
| 1 | 1–2 | 8x8 | static | 0 / 3 |
|
||||
| 2 | 3–4 | 9x9 | + rotating + blinking | 4 / 5 |
|
||||
| 3 | 5–6 | 10x10 | + patrolling | 6 / 7 |
|
||||
| 4 | 7–8 | 11x11 | + mirror | 7 / 8 |
|
||||
| 5 | 9–10 | 12x12 | + chaser | 8 / 9 |
|
||||
| 6 | 11 | 12x12 | all | 9 |
|
||||
| 6 | 12 | 13x13 | all + expanding wave | 10 (unsolvable) |
|
||||
|
||||
## Phases
|
||||
|
||||
| # | File | Status | Description |
|
||||
|---|------|--------|-------------|
|
||||
| 1 | [phase-01-scrollable-viewport.md](phase-01-scrollable-viewport.md) | pending | Camera-follow viewport for grids >9x9 |
|
||||
| 2 | [phase-02-level-solver.md](phase-02-level-solver.md) | pending | BFS solver in `src/lib/game/level-solver.js` |
|
||||
| 3 | [phase-03-solvability-tests.md](phase-03-solvability-tests.md) | pending | Vitest suite + CI wiring; assert L2 fails as baseline |
|
||||
| 4 | [phase-04-redesign-act1-l1-l2.md](phase-04-redesign-act1-l1-l2.md) | pending | Act 1 (L1–L2, 8x8, static) |
|
||||
| 5 | [phase-05-redesign-act2-l3-l4.md](phase-05-redesign-act2-l3-l4.md) | pending | Act 2 (L3–L4, 9x9, + rotating + blinking) |
|
||||
| 6 | [phase-06-redesign-act3-l5-l6.md](phase-06-redesign-act3-l5-l6.md) | pending | Act 3 (L5–L6, 10x10, + patrolling) |
|
||||
| 7 | [phase-07-redesign-act4-l7-l8.md](phase-07-redesign-act4-l7-l8.md) | pending | Act 4 (L7–L8, 11x11, + mirror) |
|
||||
| 8 | [phase-08-redesign-act5-l9-l10.md](phase-08-redesign-act5-l9-l10.md) | pending | Act 5 (L9–L10, 12x12, + chaser) |
|
||||
| 9 | [phase-09-redesign-l11.md](phase-09-redesign-l11.md) | pending | L11 Throne Room (12x12, all guards) |
|
||||
| 10 | [phase-10-redesign-l12-easter-egg.md](phase-10-redesign-l12-easter-egg.md) | pending | L12 Princess Chamber (13x13, unsolvable) + console win hook |
|
||||
| 11 | [phase-11-docs-and-smoke-test.md](phase-11-docs-and-smoke-test.md) | pending | Update docs/ + manual playthrough QA |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Phase 1 (viewport) blocks Phases 6–10 (grids ≥10x10 unplayable without it).
|
||||
- Phase 2 (solver) blocks Phase 3 (tests need solver).
|
||||
- Phase 3 (tests) blocks Phases 4–10 (each redesign validated by tests).
|
||||
- Phases 4–9 independent of each other but best done in order (progression curve).
|
||||
- Phase 10 depends on Phase 9 (L11 finalized before L12).
|
||||
- Phase 11 depends on all previous.
|
||||
|
||||
## Source
|
||||
|
||||
Brainstorm summary: `plans/reports/brainstorm-260421-2042-level-redesign-solvability.md`
|
||||
@@ -0,0 +1,145 @@
|
||||
# Level Redesign & Solvability — Brainstorm Summary
|
||||
|
||||
**Date:** 2026-04-21
|
||||
**Scope:** Redesign all 12 levels of Night Ninja: Twilight Voyage. Fix L2 unsolvability. Expand grid sizes. Add more guards. Preserve L12 as intentionally unsolvable.
|
||||
**Status:** Design approved — ready for implementation plan.
|
||||
|
||||
---
|
||||
|
||||
## 1. Problem Statement
|
||||
|
||||
- **L2 unsolvable** — BFS from `(0,0)` cannot reach `(5,5)`. Right-side component `{(3,5),(4,4),(4,5),(5,3),(5,4),(5,5)}` is disconnected by permanent light walls from static guards at `(2,4)` and `(4,2)`.
|
||||
- **No solvability gate** — levels authored manually; no automated proof. Risk of regressions on future edits.
|
||||
- **Levels feel narrow** — current grids 6x6–9x9 have single corridors; user wants wider, more complex puzzles.
|
||||
- **Progression imbalanced** — mirror/chaser/patrolling introduced too late (Acts 4–5); early acts feel sparse.
|
||||
|
||||
## 2. Hard Rules (invariants)
|
||||
|
||||
1. **Exactly 12 levels, 6 acts.** No adds, removes, renumbers.
|
||||
2. **L12 must remain unsolvable by normal play.** Only console easter-egg win (user teleports player to goal via dev tools). Win condition / congratulations screen still fires when goal reached by any means.
|
||||
3. **Desktop-first.** No mobile responsive work. Large grids handled via scrollable viewport that follows the player.
|
||||
4. **Preserve** `id`, `name`, `storyKey`, `isFinalLevel` per level — zero progress-persistence migration.
|
||||
|
||||
## 3. Solution — Redesign Philosophy
|
||||
|
||||
### 3.1 Progression (rebalanced acts)
|
||||
|
||||
| Act | Levels | Grid | Guard types (cumulative) | Guard count |
|
||||
|-----|--------|------|--------------------------|-------------|
|
||||
| 1 | 1–2 | 8x8 | static | 0 / 3 |
|
||||
| 2 | 3–4 | 9x9 | + rotating + **blinking** (promoted from Act 3) | 4 / 5 |
|
||||
| 3 | 5–6 | 10x10 | + **patrolling** (promoted from Act 4) | 6 / 7 |
|
||||
| 4 | 7–8 | 11x11 | + **mirror** (promoted from Act 5) | 7 / 8 |
|
||||
| 5 | 9–10 | 12x12 | + chaser | 8 / 9 |
|
||||
| 6 | 11 | 12x12 | all types combined | 9 |
|
||||
| 6 | 12 | 13x13 | all types + expanding-wave mechanic | 10 (unsolvable) |
|
||||
|
||||
### 3.2 Design Rules Per Level
|
||||
|
||||
- **Connectivity gate:** at least one reachable path from start→goal in solver (except L12).
|
||||
- **Widen, don't narrow:** add 2–3 alternative routes so guards force *decisions*, not single chokepoints.
|
||||
- **More guards ≠ more walls.** Keep wall density ~20–25% of grid area; increase guard count per act tier.
|
||||
- **Par = solver_optimal + 2** (small slack for undo experimentation).
|
||||
- **No redundant wall-on-light** (L2 had wall `(3,4)` over guard light `(3,4)` — sloppy overlap, remove).
|
||||
- **Guard beam pruning:** rotating beams stop at walls — use walls strategically to create safe windows.
|
||||
|
||||
## 4. Solvability Solver (automated validation)
|
||||
|
||||
### 4.1 State & Search
|
||||
|
||||
**State key:** `(player_row, player_col, chaser_positions_tuple, turn_mod_cycle)`
|
||||
- `cycle = LCM(4 [rotating], 2 [blinking], patrol_path_length_i...)`. Typical cycles 4–24.
|
||||
- **Chaser** is stateful — include each chaser's position in state key. State space stays tractable (<10M for 12x12 + 2 chasers + cycle 24).
|
||||
- **Mirror beams** are pure function of rotating-guard directions + mirror positions each turn — not part of state.
|
||||
|
||||
**Transitions:** 5 actions per turn — `up / down / left / right / wait`.
|
||||
**Pruning:** after applying action + running guard update for next turn, if player cell `isLight` → dead branch.
|
||||
**Algorithm:** BFS (guarantees shortest). Bail out at state-count cap (~10M) with "unsolvable within budget" → manual check.
|
||||
|
||||
### 4.2 Location & Modules
|
||||
|
||||
- `src/lib/game/level-solver.js` — pure BFS solver, reuses existing `grid-system.js`, `guards.js`, `turn-manager.js`.
|
||||
- `src/lib/levels/levels.solvability.test.js` — Vitest suite asserting:
|
||||
- For L1–L11: solver returns a valid path.
|
||||
- For L12: solver returns no path within budget (unsolvable invariant).
|
||||
- Par values match `solver_optimal + 2`.
|
||||
- CI hook: run solvability suite on every PR that touches `levels.js`.
|
||||
|
||||
### 4.3 Known Limitations
|
||||
|
||||
- Chaser path depends on guard AI implementation — solver must use *identical* chaser logic as runtime (import from `guards.js`, don't reimplement).
|
||||
- Cycle computation must account for patrol path length (may be odd / prime).
|
||||
- L12's expanding-wave mechanic needs the solver to model princess-mechanic.js state — include `waveRadius` in state key.
|
||||
|
||||
## 5. Scrollable Viewport (prereq for large grids)
|
||||
|
||||
- Current rendering assumes full grid fits on screen.
|
||||
- Need: camera-follow the player so 12x12 / 13x13 grids are playable on 1080p screens.
|
||||
- Implementation touch points:
|
||||
- `src/components/GameBoard.svelte` (or equivalent) — CSS `overflow: auto` + `scroll-behavior: smooth` + programmatic `scrollIntoView` on player position change.
|
||||
- Viewport: show ~9x9 cells visible; pan on player move.
|
||||
- Out-of-scope for this redesign brainstorm but **blocker** for Acts 4–6 playability.
|
||||
|
||||
## 6. L12 Easter Egg — Console Win
|
||||
|
||||
- Expose a minimal dev hook on `window` (e.g., `window.__nntvDev = { teleportToGoal() { ... } }`) OR rely on existing game-state reactivity so user can directly mutate player position via devtools.
|
||||
- Reaching goal by any means → normal win flow (congratulations, progress saved).
|
||||
- Document the easter egg ONLY in code comments, not in-game UI or README.
|
||||
|
||||
## 7. Migration & Rollout
|
||||
|
||||
- **In-place rewrite** of `src/lib/levels/levels.js`. No new file.
|
||||
- Preserve each level's `id`, `name`, `storyKey`, `isFinalLevel`, array position. No changes to `src/lib/localization/*` story keys needed.
|
||||
- Update `parMoves` from solver output for all levels.
|
||||
- Tests first: land solver + solvability suite **before** level content changes, so each new level design is validated as it's written.
|
||||
|
||||
## 8. Evaluated Alternatives
|
||||
|
||||
| Approach | Pros | Cons | Verdict |
|
||||
|----------|------|------|---------|
|
||||
| Fix L2 only, leave others | Minimal churn | Doesn't address "narrow levels" complaint; no regression protection | **Rejected** |
|
||||
| Keep grids, add guards only | No viewport work | Crowded; still narrow corridors; low decision value | **Rejected** |
|
||||
| Aggressive grid expansion + more guards + solver | Solves all issues; prevents future regressions | Requires viewport work; solver effort | **Chosen** |
|
||||
| Expand to 15–18 levels | Richer progression | Breaks progress persistence, story keys, count invariant | **Rejected** (violates hard rule) |
|
||||
|
||||
## 9. Success Criteria
|
||||
|
||||
- [ ] Vitest suite: 11/11 levels solvable, L12 unsolvable. Passes on CI.
|
||||
- [ ] Every level has ≥2 distinct BFS-discovered paths OR clear single-path justification in code comment.
|
||||
- [ ] All acts visually wider than before (row/col counts match Section 3.1).
|
||||
- [ ] Scrollable viewport works on 1080p / 1440p screens for 12x12 grids.
|
||||
- [ ] Win condition fires when L12 goal is reached via console manipulation.
|
||||
- [ ] No changes to story keys, level IDs, or progress-persistence schema.
|
||||
|
||||
## 10. Next Steps (Implementation Plan Phases)
|
||||
|
||||
1. **Phase 1:** Implement scrollable viewport (prereq for >9x9 grids).
|
||||
2. **Phase 2:** Build `level-solver.js` + unit tests on current L1, L3–L11 (should solve) and L12 (should fail). Accept that L2 will fail — that's the baseline bug.
|
||||
3. **Phase 3:** Solvability test suite — wire into CI.
|
||||
4. **Phase 4:** Redesign L1–L2 (Act 1, 8x8). Run solver, lock par values.
|
||||
5. **Phase 5:** Redesign L3–L4 (Act 2, 9x9, introduce blinking earlier).
|
||||
6. **Phase 6:** Redesign L5–L6 (Act 3, 10x10, introduce patrolling).
|
||||
7. **Phase 7:** Redesign L7–L8 (Act 4, 11x11, introduce mirror).
|
||||
8. **Phase 8:** Redesign L9–L10 (Act 5, 12x12, introduce chaser).
|
||||
9. **Phase 9:** Redesign L11 (Act 6, 12x12, all guards combined).
|
||||
10. **Phase 10:** Redesign L12 (Act 6, 13x13, preserve unsolvable invariant; expose console win hook).
|
||||
11. **Phase 11:** Update `docs/` (code-standards, system-architecture) with solver + easter-egg notes. Smoke-test full playthrough.
|
||||
|
||||
## 11. Risks
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Solver too slow on chaser+long-cycle levels | State cap 10M; if exceeded, reduce cycle via shorter patrol paths |
|
||||
| Viewport work reveals rendering bugs | Isolate in Phase 1; land before level changes |
|
||||
| Solver disagrees with runtime (chaser logic) | Import guard logic from `guards.js` — single source of truth |
|
||||
| L12 accidentally solvable after redesign | Solver test asserts unsolvability; fails CI |
|
||||
| Parsing console easter egg as "cheating" concern | Document in code comments only; feature is intentional |
|
||||
|
||||
---
|
||||
|
||||
## Unresolved Questions
|
||||
|
||||
1. **Console easter-egg API shape** — expose `window.__nntvDev.teleport()` explicitly, or rely on Svelte 5 reactive store mutation? Simplest path TBD in Phase 10.
|
||||
2. **Viewport implementation owner** — is scrollable viewport a separate brainstorm/plan, or bundled into this redesign's Phase 1?
|
||||
3. **Story text updates** — do any `storyKey` copy lines need rewriting to match new grid sizes/themes (e.g., "narrow corridor" lines on now-wider levels)? Decision deferred to QA after redesigns land.
|
||||
4. **Chaser count in Act 5+** — cap at 1 per level, or allow 2 in L10/L11? Affects solver state-space budget.
|
||||
Reference in new issue
Block a user