clone

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

main
miau-is-true / src / juice / ledger.js
ledger.js 2.2K
1/**2 * Pure presentation math for the physical rule ledger.3 *4 * The ledger uses the same physical sentence ids as the board. Keeping this5 * projection separate lets a renderer draw the tiles without owning a second6 * copy of rule timing or guessing which duplicate sentence changed.7 */89const clamp = (value, min = 0, max = 1) => Math.max(min, Math.min(max, value));1011export const LEDGER_TIMINGS = Object.freeze({12 stamp: 90,13 tileStagger: 70,14 strike: 140,15 exit: 160,16});1718const stampProgress = (at, start, index, timings) => {19 const elapsed = at - start - index * timings.tileStagger;20 if (timings.stamp <= 0) return 1;21 return elapsed <= 0 ? 0 : clamp(elapsed / timings.stamp);22};2324/**25 * Project one ledger entry at a presentation timestamp.26 *27 * `entry.phase` is either `stamping`, `active`, or `removing`. Removing28 * entries remain available through the strike and exit so a renderer can29 * show the physical sentence leaving the ledger instead of replacing it.30 */31export function projectLedgerEntry(entry, at, timings = LEDGER_TIMINGS) {32 const ids = [...(entry.ids || [])];33 if (entry.phase === 'removing') {34 const strikeProgress = timings.strike <= 0 ? 1 : clamp((at - entry.start) / timings.strike);35 const exitProgress = timings.exit <= 0 ? 1 : clamp((at - (entry.start + timings.strike)) / timings.exit);36 return {37 key: entry.key,38 ids,39 tokens: [...(entry.tokens || [])],40 phase: exitProgress >= 1 ? 'exited' : 'removing',41 stampProgress: ids.map(() => 1),42 strikeProgress,43 exitProgress,44 visible: exitProgress < 1,45 };46 }47 if (entry.phase === 'active') {48 return {49 key: entry.key,50 ids,51 tokens: [...(entry.tokens || [])],52 phase: 'active',53 stampProgress: ids.map(() => 1),54 strikeProgress: 0,55 exitProgress: 0,56 visible: true,57 };58 }59 const progresses = ids.map((_, index) => stampProgress(at, entry.start, index, timings));60 const complete = progresses.every(progress => progress >= 1);61 return {62 key: entry.key,63 ids,64 tokens: [...(entry.tokens || [])],65 phase: complete ? 'active' : 'stamping',66 stampProgress: progresses,67 strikeProgress: 0,68 exitProgress: 0,69 visible: true,70 };71}