1// Clockwork: a clock tower workshop where clockwork keeps moving between your2// steps. The world's custom property is TICK, built on the engine's declarative3// level.automata: `X IS TICK` makes every X repeat the level's step pattern,4// one beat per accepted turn (a move or a wait).5//6// Runs on the shipped engine (manifest.engine 1). Verified with7// `node tools/solve.mjs mods/worlds/clockwork.cjs`; see docs/worlds/clockwork.md.8// The world's second property (OPEN and SHUT) is a proposal only; its draft9// levels live with the prototype, not in this pack. See clockwork-api.md.10const { word, object, level } = require('@miau/engine');1112// Pattern steps.13const L = { action: 'move', dx: -1, dy: 0 };14const R = { action: 'move', dx: 1, dy: 0 };15const U = { action: 'move', dx: 0, dy: -1 };16const D = { action: 'move', dx: 0, dy: 1 };17const times = (n, step) => Array.from({ length: n }, () => step);1819// ASCII maps, one character per cell. Uppercase letters are word tiles,20// lowercase letters and symbols are things, '.' is empty.21const WORDS = {22 M: 'MIAU', I: 'IS', Y: 'YOU', W: 'WALL', S: 'STOP', B: 'BELL', N: 'WIN',23 O: 'MOUSE', T: 'TICK', A: 'AND', G: 'COG', X: 'DEFEAT', Q: 'SINK',24};25const THINGS = { c: 'MIAU', '#': 'WALL', b: 'BELL', m: 'MOUSE', g: 'COG' };2627function grid(id, rows, meta) {28 const objects = [];29 rows.forEach((row, y) => [...row].forEach((ch, x) => {30 const tag = `${id}-${x}-${y}`;31 if (WORDS[ch]) objects.push(word(WORDS[ch], x, y, tag));32 else if (THINGS[ch]) objects.push(object(THINGS[ch], x, y, tag));33 else if (ch !== '.') throw new Error(`${id}: unknown map character ${ch}`);34 }));35 return level(Math.max(...rows.map(r => r.length)), rows.length, objects, { id, ...meta });36}3738module.exports = {39 manifest: {40 id: 'clockwork',41 name: 'Clockwork',42 version: '0.1.0',43 engine: 1,44 rules: 2, // authored against post-W6 rule semantics (D23)45 author: 'Clockwork world-builder',46 description: 'A clock tower workshop where mice keep time and cogs keep turning.',47 world: { glyph: '⚙️', tagline: 'Some things keep walking after you stop.', accent: 'push' },48 },49 vocabulary: ['MIAU', 'WALL', 'BELL', 'MOUSE', 'COG'],50 palette: {51 MIAU: { glyph: '🐈', faces: 'left' },52 MOUSE: { glyph: '🐁', faces: 'left' },53 WALL: '🧱',54 BELL: '🔔',55 COG: '⚙️',56 },57 // PROPOSED glossary (clockwork-api.md, ask 6): how the stage explains an58 // unfamiliar property tile. The shipped loader ignores this field today.59 properties: {60 TICK: { accent: 'push', describe: 'Repeats its steps, one for each of your turns.' },61 },62 levels: [63 grid('mouse-keeps-time', [64 'MIY.WIS.OIT',65 '...........',66 '###########',67 '#BI..N...m#',68 '###########',69 'c..........',70 '..........b',71 ], {72 title: 'The mouse keeps time',73 allowBypass: true, // "just watch": the mouse moves the word, not the player74 hint: 'The mouse takes one step for every step you take.',75 category: 'required',76 introduces: ['TICK', 'MOUSE'],77 automata: [{ id: 'mouse-shuttle', subject: 'MOUSE', property: 'TICK', phase: 0, pattern: [...times(5, L), ...times(5, R)] }],78 }),79 grid('wind-it-up', [80 'MIY..WIS...',81 '...........',82 'OI.........',83 '....T......',84 'c.........b',85 '###########',86 '#BI..N...m#',87 '###########',88 ], {89 title: 'Wind it up',90 hint: 'A mouse with no rule just sits there.',91 category: 'required',92 introduces: ['TICK'],93 automata: [{ id: 'mouse-shuttle', subject: 'MOUSE', property: 'TICK', phase: 0, pattern: [...times(5, L), ...times(5, R)] }],94 }),95 grid('ring-for-service', [96 'MIY...BIN',97 '......I..',98 '.........',99 '.OIT.....',100 '.c.......',101 'ggggggggg',102 '#.###.###',103 '#m###b###',104 'WIS###GIX',105 ], {106 title: 'Ring for service',107 hint: 'You can\'t cross the cogs. The rule can.',108 category: 'required',109 introduces: ['COG', 'DEFEAT'],110 automata: [{ id: 'spring', property: 'TICK', phase: 0, pattern: [U, U, U, D, D, D] }],111 }),112 grid('double-time', [113 'MIY...BIN',114 'I........',115 '.........',116 '...T.....',117 '.c.......',118 'ggggggggg',119 '.........',120 '.b.......',121 'GIX......',122 ], {123 title: 'Double time',124 hint: 'The cogs only bite if you stop on them.',125 category: 'required',126 introduces: ['TICK'],127 automata: [{ id: 'spring', property: 'TICK', phase: 0, pattern: [D] }],128 }),129 grid('mouse-hole', [130 'MIYOITA.BIN',131 '...........',132 '...........',133 '...........',134 '......Q.m..',135 '.c.........',136 'ggggggggggg',137 '...........',138 '.....b.....',139 '........GIX',140 ], {141 title: 'Mouse hole',142 hint: 'The mouse already walks through the cogs. Make it take one with it.',143 category: 'required',144 introduces: ['SINK'],145 automata: [{ id: 'mouse-dive', subject: 'MOUSE', property: 'TICK', phase: 0, pattern: [D, D, U, U] }],146 }),147 grid('escapement', [148 'MIY.....OIT',149 '......m....',150 '.N.......B.',151 'c..........',152 'ggggggggggg',153 '...........',154 '...........',155 'BIN.GI..GIX',156 ], {157 title: 'Escapement',158 hint: 'The obvious word only makes the cogs worse. What else could a cog be?',159 category: 'challenge',160 introduces: ['TICK'],161 automata: [{ id: 'mouse-press', subject: 'MOUSE', property: 'TICK', phase: 0, pattern: [...times(6, D), ...times(6, U)] }],162 }),163 ],164};