clone

a browser grid puzzle game where you push words around to rewrite the rules of each level

main
miau-is-true / mods / worlds / crossword.cjs
crossword.cjs 8.2K
1// Crossword: a world made only from the base vocabulary. No custom nouns, no2// palette, no custom rules. The board is a type case of words, and every3// puzzle is a grammar puzzle: one IS read across and down, AND on either side,4// stacked words that travel as one, and nouns that are both an answer and the5// next clue.6//7// Levels are drawn as text grids so a reader can check a board at a glance.8// Cells are separated by spaces:9// . empty cell10// m r f w a MIAU, ROCK, FLAG, or WALL thing11// MIAU, IS ... a word tile (any uppercase token)12// A+B several entities stacked in one cell, e.g. WIN+STOP13// Every row must have the same number of cells. Entity ids are derived from14// the level id and the cell, so they stay stable while the board is unchanged.15const { word, object, level } = require('@miau/engine');1617const THINGS = { m: 'MIAU', r: 'ROCK', f: 'FLAG', w: 'WALL' };1819function grid(id, rows, meta) {20 const cells = rows.map(row => row.trim().split(/\s+/));21 const width = cells[0].length;22 cells.forEach((row, y) => {23 if (row.length !== width) throw new Error(`${id}: row ${y} has ${row.length} cells, expected ${width}`);24 });25 const objects = [];26 cells.forEach((row, y) => row.forEach((cell, x) => {27 if (cell === '.') return;28 cell.split('+').forEach((part, i) => {29 const entityId = `${id}-${part.toLowerCase()}-${x}-${y}${i ? `-${i}` : ''}`;30 if (THINGS[part]) objects.push(object(THINGS[part], x, y, entityId));31 else if (/^[A-Z][A-Z0-9_-]*$/.test(part)) objects.push(word(part, x, y, entityId));32 else throw new Error(`${id}: unknown cell "${part}" at column ${x}, row ${y}`);33 });34 }));35 return level(width, cells.length, objects, { id, ...meta });36}3738module.exports = {39 manifest: {40 id: 'crossword',41 name: 'Crossword',42 version: '1.0.0',43 engine: 1,44 rules: 2, // authored against post-W6 rule semantics (D23)45 author: 'Crossword world team',46 description: 'Pure grammar from the base words: read across and down, share one word between two sentences, and carry stacked words.',47 world: { glyph: '✏️', tagline: 'Every word can be read across and down.', accent: 'win' },48 },49 vocabulary: ['MIAU', 'ROCK', 'FLAG', 'WALL'],50 levels: [51 // 1. One IS, read twice. Slide WIN in under the IS from the side (pushing up52 // from below would lift IS out of MIAU IS YOU) to read FLAG IS WIN down.53 grid('across-and-down', [54 '. . . . . . . . .',55 '. . FLAG . . . . . .',56 '. MIAU IS YOU . . . . .',57 '. . . . . WIN . . .',58 '. . . . . . . . .',59 '. m . . . . . f .',60 '. . . . . . . . .',61 ], {62 title: 'Across and down',63 hint: 'There is only one IS. Words are read down as well as across.',64 category: 'required',65 introduces: ['crossing'],66 reuses: [],67 }),68 // 2. MIAU IS YOU is pinned to the top right edge and cannot be broken. Push69 // ROCK and AND up into the two open cells in front of it: ROCK AND MIAU IS70 // YOU. The rock walks to the flag while the cat stays in its pen.71 grid('company', [72 '. . . . . . w . . MIAU IS YOU',73 '. . . . . . w . . . . .',74 '. . r . . . w . ROCK . . .',75 '. . . . . . w . . AND m .',76 '. . . . . . w . . . . .',77 '. . . . f . w w w w w w',78 '. . . . . . . . . . . .',79 'FLAG IS WIN . . . . . . WALL IS STOP',80 ], {81 title: 'Company',82 hint: 'MIAU IS YOU is stuck to the edge, but the start of the line is open.',83 category: 'required',84 introduces: ['AND'],85 reuses: ['YOU'],86 mustForm: ['ROCK IS YOU'],87 }),88 // 3. ROCK IS DEFEAT is pinned to the top left corner. Append AND PUSH so89 // the column of rocks can be shoved instead of touched.90 grid('shove-the-danger', [91 'ROCK IS DEFEAT . . r .',92 '. . . . . r .',93 '. AND . . . r f',94 '. . . PUSH . r .',95 '. m . . . r .',96 '. . . . . r .',97 'MIAU IS YOU . FLAG IS WIN',98 ], {99 title: 'Shove the danger',100 hint: 'You cannot take DEFEAT away from ROCK. You can add something after it.',101 category: 'required',102 introduces: ['AND (predicate)'],103 reuses: ['AND'],104 mustForm: ['ROCK IS PUSH'],105 }),106 // 4. YOU and DEFEAT share a cell and can never be separated, and DEFEAT107 // destroys whatever carries it. On MIAU the pair is suicide; on the ring of108 // STOP rocks it is a demolition order. Build ROCK IS YOU+DEFEAT down from109 // the pinned ROCK and walk through the gap to the flag.110 grid('carry-the-pair', [111 '. . . . . . . ROCK IS STOP',112 '. . . . . . . . . .',113 '. . IS . . . . . . .',114 '. . . . . . r r r .',115 '. m . YOU+DEFEAT . . r f r .',116 '. . . . . . r r r .',117 'MIAU IS YOU . . . . FLAG IS WIN',118 ], {119 title: 'Carry the pair',120 hint: 'Two words share one square and always move together. DEFEAT hurts whatever carries it.',121 category: 'required',122 introduces: ['stacked words', 'DEFEAT on its carrier'],123 reuses: ['crossing'],124 mustForm: ['ROCK IS DEFEAT'],125 }),126 // 5. The flag is sealed in. Put ROCK IS in front of the pinned FLAG IS WIN:127 // ROCK IS FLAG IS WIN. The FLAG word is both an answer and the next clue.128 grid('pass-it-on', [129 '. . . . . FLAG IS WIN',130 '. . . . w w w w',131 '. IS . . w . . .',132 '. . m . w . f .',133 '. ROCK . r w . . .',134 '. . . . w . . .',135 'MIAU IS YOU . w WALL IS STOP',136 ], {137 title: 'Pass it on',138 hint: 'The flag is out of reach. A noun can end one sentence and start the next.',139 category: 'required',140 introduces: ['noun as predicate', 'transformation'],141 reuses: ['AND row insertion'],142 mustForm: ['ROCK IS FLAG'],143 }),144 // 6. The black square above YOU stops the easy ROCK IS YOU down. Build ROCK145 // IS MIAU down onto the pinned MIAU; the rock becomes a second cat.146 grid('down-to-miau', [147 '. . . . . . w WALL IS STOP',148 '. . . . . . w . r .',149 '. . ROCK . . . w . . .',150 '. . . . m . w . . .',151 '. IS . . . . w . . .',152 '. . w . . . w . . f',153 'MIAU IS YOU . . . w FLAG IS WIN',154 ], {155 title: 'Down to MIAU',156 hint: 'MIAU starts a sentence across. What could end on MIAU from above?',157 category: 'optional',158 introduces: ['noun at a crossing'],159 reuses: ['crossing', 'transformation'],160 mustForm: ['ROCK IS MIAU'],161 }),162 // 7. One IS. First WALL IS ROCK (down from the pinned WALL) turns every wall163 // into a passable rock, and that change stays after the sentence breaks.164 // Then take the same IS for a WIN rule: FLAG IS WIN down, or ROCK IS WIN.165 grid('spend-it-twice', [166 '. . . . . FLAG WALL IS STOP',167 '. . . . . . . . .',168 '. . ROCK . . . . . .',169 '. . . . IS . . . .',170 '. . . . . w w w w',171 '. m . . . w WIN . f',172 'MIAU IS YOU . . w . . .',173 ], {174 title: 'Spend it twice',175 hint: 'There is one IS to go around. A thing that has changed stays changed.',176 category: 'challenge',177 introduces: ['sequencing'],178 reuses: ['crossing', 'transformation'],179 mustForm: ['WALL IS ROCK'],180 solverBudget: 4000000,181 }),182 ],183};