Word Search bug-fixes + Codex polish
Two reported bugs, same root cause: the fixed-cell grid overflowed its wrapper on Large, so (a) the last column spilled past the border and (b) the pointer→cell math drifted across the row, recording finds "off by a letter". * Grid now uses 1fr columns with max-width = n·32px: the board grows with the grid and can never overflow (shrinks to fit a narrow phone instead). * cellAt() accounts for the grid padding/border, so selection is exact edge-to-edge. * restore() now validates each saved find against the CURRENT grid and drops any whose cells no longer spell the word — clears stale highlights if the day's puzzle changed. Codex follow-ups: * _ws_propose now requires >= large.count + 4 valid words before accepting an LLM proposal (else falls back to a curated theme), so a thin LLM result can't underfill Large. Added a thin-LLM fallback test. * Cleaned Svelte warnings: removed the now-unused .gamecard.soon CSS, added an ARIA role/label to the grid, declared gridEl with $state. Build is warning-clean. * Added a stale-load guard in WordSearchGame.load() so rapid size switches can't let an older request overwrite the newer selection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -336,3 +336,28 @@ def test_wordsearch_endpoint(tmp_path, monkeypatch):
|
||||
assert len(themes) == 1 # all sizes share the day's one theme
|
||||
# an unknown size falls back to med
|
||||
assert tc.get("/api/puzzle/wordsearch?variant=nope").json()["size"] == "med"
|
||||
|
||||
|
||||
def test_wordsearch_thin_llm_falls_back(tmp_path, monkeypatch):
|
||||
from goodnews.db import connect, init_db
|
||||
from goodnews import games
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
|
||||
def chat_text(self, msg):
|
||||
return self.text
|
||||
|
||||
c = connect(":memory:"); init_db(c)
|
||||
# Thin proposal (only ~6 valid words) must be REJECTED so Large can't underfill.
|
||||
thin = FakeClient("THEME: Thin Theme\nWORDS: ONE, TWO, FOUR, FIVE, SEVEN, EIGHT, THREE")
|
||||
p = games.generate_wordsearch_puzzle(c, "2026-07-01", client=thin)
|
||||
assert p["theme"] != "Thin Theme" # fell back to a curated theme
|
||||
large = games.wordsearch_response(c, "2026-07-01", "large")
|
||||
assert len(large["words"]) == games.WS_TIERS["large"]["count"] # still full
|
||||
|
||||
# A rich proposal (>= WS_MIN_ACCEPT valid words) is accepted.
|
||||
rich = FakeClient("THEME: Rich Theme\nWORDS: " + ", ".join(chr(65 + i) * 5 for i in range(20)))
|
||||
p2 = games.generate_wordsearch_puzzle(c, "2026-07-02", client=rich)
|
||||
assert p2["theme"] == "Rich Theme" and len(p2["words"]) >= games.WS_MIN_ACCEPT
|
||||
|
||||
Reference in New Issue
Block a user