From ea44758f7d5702b534f95dfabb6d1ed3f66c415d Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Mon, 8 Jun 2026 21:58:28 -0600 Subject: [PATCH] feat: add Jell-o Monsters (Pudding Monsters) slide-and-merge puzzle game MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new single-player logic puzzle game where players flick jelly blobs across a grid to slide and merge them into a single monster. - PuddingMonstersLogic.js: pure slide-and-merge model with BFS solver, no Phaser/DOM dependencies; shared by client and generator - PuddingMonstersGame.js: Phaser scene with drag-to-flick, arrow keys, undo/reset/hint, par tracking, star collection, death on spikes - genPuddingMonsters.js: offline level generator using seeded RNG and BFS solver; produces 40 levels across 5 difficulty tiers - puddingmonsters.json: generated level data (40 levels, 5–7 grid sizes) - Update main.js, GameRoomScene.js, PreloadScene.js, registry.js to wire the new game into the app - Update game-icons assets --- public/assets/images/game-icons.png | Bin 199162 -> 204694 bytes public/assets/images/game-icons.psd | Bin 528835 -> 540598 bytes public/data/puddingmonsters.json | 2167 +++++++++++++++++ .../puddingmonsters/PuddingMonstersGame.js | 662 +++++ .../puddingmonsters/PuddingMonstersLogic.js | 232 ++ public/src/main.js | 2 + public/src/scenes/GameRoomScene.js | 2 +- public/src/scenes/PreloadScene.js | 1 + server/games/registry.js | 1 + server/scripts/genPuddingMonsters.js | 178 ++ 10 files changed, 3244 insertions(+), 1 deletion(-) create mode 100644 public/data/puddingmonsters.json create mode 100644 public/src/games/puddingmonsters/PuddingMonstersGame.js create mode 100644 public/src/games/puddingmonsters/PuddingMonstersLogic.js create mode 100644 server/scripts/genPuddingMonsters.js diff --git a/public/assets/images/game-icons.png b/public/assets/images/game-icons.png index 6c662e2381bffc46c13a18f23f5e1b392f75a90e..897b0bcba0e377b352299dfc0dd34b2c73b0099a 100644 GIT binary patch delta 44339 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 MGexu;nBv|42TTCJz5oCK delta 7150 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 a.level - b.level); + + try { + const res = await api.get('/puzzles/puddingmonsters/progress'); + this.levelsCompleted = res?.levelsCompleted ?? 0; + } catch (_) { + this.canPersist = false; + this.levelsCompleted = 0; + } + + this.layer = this.add.container(0, 0); + this.bindInput(); + this.showLevelSelect(); + } + + bindInput() { + this.input.on('pointerdown', (p) => this.onPointerDown(p)); + this.input.on('pointerup', (p) => this.onPointerUp(p)); + this.input.keyboard.on('keydown', (e) => this.onKey(e)); + } + + clearLayer() { + this.layer.removeAll(true); + this.monsterGfx = null; + this.starGfx = null; + this.animGfx = null; + this.undoBtn = null; + this.movesText = null; + this.starsText = null; + } + + bestStars(level) { + try { return Number(localStorage.getItem(`pm-stars-${level}`)) || 0; } catch (_) { return 0; } + } + + saveStars(level, stars) { + try { + if (stars > this.bestStars(level)) localStorage.setItem(`pm-stars-${level}`, String(stars)); + } catch (_) { /* ignore */ } + } + + // ── Level select ──────────────────────────────────────────────────────────── + + showLevelSelect() { + this.view = 'select'; + this.overlayUp = false; + this.busy = false; + this.drag = null; + this.clearLayer(); + const cx = GAME_WIDTH / 2; + + const title = this.add.text(cx, 84, 'JELL-O MONSTERS', { + fontFamily: 'Righteous', fontSize: '64px', color: COLORS.goldHex, + }).setOrigin(0.5); + const sub = this.add.text(cx, 140, 'Flick the jellies so they slide and stick — merge them all into one. Grab the 3 stars!', { + fontFamily: '"Julius Sans One"', fontSize: '22px', color: COLORS.mutedHex, + }).setOrigin(0.5); + this.layer.add([title, sub]); + + if (!this.bank.length) { + const msg = this.add.text(cx, 520, 'No levels found.\nRun: node server/scripts/genPuddingMonsters.js', { + fontFamily: '"Julius Sans One"', fontSize: '26px', color: COLORS.dangerHex, align: 'center', + }).setOrigin(0.5); + this.layer.add(msg); + const back = new Button(this, cx, GAME_HEIGHT - 90, 'Back', () => this.scene.start('GameMenu'), { variant: 'ghost' }); + this.layer.add(back); + return; + } + + const nextLevel = Math.min(this.levelsCompleted + 1, this.bank.length); + const prog = this.add.text(cx, 184, `Completed ${this.levelsCompleted} / ${this.bank.length}`, { + fontFamily: 'Righteous', fontSize: '24px', color: COLORS.textHex, + }).setOrigin(0.5); + this.layer.add(prog); + + const COLS = 10; + const SIZE = 120; + const GAP = 16; + const gridW = COLS * SIZE + (COLS - 1) * GAP; + const left = cx - gridW / 2 + SIZE / 2; + const top = 280; + + this.bank.forEach((p, i) => { + const col = i % COLS; + const row = Math.floor(i / COLS); + const x = left + col * (SIZE + GAP); + const y = top + row * (SIZE + GAP); + const level = p.level; + const cleared = level <= this.levelsCompleted; + const playable = level <= nextLevel; + + const fill = cleared ? 0x35265a : playable ? 0x271f44 : 0x191427; + const stroke = cleared ? 0xc792ff : playable ? COLORS.gold : 0x2a2440; + const tile = this.add.rectangle(x, y, SIZE, SIZE, fill).setStrokeStyle(playable || cleared ? 3 : 2, stroke, 1); + const num = this.add.text(x, y - 18, String(level), { + fontFamily: 'Righteous', fontSize: '40px', + color: playable || cleared ? COLORS.textHex : '#564f6b', + }).setOrigin(0.5); + this.layer.add([tile, num]); + + if (playable || cleared) { + const earned = this.bestStars(level); + const stars = this.add.text(x, y + 30, '★★★'.slice(0, earned) + '☆☆☆'.slice(0, 3 - earned), { + fontFamily: 'serif', fontSize: '22px', color: earned ? '#ffd54a' : '#6b5a8a', + }).setOrigin(0.5); + const par = this.add.text(x, y + 52, `par ${p.par}`, { + fontFamily: '"Julius Sans One"', fontSize: '13px', color: COLORS.mutedHex, + }).setOrigin(0.5); + this.layer.add([stars, par]); + } else { + const lock = this.add.text(x, y + 36, 'locked', { + fontFamily: '"Julius Sans One"', fontSize: '14px', color: '#564f6b', + }).setOrigin(0.5); + this.layer.add(lock); + } + + if (playable) { + tile.setInteractive({ useHandCursor: true }); + tile.on('pointerover', () => tile.setStrokeStyle(4, COLORS.gold, 1)); + tile.on('pointerout', () => tile.setStrokeStyle(3, stroke, 1)); + tile.on('pointerup', () => this.playLevel(level)); + } + }); + + const resume = new Button(this, cx - 160, GAME_HEIGHT - 76, `Play Level ${nextLevel}`, () => this.playLevel(nextLevel), + { width: 300, height: 58, fontSize: 24 }); + const back = new Button(this, cx + 170, GAME_HEIGHT - 76, 'Back', () => this.scene.start('GameMenu'), + { variant: 'ghost', width: 180, height: 58, fontSize: 24 }); + this.layer.add([resume, back]); + + if (!this.canPersist) { + const note = this.add.text(cx, GAME_HEIGHT - 26, 'Sign in to save your progress across devices.', { + fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex, + }).setOrigin(0.5); + this.layer.add(note); + } + } + + // ── Play a level ────────────────────────────────────────────────────────────── + + playLevel(level) { + const def = this.bank.find((p) => p.level === level); + if (!def) return; + this.view = 'play'; + this.level = level; + this.levelDef = def; + this.par = def.par; + this.state = newState(def); + this.undoStack = []; + this.moves = 0; + this.overlayUp = false; + this.busy = false; + this.drag = null; + this.selectedCell = null; + + this.clearLayer(); + this.computeLayout(); + this.buildBoard(); + this.starGfx = this.add.graphics().setDepth(D.star); this.layer.add(this.starGfx); + this.monsterGfx = this.add.graphics().setDepth(D.monster); this.layer.add(this.monsterGfx); + this.drawStars(); + this.drawAllMonsters(); + this.drawHud(); + this.updateHud(); + } + + computeLayout() { + const { cols, rows } = this.state; + const LEFT_STRIP = 300; + const TOP = 170; + const BOTTOM = GAME_HEIGHT - 50; + const RIGHT = GAME_WIDTH - 50; + this.cell = Math.min((RIGHT - LEFT_STRIP) / cols, (BOTTOM - TOP) / rows, 132); + const boardW = cols * this.cell; + const boardH = rows * this.cell; + this.originX = LEFT_STRIP + (RIGHT - LEFT_STRIP - boardW) / 2; + this.originY = TOP + (BOTTOM - TOP - boardH) / 2; + } + + cellLeft(x) { return this.originX + x * this.cell; } + cellTop(y) { return this.originY + y * this.cell; } + cellCx(x) { return this.originX + (x + 0.5) * this.cell; } + cellCy(y) { return this.originY + (y + 0.5) * this.cell; } + + cellFromPx(px, py) { + const x = Math.floor((px - this.originX) / this.cell); + const y = Math.floor((py - this.originY) / this.cell); + if (x < 0 || y < 0 || x >= this.state.cols || y >= this.state.rows) return null; + return { x, y }; + } + + buildBoard() { + const { cols, rows } = this.state; + const boardW = cols * this.cell; + const boardH = rows * this.cell; + + const frame = this.add.graphics().setDepth(D.frame); + frame.fillStyle(FRAME, 1); + frame.fillRoundedRect(this.originX - 18, this.originY - 18, boardW + 36, boardH + 36, 22); + this.layer.add(frame); + + const floor = this.add.graphics().setDepth(D.floor); + for (let y = 0; y < rows; y++) { + for (let x = 0; x < cols; x++) { + const k = `${x},${y}`; + if (this.state.walls.has(k)) continue; + floor.fillStyle(FLOOR, 1); + floor.fillRoundedRect(this.cellLeft(x) + 2, this.cellTop(y) + 2, this.cell - 4, this.cell - 4, 8); + floor.lineStyle(1, FLOOR_LN, 1); + floor.strokeRoundedRect(this.cellLeft(x) + 2, this.cellTop(y) + 2, this.cell - 4, this.cell - 4, 8); + } + } + this.layer.add(floor); + + const deco = this.add.graphics().setDepth(D.deco); + for (const k of this.state.walls) { + const [x, y] = k.split(',').map(Number); + deco.fillStyle(WALL, 1); + deco.fillRoundedRect(this.cellLeft(x) + 2, this.cellTop(y) + 2, this.cell - 4, this.cell - 4, 8); + deco.fillStyle(WALL_HI, 1); + deco.fillRoundedRect(this.cellLeft(x) + 6, this.cellTop(y) + 6, this.cell - 12, this.cell * 0.22, 6); + } + for (const k of this.state.spikes) { + const [x, y] = k.split(',').map(Number); + const L = this.cellLeft(x), T = this.cellTop(y), c = this.cell; + deco.fillStyle(SPIKE_BG, 1); + deco.fillRoundedRect(L + 2, T + 2, c - 4, c - 4, 8); + deco.fillStyle(SPIKE, 1); + const baseY = T + c - c * 0.18; + const tipY = T + c * 0.24; + for (let i = 0; i < 3; i++) { + const bx = L + c * (0.2 + i * 0.3); + deco.fillTriangle(bx, baseY, bx + c * 0.2, baseY, bx + c * 0.1, tipY); + } + } + this.layer.add(deco); + } + + drawStars() { + const g = this.starGfx; + g.clear(); + for (const [sx, sy] of this.state.stars) { + const on = this.state.collected.has(`${sx},${sy}`); + const cx = this.cellCx(sx), cy = this.cellCy(sy); + if (on) { + g.fillStyle(STAR_ON, 0.22); + g.fillCircle(cx, cy, this.cell * 0.42); + } + this.drawStarShape(g, cx, cy, this.cell * 0.26, on); + } + } + + drawStarShape(g, cx, cy, r, filled) { + const pts = []; + for (let i = 0; i < 10; i++) { + const ang = (Math.PI / 180) * (-90 + i * 36); + const rad = i % 2 === 0 ? r : r * 0.45; + pts.push(cx + rad * Math.cos(ang), cy + rad * Math.sin(ang)); + } + if (filled) { + g.fillStyle(STAR_ON, 1); + g.beginPath(); g.moveTo(pts[0], pts[1]); + for (let i = 2; i < pts.length; i += 2) g.lineTo(pts[i], pts[i + 1]); + g.closePath(); g.fillPath(); + } + g.lineStyle(3, filled ? 0xffec99 : STAR_OFF, 1); + g.beginPath(); g.moveTo(pts[0], pts[1]); + for (let i = 2; i < pts.length; i += 2) g.lineTo(pts[i], pts[i + 1]); + g.closePath(); g.strokePath(); + } + + colorFor(blob) { + const [x, y] = repCell(blob); + return PALETTE[(x * 7 + y * 13) % PALETTE.length]; + } + + drawAllMonsters(excludeIdx = -1) { + const g = this.monsterGfx; + g.clear(); + this.state.blobs.forEach((blob, i) => { + if (i === excludeIdx) return; + const sel = this.selectedCell && blobAt(this.state, this.selectedCell.x, this.selectedCell.y) === i; + this.drawBlobInto(g, blob.cells, this.colorFor(blob), 0, 0, sel); + }); + } + + drawBlobInto(g, cells, color, ox, oy, selected = false) { + const c = this.cell; + const inset = c * 0.10; + const r = c * 0.30; + const set = new Set(cells.map(([x, y]) => `${x},${y}`)); + + g.fillStyle(color, 1); + for (const [x, y] of cells) { + g.fillRoundedRect(this.cellLeft(x) + ox + inset, this.cellTop(y) + oy + inset, c - 2 * inset, c - 2 * inset, r); + } + for (const [x, y] of cells) { + const L = this.cellLeft(x) + ox, T = this.cellTop(y) + oy; + if (set.has(`${x + 1},${y}`)) g.fillRect(L + c - inset - 1, T + inset, 2 * inset + 2, c - 2 * inset); + if (set.has(`${x},${y + 1}`)) g.fillRect(L + inset, T + c - inset - 1, c - 2 * inset, 2 * inset + 2); + } + // glossy highlight + g.fillStyle(0xffffff, 0.18); + for (const [x, y] of cells) { + const L = this.cellLeft(x) + ox, T = this.cellTop(y) + oy; + g.fillRoundedRect(L + inset + c * 0.12, T + inset + c * 0.10, c * 0.30, c * 0.14, c * 0.07); + } + if (selected) { + g.lineStyle(3, 0xffffff, 0.85); + for (const [x, y] of cells) { + g.strokeRoundedRect(this.cellLeft(x) + ox + inset, this.cellTop(y) + oy + inset, c - 2 * inset, c - 2 * inset, r); + } + } + // eyes at centroid + let ex = 0, ey = 0; + for (const [x, y] of cells) { ex += this.cellCx(x) + ox; ey += this.cellCy(y) + oy; } + ex /= cells.length; ey /= cells.length; + const er = c * 0.12, sp = c * 0.17; + g.fillStyle(0xffffff, 1); + g.fillCircle(ex - sp, ey - er * 0.6, er); g.fillCircle(ex + sp, ey - er * 0.6, er); + g.fillStyle(0x1a1a1a, 1); + g.fillCircle(ex - sp + er * 0.2, ey - er * 0.45, er * 0.5); g.fillCircle(ex + sp + er * 0.2, ey - er * 0.45, er * 0.5); + } + + drawHud() { + const title = this.add.text(40, 80, 'JELL-O\nMONSTERS', { + fontFamily: 'Righteous', fontSize: '34px', color: COLORS.goldHex, lineSpacing: 2, + }).setOrigin(0, 0.5).setDepth(D.ui); + this.layer.add(title); + + this.movesText = this.add.text(GAME_WIDTH - 50, 80, '', { + fontFamily: 'Righteous', fontSize: '30px', color: COLORS.textHex, + }).setOrigin(1, 0.5).setDepth(D.ui); + this.starsText = this.add.text(GAME_WIDTH - 50, 124, '', { + fontFamily: 'serif', fontSize: '30px', color: '#ffd54a', + }).setOrigin(1, 0.5).setDepth(D.ui); + this.layer.add([this.movesText, this.starsText]); + + const BTN_W = 200; + const BTN_H = 56; + const BTN_GAP = 14; + const BTN_X = 150; + const totalH = 4 * BTN_H + 3 * BTN_GAP; + let y = GAME_HEIGHT / 2 - totalH / 2; + + const undo = new Button(this, BTN_X, y, 'Undo', () => this.undo(), { width: BTN_W, height: BTN_H, fontSize: 22 }); + y += BTN_H + BTN_GAP; + const reset = new Button(this, BTN_X, y, 'Reset', () => this.resetLevel(), { width: BTN_W, height: BTN_H, fontSize: 22, variant: 'ghost' }); + y += BTN_H + BTN_GAP; + const hint = new Button(this, BTN_X, y, 'Hint', () => this.showHint(), { width: BTN_W, height: BTN_H, fontSize: 22, variant: 'ghost' }); + y += BTN_H + BTN_GAP; + const levels = new Button(this, BTN_X, y, 'Levels', () => this.showLevelSelect(), { width: BTN_W, height: BTN_H, fontSize: 22, variant: 'ghost' }); + this.undoBtn = undo; + this.layer.add([undo, reset, hint, levels]); + + const tip = this.add.text(BTN_X, y + BTN_H + 26, 'Drag a monster to\nslide it. Arrow keys\nflick the selected one.', { + fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex, align: 'center', + }).setOrigin(0.5, 0).setDepth(D.ui); + this.layer.add(tip); + } + + updateHud() { + if (this.movesText) this.movesText.setText(`Moves: ${this.moves} Par: ${this.par}`); + if (this.starsText) this.starsText.setText(`★ ${starsCollected(this.state)}/3`); + if (this.undoBtn) this.undoBtn.setEnabled(this.undoStack.length > 0); + } + + // ── Input handling ────────────────────────────────────────────────────────── + + onPointerDown(p) { + if (this.view !== 'play' || this.overlayUp || this.busy) return; + const cell = this.cellFromPx(p.x, p.y); + if (cell && blobAt(this.state, cell.x, cell.y) >= 0) { + this.drag = { cell, sx: p.x, sy: p.y }; + this.selectedCell = cell; + this.drawAllMonsters(); + } else { + this.drag = null; + } + } + + onPointerUp(p) { + if (this.view !== 'play' || this.overlayUp || this.busy || !this.drag) return; + const dx = p.x - this.drag.sx; + const dy = p.y - this.drag.sy; + const cell = this.drag.cell; + this.drag = null; + if (Math.max(Math.abs(dx), Math.abs(dy)) < this.cell * 0.28) return; // tap, not a flick + const dir = Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? 'right' : 'left') : (dy > 0 ? 'down' : 'up'); + this.doFlick(cell, dir); + } + + onKey(e) { + if (this.view !== 'play' || this.overlayUp || this.busy || !this.selectedCell) return; + const map = { ArrowUp: 'up', ArrowDown: 'down', ArrowLeft: 'left', ArrowRight: 'right' }; + const dir = map[e.key]; + if (dir) { e.preventDefault?.(); this.doFlick(this.selectedCell, dir); } + } + + // ── Flick / move ──────────────────────────────────────────────────────────── + + doFlick(cell, dir) { + const idx = blobAt(this.state, cell.x, cell.y); + if (idx < 0) return; + const { maxSteps, deathStep } = computeSlide(this.state, idx, dir); + if (maxSteps === 0) { this.nudgeInvalid(idx); return; } + + this.busy = true; + this.undoStack.push(cloneState(this.state)); + this.moves++; + this.updateHud(); + + const movingCells = this.state.blobs[idx].cells.map((c) => [c[0], c[1]]); + const color = this.colorFor(this.state.blobs[idx]); + const [dx, dy] = DIRS[dir]; + const steps = deathStep > 0 ? deathStep : maxSteps; + + this.drawAllMonsters(idx); + this.animGfx = this.add.graphics().setDepth(D.anim); + this.layer.add(this.animGfx); + const proxy = { v: 0 }; + const tgtX = dx * steps * this.cell; + const tgtY = dy * steps * this.cell; + const drawAt = (t) => { this.animGfx.clear(); this.drawBlobInto(this.animGfx, movingCells, color, tgtX * t, tgtY * t); }; + drawAt(0); + playSound(this, SFX.PIECE_CLICK); + + this.tweens.add({ + targets: proxy, v: 1, + duration: Math.min(420, 110 + steps * 55), + ease: deathStep > 0 ? 'Quad.easeIn' : 'Back.easeOut', + onUpdate: () => drawAt(proxy.v), + onComplete: () => { + if (deathStep > 0) this.splatAndFail(idx, dir); + else this.commitMove(idx, dir, movingCells, dx, dy, steps); + }, + }); + } + + commitMove(idx, dir, movingCells, dx, dy, steps) { + const res = slide(this.state, idx, dir); + if (this.animGfx) { this.animGfx.destroy(); this.animGfx = null; } + // keep the moved blob selected at its new resting position + this.selectedCell = { x: movingCells[0][0] + dx * steps, y: movingCells[0][1] + dy * steps }; + this.drawAllMonsters(); + this.drawStars(); + this.updateHud(); + if (res.merged) playSound(this, SFX.CARD_PLACE); + this.busy = false; + if (this.state.state === 'won') this.onSolved(); + } + + splatAndFail(idx, dir) { + slide(this.state, idx, dir); // marks state 'dead' (positions unchanged) + playSound(this, SFX.SCIFI_EXPLODE); + const g = this.animGfx; + if (g) { + this.tweens.add({ + targets: g, alpha: 0, duration: 260, ease: 'Quad.easeIn', + onComplete: () => { g.destroy(); if (this.animGfx === g) this.animGfx = null; this.onDead(); }, + }); + } else { + this.onDead(); + } + } + + nudgeInvalid(idx) { + // brief wobble of the whole monster layer to signal "can't move" + const blob = this.state.blobs[idx]; + const cx = this.cellCx(repCell(blob)[0]); + void cx; + this.tweens.add({ targets: this.monsterGfx, x: 6, duration: 50, yoyo: true, repeat: 1, onComplete: () => { this.monsterGfx.x = 0; } }); + } + + undo() { + if (!this.undoStack.length || this.busy || this.overlayUp) return; + this.state = this.undoStack.pop(); + this.moves++; // an undo still counts as a move (matches Rush Hour) + this.selectedCell = null; + this.drawAllMonsters(); + this.drawStars(); + this.updateHud(); + playSound(this, SFX.PIECE_CLICK); + } + + resetLevel() { + if (this.busy || this.overlayUp) return; + this.state = newState(this.levelDef); + this.undoStack = []; + this.moves = 0; + this.selectedCell = null; + this.drawAllMonsters(); + this.drawStars(); + this.updateHud(); + playSound(this, SFX.CARD_SHUFFLE); + } + + showHint() { + if (this.busy || this.overlayUp) return; + const { path } = solve(this.state, { maxStates: 200000 }); + if (!path || !path.length) return; + const mv = path[0]; + const idx = blobAt(this.state, mv.cell[0], mv.cell[1]); + if (idx < 0) return; + this.selectedCell = { x: mv.cell[0], y: mv.cell[1] }; + this.drawAllMonsters(); + const [dx, dy] = DIRS[mv.dir]; + const cx = this.cellCx(mv.cell[0]); + const cy = this.cellCy(mv.cell[1]); + const arrow = this.add.text(cx + dx * this.cell * 0.5, cy + dy * this.cell * 0.5, + ({ up: '↑', down: '↓', left: '←', right: '→' })[mv.dir], { + fontFamily: 'Righteous', fontSize: `${Math.round(this.cell * 0.6)}px`, color: '#ffffff', + }).setOrigin(0.5).setDepth(D.anim); + this.layer.add(arrow); + this.tweens.add({ targets: arrow, alpha: 0, scale: 1.4, duration: 900, ease: 'Quad.easeOut', onComplete: () => arrow.destroy() }); + } + + // ── End states ────────────────────────────────────────────────────────────── + + onDead() { + this.overlayUp = true; + const cx = GAME_WIDTH / 2; + const cy = GAME_HEIGHT / 2; + const dim = this.add.rectangle(cx, cy, GAME_WIDTH, GAME_HEIGHT, 0x000000, 0.6).setDepth(D.overlay).setInteractive(); + this.layer.add(dim); + + const panel = this.add.graphics().setDepth(D.overlay); + panel.fillStyle(COLORS.panel, 0.98); + panel.fillRoundedRect(cx - 300, cy - 170, 600, 340, 20); + panel.lineStyle(3, COLORS.danger, 1); + panel.strokeRoundedRect(cx - 300, cy - 170, 600, 340, 20); + this.layer.add(panel); + + const title = this.add.text(cx, cy - 90, 'Splat!', { + fontFamily: 'Righteous', fontSize: '64px', color: COLORS.dangerHex, + }).setOrigin(0.5).setDepth(D.overlayUI); + const msg = this.add.text(cx, cy - 14, 'A monster slid onto the spikes. Try again!', { + fontFamily: '"Julius Sans One"', fontSize: '24px', color: COLORS.textHex, + }).setOrigin(0.5).setDepth(D.overlayUI); + this.layer.add([title, msg]); + + const retry = new Button(this, cx - 150, cy + 90, 'Retry', () => this.playLevel(this.level), + { width: 250, height: 58, fontSize: 24 }).setDepth(D.overlayUI); + const levels = new Button(this, cx + 150, cy + 90, 'Levels', () => this.showLevelSelect(), + { width: 250, height: 58, fontSize: 24, variant: 'ghost' }).setDepth(D.overlayUI); + this.layer.add([retry, levels]); + } + + onSolved() { + this.overlayUp = true; + const stars = starsCollected(this.state); + this.saveStars(this.level, stars); + + if (this.level > this.levelsCompleted) this.levelsCompleted = this.level; + api.post('/puzzles/puddingmonsters/complete', { level: this.level }) + .then((res) => { if (res?.levelsCompleted != null) this.levelsCompleted = Math.max(this.levelsCompleted, res.levelsCompleted); }) + .catch(() => { /* best effort */ }); + api.post('/history/single-player', { + slug: 'puddingmonsters', score: this.moves, opponentScores: [], result: 'win', + }).catch(() => { /* best effort */ }); + playSound(this, SFX.VICTORY_SHORT); + + const cx = GAME_WIDTH / 2; + const cy = GAME_HEIGHT / 2; + const dim = this.add.rectangle(cx, cy, GAME_WIDTH, GAME_HEIGHT, 0x000000, 0.62).setDepth(D.overlay).setInteractive(); + this.layer.add(dim); + + const panel = this.add.graphics().setDepth(D.overlay); + panel.fillStyle(COLORS.panel, 0.98); + panel.fillRoundedRect(cx - 320, cy - 210, 640, 420, 20); + panel.lineStyle(3, 0xc792ff, 1); + panel.strokeRoundedRect(cx - 320, cy - 210, 640, 420, 20); + this.layer.add(panel); + + const title = this.add.text(cx, cy - 140, 'Solved!', { + fontFamily: 'Righteous', fontSize: '64px', color: COLORS.goldHex, + }).setOrigin(0.5).setDepth(D.overlayUI); + const starRow = this.add.text(cx, cy - 64, '★★★'.slice(0, stars) + '☆☆☆'.slice(0, 3 - stars), { + fontFamily: 'serif', fontSize: '56px', color: '#ffd54a', + }).setOrigin(0.5).setDepth(D.overlayUI); + const beatPar = this.moves <= this.par; + const stat = this.add.text(cx, cy - 4, + `Level ${this.level} cleared in ${this.moves} moves (par ${this.par})${beatPar ? ' ★ par or better!' : ''}`, { + fontFamily: '"Julius Sans One"', fontSize: '24px', color: COLORS.textHex, + }).setOrigin(0.5).setDepth(D.overlayUI); + this.layer.add([title, starRow, stat]); + + const hasNext = this.level < this.bank.length; + const btns = []; + if (hasNext) { + btns.push(new Button(this, cx, cy + 70, `Next Level (${this.level + 1})`, () => this.playLevel(this.level + 1), + { width: 340, height: 60, fontSize: 26 }).setDepth(D.overlayUI)); + } else { + btns.push(this.add.text(cx, cy + 62, 'You cleared every level. Sweet!', { + fontFamily: '"Julius Sans One"', fontSize: '24px', color: COLORS.goldHex, + }).setOrigin(0.5).setDepth(D.overlayUI)); + } + const replay = new Button(this, cx - 120, cy + 150, 'Replay', () => this.playLevel(this.level), + { width: 210, height: 54, fontSize: 22, variant: 'ghost' }).setDepth(D.overlayUI); + const levels = new Button(this, cx + 120, cy + 150, 'Levels', () => this.showLevelSelect(), + { width: 210, height: 54, fontSize: 22, variant: 'ghost' }).setDepth(D.overlayUI); + btns.push(replay, levels); + this.layer.add(btns); + } +} diff --git a/public/src/games/puddingmonsters/PuddingMonstersLogic.js b/public/src/games/puddingmonsters/PuddingMonstersLogic.js new file mode 100644 index 0000000..39965c7 --- /dev/null +++ b/public/src/games/puddingmonsters/PuddingMonstersLogic.js @@ -0,0 +1,232 @@ +// Pudding Monsters — pure slide-and-merge model + BFS solver. No Phaser, no DOM. +// Shared by the client scene and the offline level generator (both ESM). +// +// A level is a grid of cols x rows. Monsters are jelly blobs (each starts as one +// cell). Flicking a blob slides it in a direction until any of its cells would +// leave the board, hit a WALL, or hit another blob — classic "ice slide". When a +// blob comes to rest orthogonally adjacent to another blob they STICK into one +// rigid blob. The level is solved when every monster has merged into a single +// connected blob. +// +// SPIKES are deadly: if a blob's slide path crosses a spike it dies (the run +// fails and the level must be restarted). STARS are bonus floor tiles collected +// when a monster covers them. +// +// A blob: { cells: [[x,y], ...] } (rigid; moves as a unit) + +export const DIRS = { up: [0, -1], down: [0, 1], left: [-1, 0], right: [1, 0] }; +export const DIR_LIST = ['up', 'down', 'left', 'right']; + +const ADJ = [[1, 0], [-1, 0], [0, 1], [0, -1]]; +const key = (x, y) => `${x},${y}`; + +function occupiedAt(state, x, y) { + for (const b of state.blobs) { + for (const [cx, cy] of b.cells) if (cx === x && cy === y) return true; + } + return false; +} + +export function blobAt(state, x, y) { + for (let i = 0; i < state.blobs.length; i++) { + for (const [cx, cy] of state.blobs[i].cells) if (cx === x && cy === y) return i; + } + return -1; +} + +// Lowest cell (top-left in row-major order) — a stable representative for a blob. +export function repCell(blob) { + return blob.cells.reduce((best, c) => + (c[1] < best[1] || (c[1] === best[1] && c[0] < best[0])) ? c : best, blob.cells[0]); +} + +// Union any blobs that are orthogonally adjacent, transitively, into single blobs. +function mergeBlobs(state) { + const occ = new Map(); + state.blobs.forEach((b, i) => b.cells.forEach(([x, y]) => occ.set(key(x, y), i))); + + const parent = state.blobs.map((_, i) => i); + const find = (a) => { while (parent[a] !== a) { parent[a] = parent[parent[a]]; a = parent[a]; } return a; }; + const union = (a, b) => { const ra = find(a), rb = find(b); if (ra !== rb) parent[ra] = rb; }; + + state.blobs.forEach((b, i) => { + for (const [x, y] of b.cells) { + for (const [dx, dy] of ADJ) { + const j = occ.get(key(x + dx, y + dy)); + if (j !== undefined && j !== i) union(i, j); + } + } + }); + + const groups = new Map(); + state.blobs.forEach((b, i) => { + const r = find(i); + if (!groups.has(r)) groups.set(r, []); + groups.get(r).push(...b.cells); + }); + state.blobs = [...groups.values()].map((cells) => ({ cells })); +} + +function pickUpStars(state) { + for (const [sx, sy] of state.stars) { + if (!state.collected.has(key(sx, sy)) && occupiedAt(state, sx, sy)) { + state.collected.add(key(sx, sy)); + } + } +} + +export function starsCollected(state) { + return state.collected.size; +} + +// How far blob `idx` can slide in `dir`, and whether that path crosses a spike. +// Pure (does not mutate). { maxSteps, deathStep } — deathStep>0 means fatal. +export function computeSlide(state, idx, dir) { + const [dx, dy] = DIRS[dir]; + const blob = state.blobs[idx]; + + const other = new Set(); + state.blobs.forEach((b, i) => { + if (i !== idx) b.cells.forEach(([x, y]) => other.add(key(x, y))); + }); + + let maxSteps = 0; + const limit = state.cols + state.rows; + for (let s = 1; s <= limit; s++) { + let ok = true; + for (const [x, y] of blob.cells) { + const nx = x + dx * s, ny = y + dy * s; + if (nx < 0 || ny < 0 || nx >= state.cols || ny >= state.rows) { ok = false; break; } + if (state.walls.has(key(nx, ny)) || other.has(key(nx, ny))) { ok = false; break; } + } + if (!ok) break; + maxSteps = s; + } + + let deathStep = 0; + for (let s = 1; s <= maxSteps && deathStep === 0; s++) { + for (const [x, y] of blob.cells) { + if (state.spikes.has(key(x + dx * s, y + dy * s))) { deathStep = s; break; } + } + } + return { maxSteps, deathStep }; +} + +// Flick blob `idx` in `dir`. Mutates state. Returns: +// { moved:false } — couldn't move (no-op) +// { moved:true, dead:true, deathStep } — slid onto a spike (run fails) +// { moved:true, dead:false, merged, steps } — slid and (maybe) merged +export function slide(state, idx, dir) { + const { maxSteps, deathStep } = computeSlide(state, idx, dir); + if (maxSteps === 0) return { moved: false }; + + const [dx, dy] = DIRS[dir]; + if (deathStep > 0) { + state.state = 'dead'; + return { moved: true, dead: true, deathStep }; + } + + const blob = state.blobs[idx]; + blob.cells = blob.cells.map(([x, y]) => [x + dx * maxSteps, y + dy * maxSteps]); + const before = state.blobs.length; + mergeBlobs(state); + pickUpStars(state); + state.state = state.blobs.length === 1 ? 'won' : 'playing'; + return { moved: true, dead: false, merged: state.blobs.length < before, steps: maxSteps }; +} + +// Every non-fatal flick available. Each entry carries the blob's representative +// cell so a move stays identifiable after merges renumber the blobs. +export function legalMoves(state) { + const moves = []; + state.blobs.forEach((blob, idx) => { + for (const dir of DIR_LIST) { + const { maxSteps, deathStep } = computeSlide(state, idx, dir); + if (maxSteps > 0 && deathStep === 0) moves.push({ idx, dir, cell: repCell(blob) }); + } + }); + return moves; +} + +// Canonical key: cells sorted within each blob, blobs sorted, joined. Walls and +// spikes are fixed for a level, so this fully identifies a configuration. +export function stateKey(state) { + return state.blobs + .map((b) => b.cells.map(([x, y]) => key(x, y)).sort().join(';')) + .sort() + .join('|'); +} + +export function cloneState(state) { + return { + cols: state.cols, + rows: state.rows, + walls: state.walls, // immutable during play — shared + spikes: state.spikes, // immutable during play — shared + stars: state.stars, // immutable during play — shared + blobs: state.blobs.map((b) => ({ cells: b.cells.map(([x, y]) => [x, y]) })), + collected: new Set(state.collected), + state: state.state, + }; +} + +export function newState(level) { + const state = { + cols: level.cols, + rows: level.rows, + walls: new Set((level.walls ?? []).map(([x, y]) => key(x, y))), + spikes: new Set((level.spikes ?? []).map(([x, y]) => key(x, y))), + stars: (level.stars ?? []).map((c) => [c[0], c[1]]), + blobs: (level.monsters ?? []).map(([x, y]) => ({ cells: [[x, y]] })), + collected: new Set(), + state: 'playing', + }; + mergeBlobs(state); // merge any monsters that start touching + pickUpStars(state); + state.state = state.blobs.length === 1 ? 'won' : 'playing'; + return state; +} + +// Breadth-first shortest solution (fewest flicks to merge everything into one +// blob). Returns { moves, path, footprint }: +// moves — minimum flicks (0 if already solved, -1 if none within maxStates) +// path — [{ cell:[x,y], dir }] optimal flicks (null if unsolvable) +// footprint — cells of the final single blob (null if unsolvable); the +// generator marks 3 of these as stars to guarantee a 3-star clear. +export function solve(state, { maxStates = 200000 } = {}) { + const start = cloneState(state); + if (start.blobs.length === 1) return { moves: 0, path: [], footprint: start.blobs[0].cells }; + + const startKey = stateKey(start); + const meta = new Map([[startKey, null]]); + const stateByKey = new Map([[startKey, start]]); + let frontier = [startKey]; + let depth = 0; + + while (frontier.length) { + depth++; + const next = []; + for (const k of frontier) { + const cur = stateByKey.get(k); + for (const mv of legalMoves(cur)) { + const ns = cloneState(cur); + slide(ns, mv.idx, mv.dir); + const nk = stateKey(ns); + if (meta.has(nk)) continue; + meta.set(nk, { parentKey: k, move: { cell: mv.cell, dir: mv.dir } }); + if (ns.blobs.length === 1) { + const path = []; + let c = nk; + while (meta.get(c)) { const e = meta.get(c); path.unshift(e.move); c = e.parentKey; } + return { moves: depth, path, footprint: ns.blobs[0].cells }; + } + stateByKey.set(nk, ns); + next.push(nk); + } + if (meta.size > maxStates) return { moves: -1, path: null, footprint: null }; + } + frontier = next; + if (depth > 60) break; + } + return { moves: -1, path: null, footprint: null }; +} diff --git a/public/src/main.js b/public/src/main.js index d0fed6d..13e71bb 100644 --- a/public/src/main.js +++ b/public/src/main.js @@ -63,6 +63,7 @@ import TriominoesGame from './games/triominoes/TriominoesGame.js'; import FreecellGame from './games/freecell/FreecellGame.js'; import RushHourGame from './games/rushhour/RushHourGame.js'; import HexsweeperGame from './games/hexsweeper/HexsweeperGame.js'; +import PuddingMonstersGame from './games/puddingmonsters/PuddingMonstersGame.js'; const config = { type: Phaser.AUTO, @@ -139,6 +140,7 @@ const config = { FreecellGame, RushHourGame, HexsweeperGame, + PuddingMonstersGame, ], }; diff --git a/public/src/scenes/GameRoomScene.js b/public/src/scenes/GameRoomScene.js index ee83d87..2946e01 100644 --- a/public/src/scenes/GameRoomScene.js +++ b/public/src/scenes/GameRoomScene.js @@ -22,7 +22,7 @@ export default class GameRoomScene extends Phaser.Scene { } create() { - const slugDispatch = { backgammon: 'Backgammon', holdem: 'HoldemGame', blackjack: 'BlackjackGame', parchisi: 'ParchisiGame', yatzi: 'YatziGame', skipbo: 'SkipBoGame', phase10: 'Phase10Game', chinesecheckers: 'ChineseCheckersGame', gofish: 'GoFishGame', uno: 'UnoGame', craps: 'CrapsGame', roulette: 'RouletteGame', mexicantrain: 'MexicanTrainGame', hearts: 'HeartsGame', catan: 'CatanGame', tickettoride: 'TicketToRideGame', nerts: 'NertsGame', bingo: 'BingoGame', baccarat: 'BaccaratGame', dominion: 'DominionGame', checkers: 'CheckersGame', chess: 'ChessGame', wordle: 'WordleGame', scrabble: 'ScrabbleGame', ghost: 'GhostGame', wordladder: 'WordLadderGame', wordsearch: 'WordSearchGame', hangman: 'HangmanGame', sudoku: 'SudokuGame', othello: 'OthelloGame', go: 'GoGame', battleship: 'BattleshipGame', mastermind: 'MastermindGame', connect4: 'Connect4Game', boggle: 'BoggleGame', oldmaid: 'OldMaidGame', blokus: 'BlokusGame', spellingbee: 'SpellingBeeGame', minicrossword: 'MiniCrosswordGame', forbiddenisland: 'ForbiddenIslandGame', solitairetour: 'SolitaireTourGame', splendor: 'SplendorGame', tectonic: 'TectonicGame', labyrinth: 'LabyrinthGame', videopoker: 'VideoPokerGame', farkel: 'FarkelGame', stratego: 'StrategoGame', kiitos: 'KiitosGame', monopoly: 'MonopolyGame', triominoes: 'TriominoesGame', freecell: 'FreecellGame', rushhour: 'RushHourGame', hexsweeper: 'HexsweeperGame' }; + const slugDispatch = { backgammon: 'Backgammon', holdem: 'HoldemGame', blackjack: 'BlackjackGame', parchisi: 'ParchisiGame', yatzi: 'YatziGame', skipbo: 'SkipBoGame', phase10: 'Phase10Game', chinesecheckers: 'ChineseCheckersGame', gofish: 'GoFishGame', uno: 'UnoGame', craps: 'CrapsGame', roulette: 'RouletteGame', mexicantrain: 'MexicanTrainGame', hearts: 'HeartsGame', catan: 'CatanGame', tickettoride: 'TicketToRideGame', nerts: 'NertsGame', bingo: 'BingoGame', baccarat: 'BaccaratGame', dominion: 'DominionGame', checkers: 'CheckersGame', chess: 'ChessGame', wordle: 'WordleGame', scrabble: 'ScrabbleGame', ghost: 'GhostGame', wordladder: 'WordLadderGame', wordsearch: 'WordSearchGame', hangman: 'HangmanGame', sudoku: 'SudokuGame', othello: 'OthelloGame', go: 'GoGame', battleship: 'BattleshipGame', mastermind: 'MastermindGame', connect4: 'Connect4Game', boggle: 'BoggleGame', oldmaid: 'OldMaidGame', blokus: 'BlokusGame', spellingbee: 'SpellingBeeGame', minicrossword: 'MiniCrosswordGame', forbiddenisland: 'ForbiddenIslandGame', solitairetour: 'SolitaireTourGame', splendor: 'SplendorGame', tectonic: 'TectonicGame', labyrinth: 'LabyrinthGame', videopoker: 'VideoPokerGame', farkel: 'FarkelGame', stratego: 'StrategoGame', kiitos: 'KiitosGame', monopoly: 'MonopolyGame', triominoes: 'TriominoesGame', freecell: 'FreecellGame', rushhour: 'RushHourGame', hexsweeper: 'HexsweeperGame', puddingmonsters: 'PuddingMonstersGame' }; if (slugDispatch[this.game.slug]) { this.scene.start(slugDispatch[this.game.slug], { game: this.game, diff --git a/public/src/scenes/PreloadScene.js b/public/src/scenes/PreloadScene.js index 423e9e8..60be028 100644 --- a/public/src/scenes/PreloadScene.js +++ b/public/src/scenes/PreloadScene.js @@ -60,6 +60,7 @@ export default class PreloadScene extends Phaser.Scene { this.load.json('card-backs', '/data/card-backs.json'); this.load.json('music', '/data/music.json'); this.load.json('rushhour', '/data/rushhour.json'); + this.load.json('puddingmonsters', '/data/puddingmonsters.json'); this.load.audio('sfx-water-splash', '/assets/fx/water-splash.mp3'); this.load.audio('sfx-water-sink', '/assets/fx/water-sink.mp3'); diff --git a/server/games/registry.js b/server/games/registry.js index 0194fc5..8b71c77 100644 --- a/server/games/registry.js +++ b/server/games/registry.js @@ -78,3 +78,4 @@ registerGame({ slug: 'triominoes', name: 'Tri-Ominoes', category: registerGame({ slug: 'freecell', name: 'Freecell', category: 'cards', cardGame: true, minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, iconFrame: 50 }); registerGame({ slug: 'rushhour', name: 'Rush Hour', category: 'logic', minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, hasTutorial: true, iconFrame: 51 }); registerGame({ slug: 'hexsweeper', name: 'Hexsweeper', category: 'logic', minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, iconFrame: 52 }); +registerGame({ slug: 'puddingmonsters', name: 'Jell-o Monsters', category: 'logic', minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, hasTutorial: true, iconFrame: 53 }); diff --git a/server/scripts/genPuddingMonsters.js b/server/scripts/genPuddingMonsters.js new file mode 100644 index 0000000..5ac6541 --- /dev/null +++ b/server/scripts/genPuddingMonsters.js @@ -0,0 +1,178 @@ +// Offline generator for Pudding Monsters levels. +// +// For each difficulty tier it random-fills a grid with walls, spikes and K +// monsters, runs the BFS solver to (a) reject unsolvable/trivial layouts and +// (b) label each survivor with its minimum flick count (par), then places 3 +// guaranteed-collectable stars on cells of that solution's final footprint and +// writes ordered levels to public/data/puddingmonsters.json. +// +// Usage: +// node server/scripts/genPuddingMonsters.js [seed] [outFile] +// +// Deterministic: same seed -> same bank. Re-run after changing the curve. + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { newState, solve } from '../../public/src/games/puddingmonsters/PuddingMonstersLogic.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const OUT_FILE = process.argv[3] + ? path.resolve(process.argv[3]) + : path.join(__dirname, '../../public/data/puddingmonsters.json'); + +const SEED = process.argv[2] ? Number(process.argv[2]) >>> 0 : 0x5eed1234; + +// ── Seeded RNG (mulberry32) ────────────────────────────────────────────────── +function makeRng(seed) { + let a = seed >>> 0; + return () => { + a |= 0; a = (a + 0x6d2b79f5) | 0; + let t = Math.imul(a ^ (a >>> 15), 1 | a); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +} +const rng = makeRng(SEED); +const randInt = (n) => Math.floor(rng() * n); + +// Difficulty curve. Ordered tiers ramp grid size, monsters and obstacles; each +// keeps `count` levels whose par falls in [minPar, maxPar]. Spikes appear only +// in later tiers (yield + difficulty). Levels are numbered tier-by-tier. +const TIERS = [ + { count: 8, cols: 5, rows: 5, monsters: 3, walls: 0, spikes: 0, minPar: 2, maxPar: 3 }, + { count: 8, cols: 6, rows: 6, monsters: 3, walls: 2, spikes: 0, minPar: 3, maxPar: 5 }, + { count: 8, cols: 6, rows: 6, monsters: 4, walls: 3, spikes: 0, minPar: 4, maxPar: 7 }, + { count: 8, cols: 7, rows: 7, monsters: 4, walls: 4, spikes: 1, minPar: 5, maxPar: 9 }, + { count: 8, cols: 7, rows: 7, monsters: 5, walls: 5, spikes: 2, minPar: 7, maxPar: 14 }, +]; +const MAX_ATTEMPTS = 6000000; +const MAX_SECONDS = 200; +const SOLVE_MAX_STATES = 80000; + +const keyOf = (x, y) => `${x},${y}`; + +// Place `n` distinct random cells avoiding `taken`; returns null if it can't. +function placeCells(n, cols, rows, taken) { + const out = []; + let tries = 0; + while (out.length < n && tries < 400) { + tries++; + const x = randInt(cols), y = randInt(rows); + const k = keyOf(x, y); + if (taken.has(k)) continue; + taken.add(k); + out.push([x, y]); + } + return out.length === n ? out : null; +} + +// Build one random candidate level for a tier (or null on failure). +function randomLevel(tier) { + const taken = new Set(); + const walls = placeCells(tier.walls, tier.cols, tier.rows, taken); + if (!walls) return null; + const spikes = placeCells(tier.spikes, tier.cols, tier.rows, taken); + if (!spikes) return null; + const monsters = placeCells(tier.monsters, tier.cols, tier.rows, taken); + if (!monsters) return null; + return { + cols: tier.cols, rows: tier.rows, walls, spikes, monsters, stars: [], + }; +} + +function canonKey(lvl) { + const s = (arr) => arr.map(([x, y]) => keyOf(x, y)).sort().join(' '); + return `${lvl.cols}x${lvl.rows}|M:${s(lvl.monsters)}|W:${s(lvl.walls)}|X:${s(lvl.spikes)}`; +} + +// Pick 3 spread-out, ideally non-starting footprint cells -> guaranteed stars. +function chooseStars(footprint, monsters) { + const startSet = new Set(monsters.map(([x, y]) => keyOf(x, y))); + const nonStart = footprint.filter(([x, y]) => !startSet.has(keyOf(x, y))); + const pool = nonStart.length >= 3 ? nonStart : footprint; + const sorted = pool.slice().sort((a, b) => (a[1] - b[1]) || (a[0] - b[0])); + const idx = [0, Math.floor(sorted.length / 2), sorted.length - 1]; + return [...new Set(idx)].map((i) => [sorted[i][0], sorted[i][1]]); +} + +// ── Generate pool ──────────────────────────────────────────────────────────── +console.log(`[pudding] generating with seed ${SEED}…`); +const target = TIERS.reduce((t, x) => t + x.count, 0); +const buckets = TIERS.map(() => []); +const seen = new Set(); +let attempts = 0; +let solved = 0; +const startedAt = Date.now(); +const tiersFull = () => buckets.every((b, i) => b.length >= TIERS[i].count); + +while (attempts < MAX_ATTEMPTS && !tiersFull()) { + // Round-robin the tiers that still need levels so every tier gets airtime. + for (let ti = 0; ti < TIERS.length; ti++) { + if (buckets[ti].length >= TIERS[ti].count) continue; + attempts++; + if ((attempts & 0x1ff) === 0 && (Date.now() - startedAt) / 1000 > MAX_SECONDS) { + console.log('\n[pudding] time budget reached, stopping early'); + break; + } + const tier = TIERS[ti]; + const lvl = randomLevel(tier); + if (!lvl) continue; + + const ck = canonKey(lvl); + if (seen.has(ck)) continue; + seen.add(ck); + + const state = newState(lvl); + if (state.blobs.length !== tier.monsters) continue; // started adjacent -> skip + + const res = solve(state, { maxStates: SOLVE_MAX_STATES }); + if (res.moves < Math.max(2, tier.minPar) || res.moves > tier.maxPar) continue; + solved++; + + lvl.stars = chooseStars(res.footprint, lvl.monsters); + if (lvl.stars.length !== 3) continue; + lvl.par = res.moves; + buckets[ti].push(lvl); + + if (solved % 200 === 0) { + const kept = buckets.reduce((t, b) => t + b.length, 0); + process.stdout.write(`\r[pudding] attempts=${attempts} kept=${kept}/${target} `); + } + } + if ((Date.now() - startedAt) / 1000 > MAX_SECONDS) break; +} +process.stdout.write('\n'); + +// ── Assemble ordered levels (tier order, then par ascending within tier) ────── +const chosen = []; +buckets.forEach((b) => { + b.sort((p, q) => p.par - q.par); + chosen.push(...b); +}); + +const levels = chosen.map((lvl, i) => ({ + level: i + 1, + cols: lvl.cols, + rows: lvl.rows, + walls: lvl.walls, + spikes: lvl.spikes, + stars: lvl.stars, + monsters: lvl.monsters, + par: lvl.par, +})); + +const payload = { + generatedAt: new Date().toISOString(), + seed: SEED, + count: levels.length, + levels, +}; + +fs.mkdirSync(path.dirname(OUT_FILE), { recursive: true }); +fs.writeFileSync(OUT_FILE, JSON.stringify(payload, null, 2)); + +const perTier = buckets.map((b, i) => `${b.length}/${TIERS[i].count}`).join(' '); +console.log(`[pudding] attempts=${attempts} solvable=${solved}`); +console.log(`[pudding] tiers filled: ${perTier}`); +console.log(`[pudding] wrote ${levels.length} levels (par ${levels[0]?.par}..${levels[levels.length - 1]?.par}) -> ${OUT_FILE}`);