- commit
- 7c5c66afec8c7c57bb4ddeddf8ab125683a6136d
- parent
- 551e53db3b125f10aafd9d6eb6ae85fe6872133f
- author
- Dami <dami.etoile@gmail.com>
| M | assets/ | 2 | ++ |
| M | islands/ | 2 | +- |
| M | lib/ | 22 | +++++++++++++++++----- |
| M | routes/ | 4 | ++-- |
| M | routes/ | 2 | +- |
| M | static/ | 2 | +- |
| M | tests/ | 26 | +++++++++++++++++++++++++- |
@@ -59,7 +59,7 @@ export default function ShikakuGame({ puzzle }: ShikakuGameProps) {
5959 rectangles.value = selectionsToRectangles(saved, puzzle);6060 status.value = saved.status === "solved" ? "solved" : "in_progress";6161 message.value = saved.status === "solved"62- ? "Solved on this device. Tap a placed region to remove it."62+ ? "Reset or play another."6363 : "Resumed from local storage.";6464 }6565 hydrated.value = true;@@ -101,11 +101,23 @@ export function isPuzzleSolved(
101101 puzzle: ShikakuPuzzle,102102 placed: PlacedRectangle[],103103 ): boolean {104- if (!puzzle.solution) return false;105- if (placed.length !== puzzle.solution.length) return false;104+ if (placed.length !== puzzle.givens.length) return false;106105 107- const actual = placed.map(canonicalizeRectangle).sort();108- const expected = puzzle.solution.map(canonicalizeRectangle).sort();106+ const covered = new Set<string>();107+ const accepted: PlacedRectangle[] = [];109108 110- return actual.every((key, index) => key === expected[index]);109+ for (const rectangle of placed) {110+ const validation = validateRectanglePlacement(puzzle, rectangle, accepted);111+ if (!validation.ok) return false;112+ if (validation.given.id !== rectangle.clueId) return false;113+114+ const cells = getRectangleCells(rectangle);115+ for (const cell of cells) {116+ covered.add(cell);117+ }118+119+ accepted.push(rectangle);120+ }121+122+ return covered.size === puzzle.width * puzzle.height;111123 }@@ -33,11 +33,11 @@ export default define.page<typeof handler>(({ data }) => {
3333 <div class="shikaku-play-header__row">3434 <div class="shikaku-play-day-nav shikaku-play-day-nav--with-home">3535 <a36- class="shikaku-play-nav shikaku-play-nav--home-text"36+ class="shikaku-play-nav shikaku-play-nav--icon"3737 href="/"3838 aria-label="Back home"3939 >40- Home40+ ⌂4141 </a>4242 <a4343 class="shikaku-play-nav shikaku-play-nav--icon"@@ -15,7 +15,7 @@ export default define.page(function Home() {
1515 <section class="shikaku-home__hero">1616 <div class="shikaku-home__hero-copy">1717 <div class="shikaku-home__eyebrow">SEED {latest}</div>18- <h1 class="shikaku-home__title">SHIKAKU KEEP</h1>18+ <h1 class="shikaku-home__title">RECT.RUN</h1>1919 <div class="shikaku-home__speech-stack" aria-label="Puzzle pitch">2020 <p class="shikaku-home__speech">Daily grid.</p>2121 <p class="shikaku-home__speech shikaku-home__speech--accent">@@ -5,7 +5,7 @@
55 aria-labelledby="title desc"66 >77 <title id="title">rect.run icon</title>8- <desc id="desc">A pixel-style castle keep above a puzzle grid.</desc>8+ <desc id="desc">A pixel-style rect.run icon above a puzzle grid.</desc>99 <rect width="128" height="128" fill="#122411" />1010 <rect1111 x="10"@@ -136,8 +136,32 @@ Deno.test("validateRectanglePlacement rejects overlap and multiple givens", () =
136136 );137137 });138138 139-Deno.test("isPuzzleSolved compares placed rectangles to solution", () => {139+Deno.test("isPuzzleSolved accepts any valid full tiling, not only the planned one", () => {140140 assertEquals(isPuzzleSolved(samplePuzzle, samplePuzzle.solution ?? []), true);141+142+ const multiSolutionPuzzle: ShikakuPuzzle = {143+ id: "multi-solution",144+ date: "2026-04-16",145+ width: 2,146+ height: 2,147+ difficulty: "easy",148+ seed: "multi-solution",149+ givens: [150+ { id: 0, row: 0, col: 0, value: 2 },151+ { id: 1, row: 1, col: 1, value: 2 },152+ ],153+ solution: [154+ { clueId: 0, row: 0, col: 0, width: 2, height: 1, value: 2 },155+ { clueId: 1, row: 1, col: 0, width: 2, height: 1, value: 2 },156+ ],157+ };158+159+ const alternateValidTiling: PlacedRectangle[] = [160+ { clueId: 0, row: 0, col: 0, width: 1, height: 2, value: 2 },161+ { clueId: 1, row: 0, col: 1, width: 1, height: 2, value: 2 },162+ ];163+164+ assertEquals(isPuzzleSolved(multiSolutionPuzzle, alternateValidTiling), true);141165 assertEquals(142166 isPuzzleSolved(samplePuzzle, [143167 { clueId: 0, row: 0, col: 0, width: 4, height: 1, value: 4 },