room-self-rules.js (4205B)
1 "use strict"; 2 3 /* No-rules puzzle room: the same turn-based grid as room-rules.js with every 4 * line of how-to-play prose removed. The model is never told the goal, what 5 * stepping or turning does, or what the key/door/exit mean — it only sees 6 * neutral observations (adjacent presences, what the view cone 7 * sees, bearings to discovered landmarks, carry status, a step count, 8 * or the top-down map with a neutral legend) plus the action list 9 * with accurate descriptions, and an "insufficient" escape hatch. When it 10 * cannot decide, or the step budget runs out, the client asks /plan for rules 11 * and injects the answer into later states as "Rules". 12 * 13 * Mechanics are RoomRules' (this module only assembles observation text); 14 * SelfRoomRules.PLAN_GOAL is the simulation's one-line statement of the goal — 15 * the only game-specific input to /plan. 16 */ 17 const SelfRoomRules = (() => { 18 const { W, H, MAX_STEPS, DIRS, adjacentLines, inViewLine, knownLine } = RoomRules; 19 20 // First-person state — the ruled fppText minus its three how-to-play lines 21 // (the room/goal intro, the action explanation, the key/door briefing) and 22 // the guided hint. Facing, presence, in-view, carry and steps stay: 23 // they are observations, not rules. There is no event log: a list of the 24 // actor's own recent actions is imitation bait for a single-pass reader. 25 function fppText(state, layout, seen) { 26 return [ 27 // Ordered for a single-pass reader: standing context first, immediate 28 // sensory evidence last (the freshest tokens weigh most). 29 "Current State:", 30 `Steps taken: ${state.steps}/${MAX_STEPS}`, 31 "", 32 knownLine(state, layout, seen), 33 `You carry: ${state.hasKey ? "the key" : "no key"}`, 34 "", 35 inViewLine(state, layout), 36 "", 37 ...adjacentLines(state, layout), 38 ].join("\n"); 39 } 40 41 // Top-down map — the ruled mapText minus the goal line and the pickup/door 42 // instructions. The legend stays: it names symbols, it does not teach rules. 43 function mapText(state, layout) { 44 const rows = []; 45 for (let y = 0; y < H; y++) { 46 const row = []; 47 for (let x = 0; x < W; x++) { 48 if (x === state.x && y === state.y) { row.push(DIRS[state.dir].arrow); continue; } 49 let c = layout.grid[y][x]; 50 if (c === "K" && state.hasKey) c = "."; 51 if (c === "D" && state.doorOpen) c = "o"; 52 row.push(c); 53 } 54 rows.push(row.join(" ")); 55 } 56 return [ 57 "Legend: [`#`: wall, `.`: floor, `D`: locked door, `o`: open door, `K`: key, `E`: exit, `^ > v <`: you, facing that way]", 58 "", 59 ...rows, 60 "", 61 `Steps taken: ${state.steps}/${MAX_STEPS}`, 62 ].join("\n"); 63 } 64 65 // mode: "fpp" | "map" — both neutral; there is deliberately no guided 66 // variant: guidance was instructional prose, which this demo never sends. 67 function stateText(state, layout, mode = "fpp", seen) { 68 if (mode === "map") return mapText(state, layout); 69 return fppText(state, layout, seen); 70 } 71 72 const QUESTION = "What should the player do now?"; 73 // Actions only, with accurate descriptions, plus the escape hatch. Unlike 74 // the platformer there is no denied action here (a bump is still a 75 // completed step), so insufficient is the only mid-attempt trigger. 76 const OPTIONS = [ 77 ...RoomRules.OPTIONS, 78 { id: Planner.INSUFFICIENT_ID, description: "Insufficient evidence to decide" }, 79 ]; 80 81 // Options for the CURRENT state: forward is withheld when the faced cell 82 // is a no-op (wall, or a locked door without the key) — a decision that 83 // cannot change anything is not a decision. Insufficient stays. 84 const optionsFor = (state, layout) => [ 85 ...RoomRules.optionsFor(state, layout), 86 OPTIONS[OPTIONS.length - 1], 87 ]; 88 89 /* The simulation's one-line statement of the goal — the only game-specific 90 * input to /plan. Planner.context() wraps it with the trigger note and the 91 * rules currently in effect; the system prompt is universal. */ 92 const PLAN_GOAL = "Reach the exit of the room."; 93 94 return { fppText, mapText, stateText, QUESTION, OPTIONS, optionsFor, PLAN_GOAL }; 95 })(); 96 97 if (typeof module !== "undefined") module.exports = SelfRoomRules;