mirror of
https://github.com/tiennm99/nntv.git
synced 2026-10-16 06:14:38 +00:00
feat(levels): redesign 11 solvable levels around new mechanics
Full L1-L11 overhaul around 6-mechanic palette. One mechanic intro per level (one-way L3, suspicion L4, doors+keys L5, decay L6, mirror L7, sniper L8, stones L9). L10-L11 compounding. L12 byte-identical (unsolvable narrative preserved). All BFS-verified under 2M nodes.
This commit is contained in:
1 parent
f498284c9c
commit
bb8d3a61d0
1 file changed
+353
-474
+353
-474
@@ -1,9 +1,11 @@
|
||||
export const LEVELS = [
|
||||
// === ACT 1: THE OUTSKIRTS (movement + static guards) ===
|
||||
// === ACT 1: THE OUTSKIRTS (movement tutorial) ===
|
||||
{
|
||||
// L1 Garden Path — 8x8, 0 guards. Real zigzag via two offset wall bands
|
||||
// that force the player LEFT after going right (or vice versa), not just
|
||||
// the Manhattan diagonal. Solver-verified ~24 moves optimal.
|
||||
// L1 — Garden Path
|
||||
// Mechanic intro: movement only
|
||||
// Intended path sketch: navigate zigzag corridor from top-left to bottom-right.
|
||||
// Three wall bands force alternating left/right detours across the 8x8 grid.
|
||||
// Key insight: teaches arrow movement and grid traversal; no threat present
|
||||
id: 1,
|
||||
name: "Garden Path",
|
||||
storyKey: "level1Story",
|
||||
@@ -11,18 +13,23 @@ export const LEVELS = [
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 7, col: 7 },
|
||||
walls: [
|
||||
{ row: 1, col: 4 }, { row: 1, col: 5 }, { row: 1, col: 6 },
|
||||
{ row: 3, col: 0 }, { row: 3, col: 1 }, { row: 3, col: 2 }, { row: 3, col: 3 }, { row: 3, col: 4 },
|
||||
{ row: 5, col: 3 }, { row: 5, col: 4 }, { row: 5, col: 5 }, { row: 5, col: 6 }, { row: 5, col: 7 },
|
||||
// Band 1: blocks cols 4-7 in row 1 — must go down-left first
|
||||
{ row: 1, col: 4 }, { row: 1, col: 5 }, { row: 1, col: 6 }, { row: 1, col: 7 },
|
||||
// Band 2: blocks cols 0-3 in row 3 — must swing right
|
||||
{ row: 3, col: 0 }, { row: 3, col: 1 }, { row: 3, col: 2 }, { row: 3, col: 3 },
|
||||
// Band 3: blocks cols 4-7 in row 5 — must go left before exit
|
||||
{ row: 5, col: 4 }, { row: 5, col: 5 }, { row: 5, col: 6 }, { row: 5, col: 7 },
|
||||
],
|
||||
guards: [],
|
||||
affordances: { undo: true, preview: true },
|
||||
parMoves: 22,
|
||||
},
|
||||
{
|
||||
// L2 Watchtower — 8x8, 3 wilting tomatoes (initialRadius 2). Same zigzag as
|
||||
// L1 plus three tomato auras parked on the detour path. Player can EITHER
|
||||
// wait 2-3 turns for each aura to shrink OR take a longer detour. Teaches
|
||||
// the wilting mechanic + the Space (wait) action naturally.
|
||||
// L2 — Watchtower
|
||||
// Mechanic intro: static wilting guards
|
||||
// Intended path sketch: same zigzag wall layout as L1 with three static guards on the
|
||||
// fast path. Player must wait 2-3 turns per guard for auras to shrink or take longer routes.
|
||||
// Key insight: teaches the wilting mechanic — Space=wait; patience vs detour tradeoff
|
||||
id: 2,
|
||||
name: "The Watchtower",
|
||||
storyKey: "level2Story",
|
||||
@@ -30,34 +37,34 @@ export const LEVELS = [
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 7, col: 7 },
|
||||
walls: [
|
||||
{ row: 1, col: 4 }, { row: 1, col: 5 }, { row: 1, col: 6 },
|
||||
{ row: 3, col: 0 }, { row: 3, col: 1 }, { row: 3, col: 2 }, { row: 3, col: 3 }, { row: 3, col: 4 },
|
||||
{ row: 5, col: 3 }, { row: 5, col: 4 }, { row: 5, col: 5 }, { row: 5, col: 6 }, { row: 5, col: 7 },
|
||||
{ row: 1, col: 4 }, { row: 1, col: 5 }, { row: 1, col: 6 }, { row: 1, col: 7 },
|
||||
{ row: 3, col: 0 }, { row: 3, col: 1 }, { row: 3, col: 2 }, { row: 3, col: 3 },
|
||||
{ row: 5, col: 4 }, { row: 5, col: 5 }, { row: 5, col: 6 }, { row: 5, col: 7 },
|
||||
],
|
||||
guards: [
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 2, col: 6 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 4, col: 1 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 6, col: 4 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{ type: "static", position: { row: 2, col: 5 }, initialRadius: 2 },
|
||||
{ type: "static", position: { row: 4, col: 3 }, initialRadius: 2 },
|
||||
{ type: "static", position: { row: 6, col: 6 }, initialRadius: 2 },
|
||||
],
|
||||
affordances: { undo: true, preview: true },
|
||||
parMoves: 24,
|
||||
},
|
||||
|
||||
// === ACT 2: THE VEGETABLE GARDEN (rotating + blinking intro) ===
|
||||
// === ACT 2: THE VEGETABLE GARDEN (one-way intro) ===
|
||||
{
|
||||
// L3 Vegetable Patrol — 9x9, 4 guards (3 static + 1 rotating).
|
||||
// Rotating guard introduced in the middle; static guards flank the edges.
|
||||
// L3 — Vegetable Patrol
|
||||
// Mechanic intro: one-way tile (arrow tile; entered only from designated direction)
|
||||
// Intended path sketch:
|
||||
// Grid is divided into a top-left start zone and a bottom-right goal zone by wall bands.
|
||||
// The only connections between zones pass through two one-way tiles:
|
||||
// (2,4) dir=2 (down): player MUST enter by moving down — connecting top to mid zone.
|
||||
// (5,4) dir=1 (right): player MUST enter by moving right — connecting mid to right zone.
|
||||
// Trying to re-enter (2,4) from below (up) → rejected. Trying (5,4) from right (left) → rejected.
|
||||
// One static guard at (0,5) forces a detour in the top zone.
|
||||
// One static guard at (7,3) forces a detour in the bottom zone.
|
||||
// Intended path: right×4→down×2→through(2,4)↓→down×2→right×1→through(5,4)→→right×3→down×3→goal
|
||||
// par=20. Two guards wilt turn 2 so timing is real but not brutal.
|
||||
// Key insight: one-ways create commitment ratchets — plan the full route before crossing.
|
||||
id: 3,
|
||||
name: "Vegetable Patrol",
|
||||
storyKey: "level3Story",
|
||||
@@ -65,40 +72,45 @@ export const LEVELS = [
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 8, col: 8 },
|
||||
walls: [
|
||||
{ row: 1, col: 4 }, { row: 1, col: 5 },
|
||||
{ row: 2, col: 1 }, { row: 2, col: 7 },
|
||||
{ row: 3, col: 3 }, { row: 3, col: 5 },
|
||||
{ row: 5, col: 3 }, { row: 5, col: 6 },
|
||||
{ row: 6, col: 0 }, { row: 6, col: 7 },
|
||||
{ row: 7, col: 3 }, { row: 7, col: 4 },
|
||||
// Top-zone right side wall — blocks right bypass around (2,4) one-way
|
||||
{ row: 0, col: 5 }, { row: 0, col: 6 }, { row: 0, col: 7 }, { row: 0, col: 8 },
|
||||
{ row: 1, col: 5 }, { row: 1, col: 6 }, { row: 1, col: 7 }, { row: 1, col: 8 },
|
||||
// Horizontal wall separating top zone from mid zone; only gap is col 4
|
||||
{ row: 3, col: 0 }, { row: 3, col: 1 }, { row: 3, col: 2 }, { row: 3, col: 3 },
|
||||
{ row: 3, col: 5 }, { row: 3, col: 6 }, { row: 3, col: 7 }, { row: 3, col: 8 },
|
||||
// Vertical wall separating mid zone from right zone; only gap is row 5
|
||||
{ row: 4, col: 5 }, { row: 4, col: 6 }, { row: 4, col: 7 }, { row: 4, col: 8 },
|
||||
{ row: 5, col: 5 }, { row: 5, col: 6 }, { row: 5, col: 7 }, { row: 5, col: 8 },
|
||||
// Wait — need gap at (5,4) for the one-way, so wall must be col>=5 on rows 4-5
|
||||
// Bottom-left wall forcing through bottom zone
|
||||
{ row: 6, col: 0 }, { row: 6, col: 1 }, { row: 6, col: 2 }, { row: 6, col: 3 },
|
||||
],
|
||||
guards: [
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 2, col: 3 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 4, col: 4 },
|
||||
startDirection: 0,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 6, col: 5 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 5, col: 1 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
// Static guard upper area — forces player right then down
|
||||
{ type: "static", position: { row: 0, col: 3 }, initialRadius: 1 },
|
||||
// Static guard lower area — forces player to go around before reaching goal
|
||||
{ type: "static", position: { row: 7, col: 6 }, initialRadius: 1 },
|
||||
],
|
||||
parMoves: 18,
|
||||
// (2,4) dir=2=down: only reachable by moving down from (1,4) — connects top→mid zone
|
||||
// (5,4) dir=1=right: only reachable by moving right from (5,3) — connects mid→right zone
|
||||
// Attempting to go back UP through (2,4) from (3,4) → rejected (moveDir=0≠2)
|
||||
// Attempting to go LEFT through (5,4) from (5,5) → rejected (moveDir=3≠1)
|
||||
oneWays: [
|
||||
{ row: 2, col: 4, dir: 2 }, // enter only when moving down
|
||||
{ row: 5, col: 4, dir: 1 }, // enter only when moving right
|
||||
],
|
||||
affordances: { undo: true, preview: true },
|
||||
parMoves: 22,
|
||||
},
|
||||
|
||||
// === ACT 3: THE SEARCHLIGHT (suspicion guard intro) ===
|
||||
{
|
||||
// L4 Searchlight — 9x9, 5 guards: rotating + blinking intro + 3 static.
|
||||
// Blinking guard creates a timing window — player must wait for lights off.
|
||||
// L4 — Searchlight
|
||||
// Mechanic intro: suspicion guard (3-tier meter; fires at tier 2 lighting nearby cells)
|
||||
// Intended path sketch: rotating guard sweeps upper area. Suspicion guard on right flank
|
||||
// requires player to stay ≥4 cells away (Manhattan) while navigating past it.
|
||||
// Player must time the rotating beam gap AND maintain distance from suspicion guard.
|
||||
// Key insight: suspicion is spatial — distance buffer must be maintained every turn
|
||||
id: 4,
|
||||
name: "The Searchlight",
|
||||
storyKey: "level4Story",
|
||||
@@ -107,49 +119,44 @@ export const LEVELS = [
|
||||
goal: { row: 8, col: 8 },
|
||||
walls: [
|
||||
{ row: 1, col: 5 }, { row: 1, col: 6 },
|
||||
{ row: 2, col: 3 }, { row: 3, col: 1 }, { row: 3, col: 6 },
|
||||
{ row: 4, col: 3 }, { row: 4, col: 5 },
|
||||
{ row: 5, col: 1 }, { row: 5, col: 7 },
|
||||
{ row: 6, col: 3 }, { row: 6, col: 5 },
|
||||
{ row: 7, col: 7 },
|
||||
{ row: 2, col: 2 },
|
||||
{ row: 3, col: 4 }, { row: 3, col: 7 },
|
||||
{ row: 4, col: 2 }, { row: 4, col: 6 },
|
||||
{ row: 5, col: 4 },
|
||||
{ row: 6, col: 1 }, { row: 6, col: 6 },
|
||||
{ row: 7, col: 3 }, { row: 7, col: 7 },
|
||||
],
|
||||
guards: [
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 2, col: 5 },
|
||||
startDirection: 0,
|
||||
},
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 5, col: 4 },
|
||||
startState: true,
|
||||
litCells: [
|
||||
{ row: 4, col: 4 }, { row: 5, col: 3 },
|
||||
{ row: 5, col: 5 }, { row: 6, col: 4 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 3, col: 3 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 7, col: 2 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 6, col: 7 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
// Rotating guard sweeps the central area
|
||||
{ type: "rotating", position: { row: 3, col: 3 }, startDirection: 0 },
|
||||
// Suspicion guard on right flank — range 3
|
||||
{ type: "suspicion", position: { row: 4, col: 7 }, range: 3 },
|
||||
// Static guard blocks left corridor detour
|
||||
{ type: "static", position: { row: 6, col: 3 }, initialRadius: 2 },
|
||||
],
|
||||
parMoves: 19,
|
||||
affordances: { undo: true, preview: true },
|
||||
parMoves: 20,
|
||||
},
|
||||
|
||||
// === ACT 3: THE FORTRESS (patrolling intro) ===
|
||||
// === ACT 4: THE FORTRESS (doors+keys intro) ===
|
||||
{
|
||||
// L5 Fortress Gate — 10x10, 6 guards: static+rotating+blinking+1 patrolling intro.
|
||||
// L5 — Fortress Gate
|
||||
// Mechanic intro: doors + keys (locked door = impassable wall until matching key collected)
|
||||
// Intended path sketch:
|
||||
// 10×10 grid. Player (0,0), Goal (9,9).
|
||||
// Two keys and two doors in a chained dependency: key1→door1→key2→door2→goal.
|
||||
// Key1 at (0,8): top-right; reachable freely across row 0.
|
||||
// Door1 at (4,5) keyId=1: vertical barrier gap; player needs key1 to pass.
|
||||
// Key2 at (6,8): bottom-right zone; only reachable after passing door1.
|
||||
// Door2 at (8,5) keyId=2: lower vertical barrier gap; player needs key2 to pass.
|
||||
// Goal at (9,9): reachable from (8,6) only after door2 opened.
|
||||
// Vertical barrier (col 5, rows 3-9) is the spine. Gaps at (4,5) door1 and (8,5) door2.
|
||||
// Left side: player starts here, goes right to key1 (row 0), comes back left,
|
||||
// navigates down through rotating guard zone to reach (4,5) door1.
|
||||
// Right side: after door1, grab key2 at (6,8), return to (8,5) door2, then goal.
|
||||
// Rotating guard at (2,3) covers left-center; suspicion at (5,3) range=2 guards
|
||||
// the approach to door1 — must wait or detour to avoid tier-up.
|
||||
// Key insight: chained key→door dependency; collect in order.
|
||||
id: 5,
|
||||
name: "Fortress Gate",
|
||||
storyKey: "level5Story",
|
||||
@@ -157,57 +164,49 @@ export const LEVELS = [
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 9, col: 9 },
|
||||
walls: [
|
||||
{ row: 1, col: 2 }, { row: 1, col: 6 },
|
||||
{ row: 2, col: 4 }, { row: 2, col: 8 },
|
||||
{ row: 3, col: 0 }, { row: 3, col: 6 },
|
||||
{ row: 4, col: 4 }, { row: 4, col: 8 },
|
||||
{ row: 6, col: 0 }, { row: 6, col: 4 },
|
||||
// Vertical barrier col 5, rows 3-9 (except doors at rows 4 and 8)
|
||||
{ row: 3, col: 5 },
|
||||
// (4,5) = door keyId=1
|
||||
{ row: 5, col: 5 }, { row: 6, col: 5 }, { row: 7, col: 5 },
|
||||
// (8,5) = door keyId=2
|
||||
{ row: 9, col: 5 },
|
||||
// Left zone walls — shape the approach to door1
|
||||
{ row: 1, col: 2 }, { row: 1, col: 7 },
|
||||
{ row: 3, col: 1 }, { row: 3, col: 3 }, { row: 3, col: 4 },
|
||||
// Right zone walls — shape key2 area and door2 approach
|
||||
{ row: 5, col: 7 }, { row: 5, col: 9 },
|
||||
{ row: 7, col: 6 }, { row: 7, col: 8 },
|
||||
{ row: 8, col: 2 }, { row: 8, col: 5 },
|
||||
{ row: 8, col: 7 }, { row: 8, col: 9 },
|
||||
],
|
||||
guards: [
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 2, col: 2 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 4, col: 6 },
|
||||
startDirection: 0,
|
||||
},
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 5, col: 2 },
|
||||
startState: true,
|
||||
litCells: [
|
||||
{ row: 4, col: 2 }, { row: 5, col: 1 },
|
||||
{ row: 5, col: 3 }, { row: 6, col: 2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 7, col: 7 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 3, col: 8 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "patrolling",
|
||||
startPosition: { row: 6, col: 5 },
|
||||
path: [
|
||||
{ row: 6, col: 5 }, { row: 6, col: 6 },
|
||||
{ row: 7, col: 5 }, { row: 7, col: 4 },
|
||||
],
|
||||
},
|
||||
// Rotating guard in left zone — player must time approach to door1
|
||||
{ type: "rotating", position: { row: 2, col: 3 }, startDirection: 0 },
|
||||
// Suspicion guard to the right of door1 — guards right zone approach to key2
|
||||
// Player must approach key2 from outside range (Manhattan > 2 from (6,6))
|
||||
{ type: "suspicion", position: { row: 6, col: 6 }, range: 2 },
|
||||
],
|
||||
parMoves: 21,
|
||||
// Key1 in top-right: freely reachable right along row 0
|
||||
// Key2 in right zone: only reachable after door1 opened
|
||||
keys: [
|
||||
{ row: 0, col: 8, keyId: 1 },
|
||||
{ row: 6, col: 8, keyId: 2 },
|
||||
],
|
||||
doors: [
|
||||
{ row: 4, col: 5, keyId: 1 },
|
||||
{ row: 8, col: 5, keyId: 2 },
|
||||
],
|
||||
affordances: { undo: true, preview: true },
|
||||
parMoves: 30,
|
||||
},
|
||||
|
||||
// === ACT 5: THE CORRIDOR (decay intro) ===
|
||||
{
|
||||
// L6 Flickering Corridor — 10x10, 7 guards with 2 patrollers + blinkers.
|
||||
// L6 — Flickering Corridor
|
||||
// Mechanic intro: light decay (cells stay "warm" for 1 turn after going dark)
|
||||
// Intended path sketch: two alternating blinking guards cover the corridor.
|
||||
// With decayTiles="all", cells that just went dark are warm for 1 turn —
|
||||
// player can sprint through during the decay window rather than waiting a full cycle.
|
||||
// Key insight: decay window = brief safe window after lights go out; sprint timing
|
||||
id: 6,
|
||||
name: "The Flickering Corridor",
|
||||
storyKey: "level6Story",
|
||||
@@ -215,72 +214,62 @@ export const LEVELS = [
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 9, col: 9 },
|
||||
walls: [
|
||||
{ row: 1, col: 3 }, { row: 1, col: 7 },
|
||||
{ row: 2, col: 1 }, { row: 2, col: 5 },
|
||||
{ row: 3, col: 3 }, { row: 3, col: 8 },
|
||||
{ row: 4, col: 0 }, { row: 4, col: 6 },
|
||||
{ row: 5, col: 2 }, { row: 5, col: 8 },
|
||||
{ row: 6, col: 4 }, { row: 6, col: 5 },
|
||||
{ row: 1, col: 4 }, { row: 1, col: 5 },
|
||||
{ row: 2, col: 2 }, { row: 2, col: 7 },
|
||||
{ row: 3, col: 5 }, { row: 3, col: 8 },
|
||||
// NOTE: row 4 left side clear — litCell at (4,3) needs clear path
|
||||
{ row: 4, col: 1 },
|
||||
{ row: 5, col: 6 }, { row: 5, col: 8 },
|
||||
{ row: 6, col: 2 }, { row: 6, col: 4 },
|
||||
{ row: 7, col: 1 }, { row: 7, col: 7 },
|
||||
{ row: 8, col: 3 }, { row: 8, col: 6 },
|
||||
{ row: 8, col: 4 }, { row: 8, col: 6 },
|
||||
],
|
||||
guards: [
|
||||
// Blinking guard — starts ON, upper corridor
|
||||
// All litCells verified non-wall: (2,3) clear, (3,2) clear, (3,4) clear, (4,3) clear
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 2, col: 3 },
|
||||
position: { row: 3, col: 3 },
|
||||
startState: true,
|
||||
litCells: [
|
||||
{ row: 1, col: 4 }, { row: 2, col: 2 },
|
||||
{ row: 2, col: 4 }, { row: 3, col: 4 },
|
||||
{ row: 2, col: 3 }, { row: 3, col: 2 },
|
||||
{ row: 3, col: 4 }, { row: 4, col: 3 },
|
||||
],
|
||||
},
|
||||
// Blinking guard — starts OFF (offset timing), lower corridor
|
||||
// litCells: (6,6), (7,5), (7,6), (8,5) — all verified non-wall
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 6, col: 7 },
|
||||
position: { row: 7, col: 6 },
|
||||
startState: false,
|
||||
litCells: [
|
||||
{ row: 5, col: 7 }, { row: 6, col: 6 },
|
||||
{ row: 6, col: 8 }, { row: 7, col: 6 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 4, col: 4 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 8, col: 1 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 3, col: 6 },
|
||||
startDirection: 0,
|
||||
},
|
||||
{
|
||||
type: "patrolling",
|
||||
startPosition: { row: 6, col: 2 },
|
||||
path: [
|
||||
{ row: 6, col: 2 }, { row: 6, col: 3 },
|
||||
{ row: 7, col: 3 }, { row: 7, col: 2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "patrolling",
|
||||
startPosition: { row: 5, col: 5 },
|
||||
path: [
|
||||
{ row: 5, col: 5 }, { row: 5, col: 6 },
|
||||
{ row: 4, col: 6 }, { row: 4, col: 5 },
|
||||
{ row: 6, col: 6 }, { row: 7, col: 5 },
|
||||
{ row: 7, col: 6 }, { row: 8, col: 5 },
|
||||
],
|
||||
},
|
||||
// Static guard blocks full detour
|
||||
{ type: "static", position: { row: 5, col: 4 }, initialRadius: 2 },
|
||||
],
|
||||
parMoves: 20,
|
||||
decayTiles: "all",
|
||||
affordances: { undo: true, preview: true },
|
||||
parMoves: 22,
|
||||
},
|
||||
|
||||
// === ACT 4: THE UNDERGROUND (mirror intro) ===
|
||||
// === ACT 6: THE UNDERGROUND (mirror + door intro) ===
|
||||
{
|
||||
// L7 Underground Passage — 11x11, 7 guards with a single mirror intro.
|
||||
// L7 — Underground Passage
|
||||
// Mechanic intro: mirror guard (reflects rotating beam 90°) PLUS door+key gate
|
||||
// Intended path sketch:
|
||||
// Rotating guard at (2,2) starts facing right (dir=1). Beam hits cw mirror at (2,7)
|
||||
// → deflects DOWN through col 7. Second cw mirror at (6,7) → beam deflects LEFT
|
||||
// along row 6 covering (6,6)-(6,0). Player must dodge reflected beam zones.
|
||||
// Key at (4,9): in the right zone past the mirror chain; player reaches it
|
||||
// by going along row 0 right, then down col 9 during safe beam window.
|
||||
// Door at (8,5) keyId=1: blocks the lower-center passage to the goal.
|
||||
// Player must: navigate mirror-chain beam → collect key at (4,9) →
|
||||
// cross to left zone → unlock door at (8,5) → reach goal (10,10).
|
||||
// Wall band: row 5 cols 4-8 (except (5,5) open) forces player through specific corridors.
|
||||
// Key insight: chain mirror → beam trace per turn; door adds collectible side-objective.
|
||||
id: 7,
|
||||
name: "The Underground Passage",
|
||||
storyKey: "level7Story",
|
||||
@@ -288,61 +277,54 @@ export const LEVELS = [
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 10, col: 10 },
|
||||
walls: [
|
||||
{ row: 1, col: 3 }, { row: 2, col: 6 },
|
||||
{ row: 3, col: 1 }, { row: 3, col: 4 },
|
||||
{ row: 4, col: 7 }, { row: 5, col: 2 },
|
||||
{ row: 5, col: 9 }, { row: 6, col: 5 },
|
||||
{ row: 7, col: 3 }, { row: 7, col: 8 },
|
||||
{ row: 8, col: 1 }, { row: 9, col: 5 },
|
||||
// Left approach: channel player down col 0-2 then across
|
||||
{ row: 1, col: 3 }, { row: 1, col: 4 },
|
||||
{ row: 3, col: 1 }, { row: 3, col: 5 },
|
||||
// Center barrier: wall at row 5 cols 4-8 except gap at (5,5)
|
||||
{ row: 5, col: 4 }, { row: 5, col: 6 }, { row: 5, col: 7 }, { row: 5, col: 8 },
|
||||
// Right zone walls: channel to key at (4,9)
|
||||
{ row: 2, col: 8 },
|
||||
{ row: 4, col: 7 }, { row: 4, col: 8 },
|
||||
// Right-side barrier col 9-10, rows 7-10: blocks right-column bypass to goal
|
||||
// Player cannot sneak down col 9/10 to reach (10,10) without going through door
|
||||
{ row: 7, col: 9 }, { row: 7, col: 10 },
|
||||
{ row: 8, col: 9 }, { row: 8, col: 10 },
|
||||
{ row: 9, col: 9 }, { row: 9, col: 10 },
|
||||
// Lower zone: wall at row 8 except door at (8,5)
|
||||
{ row: 8, col: 0 }, { row: 8, col: 1 }, { row: 8, col: 2 }, { row: 8, col: 3 }, { row: 8, col: 4 },
|
||||
// (8,5) = door keyId=1
|
||||
{ row: 8, col: 6 }, { row: 8, col: 7 }, { row: 8, col: 8 },
|
||||
// Goal area
|
||||
{ row: 9, col: 3 }, { row: 9, col: 7 },
|
||||
],
|
||||
guards: [
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 2, col: 3 },
|
||||
startDirection: 0,
|
||||
},
|
||||
{
|
||||
type: "mirror",
|
||||
position: { row: 2, col: 7 },
|
||||
reflectDirection: "cw",
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 4, col: 4 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "patrolling",
|
||||
startPosition: { row: 6, col: 7 },
|
||||
path: [
|
||||
{ row: 6, col: 7 }, { row: 6, col: 8 },
|
||||
{ row: 6, col: 9 }, { row: 6, col: 8 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 8, col: 4 },
|
||||
startState: false,
|
||||
litCells: [
|
||||
{ row: 7, col: 4 }, { row: 8, col: 3 },
|
||||
{ row: 8, col: 5 }, { row: 9, col: 4 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 9, col: 9 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 5, col: 6 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
// Rotating guard feeds mirror chain
|
||||
{ type: "rotating", position: { row: 2, col: 2 }, startDirection: 1 },
|
||||
// cw mirror at (2,7): right→down deflection
|
||||
{ type: "mirror", position: { row: 2, col: 7 }, reflectDirection: "cw" },
|
||||
// cw mirror at (6,7): down→left deflection — creates horizontal beam sweep
|
||||
{ type: "mirror", position: { row: 6, col: 7 }, reflectDirection: "cw" },
|
||||
],
|
||||
parMoves: 22,
|
||||
// Key at (4,9): right zone, accessible during beam-safe window in col 9
|
||||
keys: [
|
||||
{ row: 4, col: 9, keyId: 1 },
|
||||
],
|
||||
// Door at (8,5): lower barrier gap; needs key1 collected from right zone
|
||||
doors: [
|
||||
{ row: 8, col: 5, keyId: 1 },
|
||||
],
|
||||
affordances: { undo: true, preview: true },
|
||||
parMoves: 30,
|
||||
},
|
||||
|
||||
// === ACT 7: THE OUTER WALL (sniper intro) ===
|
||||
{
|
||||
// L8 Gauntlet — 11x11, 8 guards with 2 mirrors + 2 patrollers + rotating.
|
||||
// L8 — The Gauntlet
|
||||
// Mechanic intro: sniper guard (LoS beam to grid edge, rotates 90° CW every 2 turns)
|
||||
// Intended path sketch: sniper at (3,8) starts facing left, sweeping upper corridor.
|
||||
// Every 2 turns it rotates CW — player must time moves inside the 2-turn safe window.
|
||||
// Patrolling guard in lower zone creates a second constraint.
|
||||
// Key insight: sniper cadence is fixed — count turns; safe window repeats every 4 turns
|
||||
id: 8,
|
||||
name: "The Gauntlet",
|
||||
storyKey: "level8Story",
|
||||
@@ -350,150 +332,104 @@ export const LEVELS = [
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 10, col: 10 },
|
||||
walls: [
|
||||
{ row: 1, col: 2 }, { row: 1, col: 8 },
|
||||
{ row: 2, col: 5 }, { row: 3, col: 2 },
|
||||
{ row: 3, col: 7 }, { row: 4, col: 4 },
|
||||
{ row: 4, col: 9 }, { row: 5, col: 1 },
|
||||
{ row: 5, col: 6 }, { row: 6, col: 3 },
|
||||
{ row: 6, col: 8 }, { row: 7, col: 5 },
|
||||
{ row: 1, col: 3 }, { row: 1, col: 7 },
|
||||
{ row: 2, col: 5 },
|
||||
{ row: 3, col: 2 }, { row: 3, col: 6 },
|
||||
{ row: 4, col: 4 }, { row: 4, col: 9 },
|
||||
{ row: 5, col: 1 }, { row: 5, col: 7 },
|
||||
{ row: 6, col: 3 }, { row: 6, col: 8 },
|
||||
{ row: 7, col: 5 }, { row: 7, col: 9 },
|
||||
{ row: 8, col: 2 }, { row: 8, col: 7 },
|
||||
{ row: 9, col: 4 }, { row: 9, col: 9 },
|
||||
{ row: 9, col: 4 }, { row: 9, col: 6 },
|
||||
],
|
||||
guards: [
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 3, col: 3 },
|
||||
startDirection: 0,
|
||||
},
|
||||
{
|
||||
type: "mirror",
|
||||
position: { row: 3, col: 5 },
|
||||
reflectDirection: "ccw",
|
||||
},
|
||||
{
|
||||
type: "mirror",
|
||||
position: { row: 7, col: 3 },
|
||||
reflectDirection: "cw",
|
||||
},
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 7, col: 7 },
|
||||
startDirection: 2,
|
||||
},
|
||||
// Sniper starts facing left (3=left), rotates CW every 2 turns
|
||||
{ type: "sniper", position: { row: 3, col: 8 }, startFacing: 3, rotateCadence: 2 },
|
||||
// Patrolling guard in lower zone
|
||||
{
|
||||
type: "patrolling",
|
||||
startPosition: { row: 2, col: 7 },
|
||||
startPosition: { row: 7, col: 4 },
|
||||
path: [
|
||||
{ row: 2, col: 7 }, { row: 2, col: 8 },
|
||||
{ row: 2, col: 9 }, { row: 2, col: 8 },
|
||||
{ row: 7, col: 4 }, { row: 7, col: 5 },
|
||||
{ row: 7, col: 6 }, { row: 7, col: 5 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "patrolling",
|
||||
startPosition: { row: 8, col: 4 },
|
||||
path: [
|
||||
{ row: 8, col: 4 }, { row: 8, col: 5 },
|
||||
{ row: 8, col: 6 }, { row: 8, col: 5 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 5, col: 9 },
|
||||
startState: true,
|
||||
litCells: [
|
||||
{ row: 5, col: 8 }, { row: 5, col: 10 },
|
||||
{ row: 6, col: 9 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 9, col: 6 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
// Static guard blocks leftward detour
|
||||
{ type: "static", position: { row: 5, col: 3 }, initialRadius: 2 },
|
||||
],
|
||||
parMoves: 22,
|
||||
affordances: { undo: true, preview: true },
|
||||
parMoves: 28,
|
||||
},
|
||||
|
||||
// === ACT 5: THE ROYAL PALACE (chaser intro) ===
|
||||
// === ACT 8: THE ROYAL PALACE (throwable stones intro) ===
|
||||
{
|
||||
// L9 Decoy Path — 12x12, 8 guards including the chaser intro.
|
||||
// Two possible routes: fast central (chaser ambush) vs long perimeter.
|
||||
// L9 — Decoy Path
|
||||
// Mechanic intro: throwable stones (no undo)
|
||||
// Intended path sketch: patrolling guard A + patrolling guard B cover the crossing row
|
||||
// at row 5 with phase-offset patrols (A covers cols 3-5, B covers cols 5-7, overlapping).
|
||||
// Sniper below covers the only alternative lower route.
|
||||
// With stones=2: throw at patrol position → distract guard for 1 turn → sprint through
|
||||
// gap → throw second stone at sniper to clear lower exit.
|
||||
// Note (phase 04): with current engine, stones=0 still solvable via long wait loops;
|
||||
// stones provide ~10-move shortcut making the level tractable within parMoves.
|
||||
// Key insight: stones reduce required waiting turns significantly — plan throws carefully
|
||||
id: 9,
|
||||
name: "The Decoy Path",
|
||||
storyKey: "level9Story",
|
||||
grid: { rows: 12, cols: 12 },
|
||||
grid: { rows: 11, cols: 11 },
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 11, col: 11 },
|
||||
goal: { row: 10, col: 10 },
|
||||
walls: [
|
||||
{ row: 1, col: 4 }, { row: 1, col: 8 },
|
||||
{ row: 2, col: 2 }, { row: 2, col: 9 },
|
||||
{ row: 3, col: 5 }, { row: 3, col: 7 },
|
||||
{ row: 4, col: 1 }, { row: 4, col: 10 },
|
||||
{ row: 5, col: 3 }, { row: 5, col: 8 },
|
||||
{ row: 6, col: 5 }, { row: 6, col: 6 },
|
||||
{ row: 7, col: 2 }, { row: 7, col: 9 },
|
||||
{ row: 8, col: 4 }, { row: 8, col: 7 },
|
||||
{ row: 9, col: 1 }, { row: 9, col: 10 },
|
||||
{ row: 10, col: 5 }, { row: 10, col: 8 },
|
||||
{ row: 1, col: 3 }, { row: 1, col: 7 },
|
||||
{ row: 2, col: 1 }, { row: 2, col: 5 }, { row: 2, col: 9 },
|
||||
{ row: 3, col: 3 }, { row: 3, col: 7 },
|
||||
{ row: 4, col: 0 }, { row: 4, col: 1 }, { row: 4, col: 9 }, { row: 4, col: 10 },
|
||||
{ row: 5, col: 0 }, { row: 5, col: 10 },
|
||||
{ row: 6, col: 0 }, { row: 6, col: 4 }, { row: 6, col: 6 }, { row: 6, col: 10 },
|
||||
{ row: 7, col: 2 }, { row: 7, col: 8 },
|
||||
{ row: 8, col: 4 }, { row: 8, col: 6 },
|
||||
{ row: 9, col: 1 }, { row: 9, col: 9 },
|
||||
],
|
||||
guards: [
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 2, col: 5 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 4, col: 5 },
|
||||
startDirection: 1,
|
||||
},
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 3, col: 9 },
|
||||
startState: false,
|
||||
litCells: [
|
||||
{ row: 2, col: 10 }, { row: 3, col: 8 },
|
||||
{ row: 3, col: 10 }, { row: 4, col: 9 },
|
||||
],
|
||||
},
|
||||
// Patrolling guard A covers left half of row 5 corridor
|
||||
{
|
||||
type: "patrolling",
|
||||
startPosition: { row: 6, col: 2 },
|
||||
startPosition: { row: 5, col: 2 },
|
||||
path: [
|
||||
{ row: 6, col: 2 }, { row: 6, col: 3 },
|
||||
{ row: 7, col: 3 }, { row: 7, col: 2 },
|
||||
{ row: 5, col: 2 }, { row: 5, col: 3 },
|
||||
{ row: 5, col: 4 }, { row: 5, col: 5 },
|
||||
{ row: 5, col: 4 }, { row: 5, col: 3 },
|
||||
],
|
||||
},
|
||||
// Patrolling guard B covers right half of row 5 corridor (phase offset)
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 9, col: 5 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 8, col: 9 },
|
||||
startState: true,
|
||||
litCells: [
|
||||
{ row: 7, col: 10 }, { row: 8, col: 8 },
|
||||
{ row: 8, col: 10 },
|
||||
type: "patrolling",
|
||||
startPosition: { row: 5, col: 8 },
|
||||
path: [
|
||||
{ row: 5, col: 8 }, { row: 5, col: 7 },
|
||||
{ row: 5, col: 6 }, { row: 5, col: 5 },
|
||||
{ row: 5, col: 6 }, { row: 5, col: 7 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 10, col: 10 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
// Chaser ambushes central path — forces player to take perimeter
|
||||
type: "chaser",
|
||||
position: { row: 6, col: 8 },
|
||||
detectionRadius: 3,
|
||||
},
|
||||
// Sniper covers the lower exit corridor (faces left, rotates every 2 turns)
|
||||
{ type: "sniper", position: { row: 8, col: 9 }, startFacing: 3, rotateCadence: 2 },
|
||||
// Suspicion guard blocks right flank bypass
|
||||
{ type: "suspicion", position: { row: 3, col: 9 }, range: 3 },
|
||||
],
|
||||
parMoves: 24,
|
||||
stones: 2,
|
||||
affordances: { undo: false, preview: true },
|
||||
parMoves: 28,
|
||||
},
|
||||
|
||||
// === ACT 9: HALL OF MIRRORS (combo level) ===
|
||||
{
|
||||
// L10 Hall of Mirrors — 12x12, 9 guards: 2 rotating + 3 mirrors + chaser + more.
|
||||
// L10 — Hall of Mirrors
|
||||
// Mechanic intro: (none — pure combo) mirrors + sniper + decay + stones
|
||||
// Intended path sketch: two rotating guards feed two mirrors creating cross-coverage zones.
|
||||
// decayTiles="all" provides brief warm windows after beams sweep past.
|
||||
// Sniper covers goal approach — time entry between rotations.
|
||||
// One stone distracts patrolling guard to open crossing window.
|
||||
// Key insight: overlay all four mechanic systems; decay windows are the safe slots
|
||||
id: 10,
|
||||
name: "Hall of Mirrors",
|
||||
storyKey: "level10Story",
|
||||
@@ -501,153 +437,96 @@ export const LEVELS = [
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 11, col: 11 },
|
||||
walls: [
|
||||
{ row: 1, col: 3 }, { row: 1, col: 8 },
|
||||
{ row: 2, col: 6 }, { row: 3, col: 1 },
|
||||
{ row: 3, col: 9 }, { row: 4, col: 4 },
|
||||
{ row: 4, col: 7 }, { row: 5, col: 2 },
|
||||
{ row: 5, col: 10 }, { row: 6, col: 5 },
|
||||
{ row: 7, col: 3 }, { row: 7, col: 8 },
|
||||
{ row: 8, col: 1 }, { row: 8, col: 6 },
|
||||
{ row: 9, col: 4 }, { row: 9, col: 9 },
|
||||
{ row: 10, col: 2 }, { row: 10, col: 7 },
|
||||
{ row: 1, col: 3 }, { row: 1, col: 7 },
|
||||
{ row: 2, col: 5 }, { row: 2, col: 9 },
|
||||
{ row: 3, col: 1 }, { row: 3, col: 8 },
|
||||
{ row: 4, col: 4 }, { row: 4, col: 10 },
|
||||
{ row: 5, col: 2 }, { row: 5, col: 6 },
|
||||
{ row: 6, col: 4 }, { row: 6, col: 9 },
|
||||
{ row: 7, col: 1 }, { row: 7, col: 7 },
|
||||
{ row: 8, col: 5 }, { row: 8, col: 10 },
|
||||
{ row: 9, col: 3 }, { row: 9, col: 8 },
|
||||
{ row: 10, col: 6 }, { row: 10, col: 9 },
|
||||
],
|
||||
guards: [
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 2, col: 2 },
|
||||
startDirection: 0,
|
||||
},
|
||||
{
|
||||
type: "mirror",
|
||||
position: { row: 2, col: 8 },
|
||||
reflectDirection: "cw",
|
||||
},
|
||||
{
|
||||
type: "mirror",
|
||||
position: { row: 5, col: 5 },
|
||||
reflectDirection: "ccw",
|
||||
},
|
||||
{
|
||||
type: "mirror",
|
||||
position: { row: 8, col: 8 },
|
||||
reflectDirection: "cw",
|
||||
},
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 8, col: 3 },
|
||||
startDirection: 2,
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 4, col: 10 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 6, col: 9 },
|
||||
startState: true,
|
||||
litCells: [
|
||||
{ row: 5, col: 9 }, { row: 6, col: 8 },
|
||||
{ row: 7, col: 9 },
|
||||
],
|
||||
},
|
||||
// Rotating guard feeds first mirror
|
||||
{ type: "rotating", position: { row: 2, col: 2 }, startDirection: 1 },
|
||||
// cw mirror: right→down
|
||||
{ type: "mirror", position: { row: 2, col: 7 }, reflectDirection: "cw" },
|
||||
// Second rotating guard feeds second mirror
|
||||
{ type: "rotating", position: { row: 7, col: 9 }, startDirection: 3 },
|
||||
// ccw mirror: left→down deflection (complements first chain)
|
||||
{ type: "mirror", position: { row: 7, col: 4 }, reflectDirection: "ccw" },
|
||||
// Sniper at lower right — covers goal approach
|
||||
{ type: "sniper", position: { row: 10, col: 8 }, startFacing: 0, rotateCadence: 2 },
|
||||
// Patrolling guard in mid zone — distractible with stone
|
||||
{
|
||||
type: "patrolling",
|
||||
startPosition: { row: 10, col: 4 },
|
||||
startPosition: { row: 6, col: 6 },
|
||||
path: [
|
||||
{ row: 10, col: 4 }, { row: 10, col: 5 },
|
||||
{ row: 10, col: 6 }, { row: 10, col: 5 },
|
||||
{ row: 6, col: 6 }, { row: 6, col: 7 },
|
||||
{ row: 6, col: 8 }, { row: 6, col: 7 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "chaser",
|
||||
position: { row: 9, col: 7 },
|
||||
detectionRadius: 3,
|
||||
},
|
||||
],
|
||||
parMoves: 24,
|
||||
decayTiles: "all",
|
||||
stones: 1,
|
||||
affordances: { undo: false, preview: true },
|
||||
parMoves: 34,
|
||||
},
|
||||
|
||||
// === ACT 10: THE THRONE ROOM (endgame — full palette) ===
|
||||
{
|
||||
// L11 Throne Room — 12x12, 9 guards covering ALL SIX TYPES
|
||||
// (static, rotating, blinking, patrolling, mirror, chaser).
|
||||
// The climactic test before the impossible Princess Chamber.
|
||||
// L11 — Throne Room
|
||||
// Mechanic intro: chaser (full palette: all 6 guard types + stones, no undo/preview)
|
||||
// Intended path sketch: chaser ambushes the central path (stay outside detectionRadius=2).
|
||||
// Sniper sweeps right corridor. Suspicion guard flanks upper-right.
|
||||
// Stones=2: distract patrolling guard + sniper in sequence.
|
||||
// No undo, no preview — full memorization and planning required.
|
||||
// Key insight: endgame synthesis; every mechanic contributes to blocking all easy routes
|
||||
id: 11,
|
||||
name: "The Throne Room",
|
||||
storyKey: "level11Story",
|
||||
grid: { rows: 12, cols: 12 },
|
||||
grid: { rows: 11, cols: 11 },
|
||||
player: { row: 0, col: 0 },
|
||||
goal: { row: 11, col: 11 },
|
||||
goal: { row: 10, col: 10 },
|
||||
walls: [
|
||||
{ row: 1, col: 4 }, { row: 1, col: 8 },
|
||||
{ row: 1, col: 4 }, { row: 1, col: 7 },
|
||||
{ row: 2, col: 2 }, { row: 2, col: 6 },
|
||||
{ row: 3, col: 4 }, { row: 3, col: 9 },
|
||||
{ row: 3, col: 4 }, { row: 3, col: 8 },
|
||||
{ row: 4, col: 1 }, { row: 4, col: 6 },
|
||||
{ row: 5, col: 3 }, { row: 5, col: 10 },
|
||||
{ row: 6, col: 5 }, { row: 6, col: 8 },
|
||||
{ row: 5, col: 3 }, { row: 5, col: 8 },
|
||||
{ row: 6, col: 5 }, { row: 6, col: 7 },
|
||||
{ row: 7, col: 2 }, { row: 7, col: 6 },
|
||||
{ row: 8, col: 4 }, { row: 8, col: 9 },
|
||||
{ row: 8, col: 4 }, { row: 8, col: 8 },
|
||||
{ row: 9, col: 1 }, { row: 9, col: 6 },
|
||||
{ row: 10, col: 3 }, { row: 10, col: 8 },
|
||||
],
|
||||
guards: [
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 1, col: 2 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 3, col: 5 },
|
||||
startDirection: 0,
|
||||
},
|
||||
{
|
||||
type: "mirror",
|
||||
position: { row: 5, col: 5 },
|
||||
reflectDirection: "cw",
|
||||
},
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 4, col: 9 },
|
||||
startState: true,
|
||||
litCells: [
|
||||
{ row: 3, col: 10 }, { row: 4, col: 8 },
|
||||
{ row: 4, col: 10 }, { row: 5, col: 9 },
|
||||
],
|
||||
},
|
||||
// Static guard — upper left, wilting
|
||||
{ type: "static", position: { row: 2, col: 3 }, initialRadius: 2 },
|
||||
// Rotating guard feeds mirror
|
||||
{ type: "rotating", position: { row: 3, col: 5 }, startDirection: 0 },
|
||||
// cw mirror — reflects rotating beam
|
||||
{ type: "mirror", position: { row: 3, col: 8 }, reflectDirection: "cw" },
|
||||
// Sniper — covers right corridor (startFacing=3=left)
|
||||
{ type: "sniper", position: { row: 6, col: 9 }, startFacing: 3, rotateCadence: 2 },
|
||||
// Suspicion guard — flanks upper-right approach
|
||||
{ type: "suspicion", position: { row: 4, col: 8 }, range: 3 },
|
||||
// Patrolling guard — lower passage
|
||||
{
|
||||
type: "patrolling",
|
||||
startPosition: { row: 6, col: 2 },
|
||||
startPosition: { row: 8, col: 3 },
|
||||
path: [
|
||||
{ row: 6, col: 2 }, { row: 6, col: 3 },
|
||||
{ row: 7, col: 3 }, { row: 7, col: 2 },
|
||||
{ row: 8, col: 3 }, { row: 8, col: 4 },
|
||||
{ row: 8, col: 5 }, { row: 8, col: 4 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "static",
|
||||
position: { row: 8, col: 2 },
|
||||
initialRadius: 2,
|
||||
},
|
||||
{
|
||||
type: "rotating",
|
||||
position: { row: 9, col: 8 },
|
||||
startDirection: 2,
|
||||
},
|
||||
{
|
||||
type: "blinking",
|
||||
position: { row: 10, col: 10 },
|
||||
startState: false,
|
||||
litCells: [
|
||||
{ row: 9, col: 10 }, { row: 10, col: 9 },
|
||||
{ row: 10, col: 11 },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "chaser",
|
||||
position: { row: 8, col: 7 },
|
||||
detectionRadius: 3,
|
||||
},
|
||||
// Chaser — small detectionRadius to limit BFS state explosion
|
||||
{ type: "chaser", position: { row: 6, col: 5 }, detectionRadius: 2 },
|
||||
],
|
||||
parMoves: 24,
|
||||
stones: 2,
|
||||
affordances: { undo: false, preview: false },
|
||||
parMoves: 36,
|
||||
},
|
||||
{
|
||||
// L12 THE PRINCESS CHAMBER — 13x13, 10 guards + expanding light wave.
|
||||
|
||||
Reference in new issue
Block a user