Skip to content

sen-ltd/akari

Repository files navigation

akari

Akari (Light Up / 美術館) with a constraint-propagation solver inside: three sound rules — wall counts, mutual visibility, and illumination — propagated to a fixpoint, then a backtracking search that proves every bundled puzzle has a unique solution. Every board's clues are derived by the solver from its own wall layout, so there is no hand-authored answer key to drift. TypeScript, no runtime dependencies.

Akari

Live demo: https://sen.ltd/portfolio/akari/

Run

npm install
npm run dev      # http://localhost:5173
npm test         # 44 vitest cases
npm run build    # production build to dist/

Click a white cell to cycle bulb → mark → clear. Hint plays one guess-free deduction; Solve lights the whole board.

The rules, and why logic suffices

The grid is white cells and walls (black), some walls numbered 0..4. You drop light bulbs into white cells. A bulb lights its own cell and shoots a beam down its row and column that stops at the first wall. A solution must satisfy:

  1. Illumination — every white cell is lit.
  2. No mutual sight — no bulb sits in another bulb's beam.
  3. Wall counts — a numbered wall has exactly that many bulbs adjacent.

Each rule turns into a strict deduction — never a guess:

  • Wall count — a numbered wall whose adjacent bulbs already equal its number empties its other neighbours; if its remaining free neighbours are exactly what it still needs, they're all bulbs.
  • Mutual sight — a bulb empties every cell in its beam (and a second bulb in that beam is an immediate contradiction).
  • Illumination — a still-dark cell whose only remaining light source (itself or a single cell in its beam) is one candidate forces a bulb there.

propagate() applies all three to a fixpoint, recording the order cells were decided — which is exactly what Hint replays.

When logic stalls: search that proves uniqueness

Some boards need a guess. The backtracking solver takes the darkest cell with the fewest bulb candidates (minimum-remaining-values on the illumination constraint), tries a bulb, and lets propagation prune. After propagation every lit cell has already been emptied, so the remaining unknowns are exactly the still-dark cells — the search only ever branches where light is genuinely missing.

if (bulbs > w.n) return { ok: false, forced };        // wall over-filled
if (count === 0) return { ok: false, forced };        // a cell can never be lit
if (count === 1) set(candidate, BULB);                // its last light source

hasUniqueSolution enumerates up to two solutions; the test suite asserts every bundled puzzle stops at exactly one.

Puzzles are solver-generated, not hand-keyed

Each board is authored as nothing but a wall layout — '#' and '.'. The solver does the rest:

  1. Solve the bare layout with no clues; illumination logic alone yields a valid bulb arrangement S.
  2. Number every wall from S, and require the clued board to be unique.
  3. Greedily blank clues while the solution stays unique, leaving real deductions.

So the picture (the walls) is the single source of truth, and the answer key is the solver's own output — they cannot disagree. The tests re-solve the finished board and assert the bulbs match S:

expect(hasUniqueSolution(b.puzzle)).toBe(true);   // exactly one answer
expect(bulbsFound).toEqual(bulbsWant);            // and it's S

Same discipline as the Nonogram and Slitherlink entries: ship your levels through your solver.

src/akari.ts       — pure engine: geometry, three-rule propagation, isWin/isSolved,
                     backtracking solver, uniqueness count
src/akari.test.ts  — 44 cases (rules, contradictions, uniqueness, solver-exactness)
src/puzzles.ts     — 5 wall-layout boards, all solver-generated and verified
src/main.ts        — grid UI: clickable cells, light rendering, Hint, animated Solve

License

MIT

Links

About

Akari (Light Up / 美術館) with a constraint-propagation solver: wall-count, mutual-visibility and illumination rules to a fixpoint, plus a darkest-cell backtracking search that proves each bundled board is unique. TypeScript, no runtime deps.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors