Levels were authored against guards that did nothing, so the finale fell
to a twenty-move walk down two edges. Guard tax is now non-zero on every
level with guards, and search depth ramps from 50 states on the opener to
251612 on the throne room.
- fix the perimeter-walk exploit on levels four, eight, nine, ten, eleven
- unblock the mirrors on seven and ten, move guards off wall cells
- phase-align rotating, sniper and static cycles to arrival turns
- tighten par so three stars is earned rather than free
- restore preview on eleven while leaving undo disabled
Level twelve is untouched and remains unsolvable by design.
The suite asserted only that a level was solvable and within par. With no
lower bound, a level whose guards did nothing passed clean, which is how
eleven degenerate levels shipped green.
- ablation: solve with guards, then without, and require a minimum tax
- per-mechanic necessity: removing a level's named mechanic must change it
- authoring: no guard or patrol node on a wall, mirrors reachable by a beam,
sniper beams non-trivial, par not slack enough to make three stars free
- memoized baselines keep the run near 20s despite doubling the solves
Four defects left the stealth mechanics inert: warm tiles were cosmetic,
mirror beams never reflected, static guards died permanently after a few
turns, and suspicion guards reset their own tier every cycle. Deleting
every guard from every level changed the optimal path by zero moves.
- warm tiles are lethal and their decay window survives the turn cycle
- rotating and sniper beams reach mirrors and reflect
- static guards regrow, suspicion tiers persist
- a mobile guard's own cell is lethal, and cell-swap counts as detection
- turn preview simulates the player's move instead of a stationary player
- a rejected one-way move no longer opens the door it was refused
- line-of-sight extracted to one module shared by engine and solver
Every rule change lands in both the turn engine and the BFS solver so CI
cannot certify a level that a human cannot play.
Dependabot alert #16 (high, CVSS 8.1) flags esbuild < 0.28.1 for missing
SHA-256 integrity verification in the Deno download path. Even though the
npm/Vite path used by this project is not directly exploitable, bump
esbuild to the patched release via a pnpm-workspace.yaml override so the
alert closes.
esbuild 0.28.x dropped support for transforming destructuring to the
Vite 6 default browser target set (chrome87/firefox78/safari14), so
also pin build.target and esbuild.target to es2022 in vite.config.js.
Verification:
- pnpm build: success (173 modules, 1.71s)
- pnpm test: 180/180 pass
Replace procedural pixel art with pre-generated PNGs for player, princess,
guards (8 types + suspicion tiers), tiles, keys, stones, and scenes.
Add src/lib/generated-assets.js as the central path registry and update
all sprite/scene components to consume it.
All three reports are historical artifacts:
- v2 brainstorm: shipped (commit 149e07a, journal in docs/journals/)
- 260421 level-redesign brainstorm: fully superseded by v2
- 260420 full-project review: pre-v2; findings addressed by v2
Preserved in git history. Removing from working tree.
The plan and all phase reports are preserved in git history (commit
149e07a). Removing from working tree as the work is fully shipped.
Adds the v2 ship journal entry under docs/journals/.
Adds a reactive effect that fires playDoorUnlock when the count of
locked doors decreases — i.e. when player.moveTo opens a door with a
matching key. Mirrors the existing keysHeld delta pattern.
Closes the door-audio gap documented in plans/260425-1907-tight-12-design-uplift/.
Render new tile types (door/key/one-way/warm) and new guard sprites (sniper
triangle+beam, suspicion ring). Five new HUD components. Throw-targeting state
machine (E enter, arrows/click move, Enter/click confirm, Esc cancel). Per-level
affordance gates wired to Z/Y/V key handlers. GameHistory snapshots extended to
round-trip keysHeld + key/door cell state for correct undo.
Player.moveTo enforces wall/door/one-way validation with direction-of-approach
matching. keysHeld bitmask auto-collects on stepping on key cell. Capture/apply
includes inventory state.
Drop run-wide lives state. Detection routes to level-restart instead of
game-over. Repurpose GameOver scene for run-complete (L11) and bittersweet
(L12) flows. One-shot save migration wipes legacy lives field with welcome
modal.
New cell flags (door, key, one-way, warm). SniperGuard with line-of-sight beam
+ mirror reflection. SuspicionGuard with 3-tier meter. ThrowableSystem with
stone-throw distraction resolved before guard turn. Capture/apply round-trip
preserves all new state.
Static guards now emit a Manhattan aura that shrinks by 1 per turn until
harmless (clamped at -1 to keep solver state finite). Level data schema
change: 'static' guards now take 'initialRadius' instead of 'litCells'.
All 27 static-guard entries migrated in one pass.
L1 and L2 redesigned with real forced-zigzag walls — solver path is now
20 moves (vs Manhattan 14), so players must move AWAY from the goal to
reach it. L2 adds three wilting tomatoes parked on the zigzag path as
a gentle intro to the mechanic.
Tests: +3 new tests on StaticGuard wilting behavior; all 70 tests pass.
L12 widened to 13x13 with 10 guards including 2 chasers and a mirror.
Remains unsolvable by normal play (BFS-verified). window.__nntvDev.teleport
exposed as an intentional escape hatch — console-savvy users who invoke it
win the game. Hidden from README and in-game UI.
Act 5 redesign — chaser guard introduced. L9 has 8 guards with the
chaser ambushing the short route, forcing a perimeter detour. L10 is
the Hall of Mirrors with 9 guards (2 rotating + 3 mirrors + chaser).
Both solvable under 50 ms via BFS.
Act 4 redesign — mirror guard introduced. L7 has 7 guards including
one mirror that deflects a rotating beam. L8 has 8 guards with two
mirrors creating a crossfire pattern.
Act 3 redesign — patrolling guard introduced in L5 (6 guards total).
L6 widens with two patrollers, two blinkers, and a rotating guard
(7 guards). Both solvable via BFS solver.
Act 2 redesign — rotating guard (L3) and blinking guard (L4) introduced.
Both levels widened to 9x9. L4 solution requires a wait action to
synchronize with the blinking cycle, teaching the timing mechanic.
L2 right-side region was disconnected — player could not reach the goal
via any sequence of moves (verified via BFS). New L2 places three static
guards with two viable paths. L1 widens to 8x8 as a gentle Act 1 intro.
Preserves id/name/storyKey/isFinalLevel for progress persistence.
Asserts L1/5-8/10 solvable, L12 unsolvable (easter-egg invariant), and
locks the 12-level count. Known-broken levels (L2/3/4/9/11) tracked
explicitly and auto-unskip when their redesign phases flip them.
Pure BFS over (player, guard snapshots, princess state). Reuses guards.js
and princess-mechanic.js so solver verdicts match runtime behavior. State
budget cap prevents runaway search on impossible levels.
Fix cellSize at 50 and wrap board-container with overflow:auto + smooth
scrollTo effect centered on player. Unlocks grid sizes beyond 9x9 without
shrinking cells below readable size.
Built index.html now references ./assets/... instead of /assets/...
so the dist folder works when opened directly (file://), served from a
subpath (github pages project site, netlify deploy preview, etc.), or
any non-root path. Previously the absolute-root paths produced a blank
page with module load failures in those environments.
Also ignore .claude/ local scratch dir.
- Fix ChaserGuard with BFS pathfinding and proper chase/return states
- Add undo/redo system (Z/Y keys) with full state snapshots
- Add procedural audio via Web Audio API (move, wait, detection, complete)
- Add mobile swipe controls with touch gesture detection
- Add detection feedback (cell flash, player shake animation)
- Add CSS transitions on grid cells for smooth lighting changes
- Add ARIA accessibility labels on game board and cells
- Add controls overlay ("?" button) showing all keyboard/touch shortcuts
- Add mute toggle in HUD
- Update Guide scene with chaser/mirror guard descriptions and tips
- Replace guard switch statement with factory registry pattern
- Extract princess mechanic and touch controls into separate modules
- Localize all UI strings (EN/VI) including new controls and tips
- Update README for Svelte 5 architecture with all current features
- Update project docs (architecture, code standards, codebase summary)