diff --git a/frontend/src/lib/components/WordSearchGame.svelte b/frontend/src/lib/components/WordSearchGame.svelte index 5ba808a..349b28d 100644 --- a/frontend/src/lib/components/WordSearchGame.svelte +++ b/frontend/src/lib/components/WordSearchGame.svelte @@ -22,7 +22,8 @@ let ready = $state(false); let okFlash = $state(false); let copied = $state(false); - let gridEl; + let gridEl = $state(null); + let loadSeq = 0; const n = $derived(grid.length); const stateKey = $derived(`goodnews:wordsearch:${size}:${date}`); @@ -42,17 +43,21 @@ }); async function load() { + const seq = ++loadSeq; // stale-load guard for rapid size switches loading = true; ready = false; foundWords = []; sel = []; resultMs = 0; startTime = 0; try { const p = await getJSON('/api/puzzle/wordsearch?variant=' + size); + if (seq !== loadSeq) return; // a newer size was selected — abandon theme = p.theme; words = p.words; grid = p.grid; date = p.date; restore(); if (!startTime) startTime = Date.now(); try { best = JSON.parse(localStorage.getItem(bestKey) || '0'); } catch { best = 0; } } catch { + if (seq !== loadSeq) return; theme = ''; words = []; } + if (seq !== loadSeq) return; loading = false; requestAnimationFrame(() => (ready = true)); } @@ -61,9 +66,14 @@ try { const s = JSON.parse(localStorage.getItem(stateKey) || 'null'); if (s && Array.isArray(s.foundWords)) { - foundWords = s.foundWords; + // Keep only finds whose stored cells still spell their word in the CURRENT + // grid — guards against stale highlights if the day's puzzle changed. + const valid = s.foundWords.filter((fw) => + fw && Array.isArray(fw.cells) && words.includes(fw.word) && + fw.cells.map(([r, c]) => (grid[r] && grid[r][c]) || '').join('') === fw.word); + foundWords = valid; startTime = s.startTime || 0; - resultMs = s.ms || 0; + resultMs = valid.length === words.length ? (s.ms || 0) : 0; } } catch { /* ignore */ } onstatus?.(summary()); @@ -77,9 +87,10 @@ function cellAt(e) { const rect = gridEl.getBoundingClientRect(); - const cw = rect.width / n; - const c = Math.min(n - 1, Math.max(0, Math.floor((e.clientX - rect.left) / cw))); - const r = Math.min(n - 1, Math.max(0, Math.floor((e.clientY - rect.top) / cw))); + const pad = 7; // grid padding (6) + border (1) + const cw = (rect.width - 2 * pad) / n; // even 1fr columns share the inner width + const c = Math.min(n - 1, Math.max(0, Math.floor((e.clientX - rect.left - pad) / cw))); + const r = Math.min(n - 1, Math.max(0, Math.floor((e.clientY - rect.top - pad) / cw))); return [r, c]; } @@ -144,6 +155,7 @@
Today’s theme{theme}