Games batch: neutral words/themes, Word Search sizes + per-word colours
From playtesting findings:
* Pools nearly doubled (115/104 → 228/201) with calm/neutral everyday words
(claps, dance, drench, beach…), not just strictly-upbeat ones — more variety,
~7-month runway. The post-solve "why" prompt reworded to fit neutral words.
* Word Search now stores one theme + word list per day; the grid is built per
request for three SIZE tiers — Small (8×8, 6 words), Medium (11×11, 9),
Large (14×14, 13). Large packs more words = a longer sit ("too fast" fix).
All sizes share the day's theme; every size still code-placed + solvable.
* Word Search themes can now be neutral everyday scenes ("Around the house",
"At the beach", "In the kitchen", "A walk outdoors", "Making music"…), not
only hopeful — same shape as the articles.
* Each found word gets its own colour from a calm palette, in the grid and its
word-list chip. Per-size local progress + best time.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1435,7 +1435,7 @@ def create_app() -> FastAPI:
|
||||
if game == "word" and variant in games.WORD_VARIANTS:
|
||||
return games.word_puzzle_response(conn, local_today(), variant)
|
||||
if game == "wordsearch":
|
||||
return games.wordsearch_response(conn, local_today())
|
||||
return games.wordsearch_response(conn, local_today(), variant)
|
||||
raise HTTPException(status_code=404, detail="no such puzzle")
|
||||
|
||||
@app.post("/api/puzzle/word/guess")
|
||||
|
||||
@@ -1,35 +1,70 @@
|
||||
{
|
||||
"5": [
|
||||
"acorn",
|
||||
"adore",
|
||||
"agile",
|
||||
"album",
|
||||
"alive",
|
||||
"amaze",
|
||||
"ample",
|
||||
"angel",
|
||||
"apple",
|
||||
"arise",
|
||||
"aroma",
|
||||
"atlas",
|
||||
"award",
|
||||
"aware",
|
||||
"balls",
|
||||
"beach",
|
||||
"beads",
|
||||
"beams",
|
||||
"beans",
|
||||
"bench",
|
||||
"berry",
|
||||
"bless",
|
||||
"bliss",
|
||||
"bloom",
|
||||
"blush",
|
||||
"boats",
|
||||
"bonus",
|
||||
"books",
|
||||
"boots",
|
||||
"brave",
|
||||
"bread",
|
||||
"brisk",
|
||||
"brook",
|
||||
"brush",
|
||||
"cabin",
|
||||
"candy",
|
||||
"chair",
|
||||
"chalk",
|
||||
"charm",
|
||||
"cheer",
|
||||
"chime",
|
||||
"choir",
|
||||
"claps",
|
||||
"clean",
|
||||
"clear",
|
||||
"climb",
|
||||
"clock",
|
||||
"cloud",
|
||||
"coast",
|
||||
"coats",
|
||||
"cocoa",
|
||||
"coins",
|
||||
"couch",
|
||||
"craft",
|
||||
"cream",
|
||||
"daisy",
|
||||
"dance",
|
||||
"dawns",
|
||||
"desks",
|
||||
"doors",
|
||||
"dream",
|
||||
"dress",
|
||||
"drift",
|
||||
"drive",
|
||||
"drums",
|
||||
"eager",
|
||||
"earth",
|
||||
"elate",
|
||||
@@ -38,32 +73,53 @@
|
||||
"fancy",
|
||||
"favor",
|
||||
"feast",
|
||||
"fence",
|
||||
"ferns",
|
||||
"field",
|
||||
"frame",
|
||||
"fresh",
|
||||
"frost",
|
||||
"fruit",
|
||||
"games",
|
||||
"gifts",
|
||||
"glade",
|
||||
"gleam",
|
||||
"glide",
|
||||
"globe",
|
||||
"glory",
|
||||
"glows",
|
||||
"grace",
|
||||
"grand",
|
||||
"grass",
|
||||
"green",
|
||||
"grins",
|
||||
"happy",
|
||||
"heals",
|
||||
"heart",
|
||||
"hikes",
|
||||
"homes",
|
||||
"honey",
|
||||
"honor",
|
||||
"hopes",
|
||||
"humor",
|
||||
"ideal",
|
||||
"jeans",
|
||||
"jewel",
|
||||
"jolly",
|
||||
"juice",
|
||||
"kites",
|
||||
"lamps",
|
||||
"laugh",
|
||||
"learn",
|
||||
"lemon",
|
||||
"light",
|
||||
"linen",
|
||||
"lucky",
|
||||
"lunch",
|
||||
"magic",
|
||||
"mango",
|
||||
"maple",
|
||||
"melon",
|
||||
"mends",
|
||||
"mercy",
|
||||
"merit",
|
||||
@@ -71,114 +127,240 @@
|
||||
"mirth",
|
||||
"music",
|
||||
"noble",
|
||||
"novel",
|
||||
"oasis",
|
||||
"ocean",
|
||||
"olive",
|
||||
"paint",
|
||||
"paper",
|
||||
"parks",
|
||||
"party",
|
||||
"paths",
|
||||
"patio",
|
||||
"peace",
|
||||
"peach",
|
||||
"petal",
|
||||
"phone",
|
||||
"photo",
|
||||
"piano",
|
||||
"plane",
|
||||
"plant",
|
||||
"plate",
|
||||
"plays",
|
||||
"pluck",
|
||||
"poise",
|
||||
"porch",
|
||||
"pride",
|
||||
"prize",
|
||||
"proud",
|
||||
"quiet",
|
||||
"quilt",
|
||||
"rainy",
|
||||
"ready",
|
||||
"relax",
|
||||
"renew",
|
||||
"rings",
|
||||
"river",
|
||||
"roads",
|
||||
"rooms",
|
||||
"roots",
|
||||
"sails",
|
||||
"salad",
|
||||
"sandy",
|
||||
"savor",
|
||||
"scarf",
|
||||
"seeds",
|
||||
"share",
|
||||
"shelf",
|
||||
"shell",
|
||||
"shine",
|
||||
"ships",
|
||||
"shirt",
|
||||
"shoes",
|
||||
"shore",
|
||||
"smile",
|
||||
"snack",
|
||||
"socks",
|
||||
"songs",
|
||||
"space",
|
||||
"spark",
|
||||
"spoon",
|
||||
"stalk",
|
||||
"stars",
|
||||
"still",
|
||||
"story",
|
||||
"study",
|
||||
"sugar",
|
||||
"sunny",
|
||||
"sweet",
|
||||
"swift",
|
||||
"swing",
|
||||
"table",
|
||||
"teach",
|
||||
"thank",
|
||||
"tides",
|
||||
"toast",
|
||||
"towel",
|
||||
"towns",
|
||||
"trail",
|
||||
"train",
|
||||
"treat",
|
||||
"trust",
|
||||
"truth",
|
||||
"tulip",
|
||||
"unity",
|
||||
"value",
|
||||
"vases",
|
||||
"vigor",
|
||||
"vines",
|
||||
"vital",
|
||||
"vivid",
|
||||
"walks",
|
||||
"watch",
|
||||
"water",
|
||||
"waves",
|
||||
"winds",
|
||||
"windy",
|
||||
"wings",
|
||||
"witty",
|
||||
"words",
|
||||
"worth",
|
||||
"youth",
|
||||
"yummy",
|
||||
"zesty"
|
||||
],
|
||||
"6": [
|
||||
"acorns",
|
||||
"adored",
|
||||
"almond",
|
||||
"anchor",
|
||||
"artist",
|
||||
"ascend",
|
||||
"aspire",
|
||||
"avenue",
|
||||
"bakery",
|
||||
"ballad",
|
||||
"banana",
|
||||
"basket",
|
||||
"beauty",
|
||||
"bestow",
|
||||
"better",
|
||||
"bottle",
|
||||
"bouncy",
|
||||
"branch",
|
||||
"breeze",
|
||||
"bridge",
|
||||
"bright",
|
||||
"bubbly",
|
||||
"butter",
|
||||
"button",
|
||||
"calmly",
|
||||
"candle",
|
||||
"canvas",
|
||||
"caring",
|
||||
"carpet",
|
||||
"carrot",
|
||||
"celery",
|
||||
"cheery",
|
||||
"cheese",
|
||||
"cherry",
|
||||
"cherub",
|
||||
"chirpy",
|
||||
"cinema",
|
||||
"clever",
|
||||
"closet",
|
||||
"cookie",
|
||||
"cotton",
|
||||
"cradle",
|
||||
"crayon",
|
||||
"dainty",
|
||||
"dancer",
|
||||
"dazzle",
|
||||
"devote",
|
||||
"dinner",
|
||||
"divine",
|
||||
"drawer",
|
||||
"dreamy",
|
||||
"drench",
|
||||
"earthy",
|
||||
"encore",
|
||||
"esteem",
|
||||
"fabric",
|
||||
"family",
|
||||
"flower",
|
||||
"fondly",
|
||||
"forest",
|
||||
"friend",
|
||||
"garden",
|
||||
"garlic",
|
||||
"gather",
|
||||
"genial",
|
||||
"gentle",
|
||||
"ginger",
|
||||
"giving",
|
||||
"glossy",
|
||||
"gloves",
|
||||
"golden",
|
||||
"grapes",
|
||||
"growth",
|
||||
"guitar",
|
||||
"hanger",
|
||||
"harbor",
|
||||
"health",
|
||||
"heaven",
|
||||
"hockey",
|
||||
"honest",
|
||||
"island",
|
||||
"jacket",
|
||||
"joyful",
|
||||
"joyous",
|
||||
"kettle",
|
||||
"kindly",
|
||||
"lagoon",
|
||||
"laurel",
|
||||
"leaves",
|
||||
"lively",
|
||||
"lovely",
|
||||
"loving",
|
||||
"marina",
|
||||
"market",
|
||||
"meadow",
|
||||
"mellow",
|
||||
"melody",
|
||||
"mended",
|
||||
"mirror",
|
||||
"mitten",
|
||||
"morale",
|
||||
"muffin",
|
||||
"museum",
|
||||
"napkin",
|
||||
"nature",
|
||||
"needle",
|
||||
"nestle",
|
||||
"nicely",
|
||||
"nuzzle",
|
||||
"orange",
|
||||
"pantry",
|
||||
"pebble",
|
||||
"pencil",
|
||||
"pepper",
|
||||
"petals",
|
||||
"pillow",
|
||||
"please",
|
||||
"plenty",
|
||||
"potato",
|
||||
"praise",
|
||||
"pretty",
|
||||
"prized",
|
||||
"purify",
|
||||
"purity",
|
||||
"puzzle",
|
||||
"quaint",
|
||||
"racket",
|
||||
"raisin",
|
||||
"ramble",
|
||||
"reborn",
|
||||
"recipe",
|
||||
"refine",
|
||||
"relish",
|
||||
"renews",
|
||||
@@ -186,37 +368,65 @@
|
||||
"rescue",
|
||||
"revive",
|
||||
"reward",
|
||||
"ribbon",
|
||||
"riddle",
|
||||
"ripple",
|
||||
"rocker",
|
||||
"sandal",
|
||||
"saucer",
|
||||
"savior",
|
||||
"savory",
|
||||
"secure",
|
||||
"serene",
|
||||
"settle",
|
||||
"shrubs",
|
||||
"simmer",
|
||||
"simple",
|
||||
"singer",
|
||||
"sketch",
|
||||
"smiled",
|
||||
"smooth",
|
||||
"soccer",
|
||||
"soothe",
|
||||
"spirit",
|
||||
"spring",
|
||||
"sprout",
|
||||
"stairs",
|
||||
"steady",
|
||||
"stream",
|
||||
"street",
|
||||
"stroll",
|
||||
"strong",
|
||||
"subway",
|
||||
"summit",
|
||||
"sunset",
|
||||
"superb",
|
||||
"supple",
|
||||
"teapot",
|
||||
"tender",
|
||||
"tennis",
|
||||
"thanks",
|
||||
"thread",
|
||||
"thrive",
|
||||
"tomato",
|
||||
"travel",
|
||||
"tunnel",
|
||||
"unfold",
|
||||
"united",
|
||||
"uplift",
|
||||
"valley",
|
||||
"valued",
|
||||
"velvet",
|
||||
"verity",
|
||||
"violin",
|
||||
"vision",
|
||||
"voyage",
|
||||
"walnut",
|
||||
"wander",
|
||||
"warmly",
|
||||
"warmth",
|
||||
"willow",
|
||||
"window",
|
||||
"wisdom",
|
||||
"wonder",
|
||||
"zenith",
|
||||
|
||||
+74
-50
@@ -58,8 +58,9 @@ def _why(client, word: str) -> str | None:
|
||||
return None
|
||||
try:
|
||||
msg = [
|
||||
{"role": "system", "content": "You write one short, warm, plain sentence (no preamble, no quotes) "
|
||||
"on why a given word is a hopeful or uplifting one to sit with."},
|
||||
{"role": "system", "content": "You write one short, warm, plain sentence (no preamble, no quotes) — "
|
||||
"a calm or gently interesting little note about the given word: what it "
|
||||
"evokes, where we meet it in everyday life, or why it's pleasant to sit with."},
|
||||
{"role": "user", "content": f"Word: {word}"},
|
||||
]
|
||||
text = (client.chat_text(msg) or "").strip().strip('"').replace("\n", " ")
|
||||
@@ -155,17 +156,31 @@ def adjudicate_word_guess(conn: sqlite3.Connection, date: str, variant: str, gue
|
||||
# are inherently visible; the play is finding them.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
WORDSEARCH_SIZE = 10
|
||||
WORDSEARCH_COUNT = 8
|
||||
_DIRS = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
|
||||
|
||||
# Curated fallbacks (all uppercase alpha, 4–8 letters) used if the LLM is thin.
|
||||
# Size tiers: bigger grid → more words → a longer sit. Built per-request from one
|
||||
# stored theme+word list, so all three sizes share the day's theme.
|
||||
WS_TIERS = {"small": {"grid": 8, "count": 6}, "med": {"grid": 11, "count": 9}, "large": {"grid": 14, "count": 13}}
|
||||
|
||||
# Curated fallbacks — calm and neutral everyday scenes, not just upbeat. ~13
|
||||
# words each (4–8 letters, uppercase) so the large tier has enough to place.
|
||||
_WS_FALLBACKS = [
|
||||
("Kindness", ["KIND", "CARE", "GIVE", "SHARE", "GENTLE", "WARMTH", "THANKS", "FRIEND"]),
|
||||
("In the garden", ["BLOOM", "PETAL", "ROOTS", "LEAF", "GARDEN", "FLOWER", "SUNNY", "SEEDS"]),
|
||||
("Quiet calm", ["PEACE", "QUIET", "STILL", "SERENE", "REST", "SOOTHE", "GENTLE", "BREATHE"]),
|
||||
("Bright skies", ["SUNNY", "CLOUD", "BREEZE", "LIGHT", "SHINE", "RAINBOW", "WARMTH", "DAWN"]),
|
||||
("Small joys", ["SMILE", "LAUGH", "CHEER", "HAPPY", "MERRY", "DANCE", "DELIGHT", "GLOW"]),
|
||||
("Around the house", ["TABLE", "CHAIR", "CLOCK", "SHELF", "COUCH", "LAMP", "PILLOW", "WINDOW",
|
||||
"CARPET", "MIRROR", "CANDLE", "KETTLE", "DRAWER"]),
|
||||
("At the beach", ["WAVES", "SHELL", "SANDY", "TIDE", "SHORE", "TOWEL", "BREEZE", "SUNSET",
|
||||
"PEBBLE", "CORAL", "OCEAN", "SAILS", "PIER"]),
|
||||
("In the kitchen", ["BREAD", "SPOON", "PLATE", "KETTLE", "FLOUR", "APRON", "WHISK", "SUGAR",
|
||||
"BUTTER", "RECIPE", "SIMMER", "PANTRY", "TEAPOT"]),
|
||||
("In the garden", ["BLOOM", "PETAL", "ROOTS", "LEAF", "GARDEN", "FLOWER", "SUNNY", "SEEDS",
|
||||
"MEADOW", "SPROUT", "HEDGE", "TROWEL", "VINES"]),
|
||||
("A walk outdoors", ["TRAIL", "MEADOW", "BROOK", "BIRDS", "BREEZE", "PEBBLE", "FOREST", "MAPLE",
|
||||
"ACORN", "STREAM", "BRANCH", "VALLEY", "PATH"]),
|
||||
("Making music", ["PIANO", "DRUMS", "CHOIR", "MELODY", "GUITAR", "VIOLIN", "SINGER", "BALLAD",
|
||||
"RHYTHM", "ENCORE", "TEMPO", "NOTES", "SONG"]),
|
||||
("Quiet calm", ["PEACE", "QUIET", "STILL", "SERENE", "REST", "SOOTHE", "GENTLE", "BREATHE",
|
||||
"CALM", "HUSH", "DRIFT", "EASE", "DREAM"]),
|
||||
("Small joys", ["SMILE", "LAUGH", "CHEER", "HAPPY", "MERRY", "DANCE", "DELIGHT", "GLOW",
|
||||
"PLAY", "GRIN", "BEAM", "GLEE", "WARM"]),
|
||||
]
|
||||
|
||||
|
||||
@@ -175,10 +190,13 @@ def _ws_propose(client) -> tuple[str, list[str]] | None:
|
||||
return None
|
||||
try:
|
||||
msg = [
|
||||
{"role": "system", "content": "You set up a calm, hopeful word search. Reply exactly as two lines:\n"
|
||||
"THEME: <2-4 word theme>\nWORDS: W1, W2, W3, W4, W5, W6, W7, W8\n"
|
||||
"Each word a single real word, 4-8 letters, UPPERCASE, related to the theme, no phrases."},
|
||||
{"role": "user", "content": "Give me one gentle, uplifting theme."},
|
||||
{"role": "system", "content": "You set up a calm word search. The theme can be uplifting OR just a "
|
||||
"pleasant everyday scene (e.g. 'Around the house', 'At the beach', "
|
||||
"'In the kitchen'). Reply exactly as two lines:\n"
|
||||
"THEME: <2-4 word theme>\nWORDS: W1, W2, ... W13\n"
|
||||
"Give 13 single real words, 4-8 letters, UPPERCASE, related to the theme, "
|
||||
"nothing negative or unpleasant, no phrases."},
|
||||
{"role": "user", "content": "Give me one calm theme."},
|
||||
]
|
||||
text = client.chat_text(msg) or ""
|
||||
theme, words = None, []
|
||||
@@ -190,15 +208,15 @@ def _ws_propose(client) -> tuple[str, list[str]] | None:
|
||||
words = [w.strip().upper() for w in re.split(r"[,\s]+", s.split(":", 1)[1]) if w.strip()]
|
||||
words = [w for w in dict.fromkeys(words) if w.isalpha() and 4 <= len(w) <= 8]
|
||||
if theme and len(words) >= 6:
|
||||
return theme, words[:WORDSEARCH_COUNT]
|
||||
return theme, words
|
||||
except Exception: # noqa: BLE001 — fall back to a curated theme
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def generate_wordsearch_puzzle(conn: sqlite3.Connection, date: str, client=None) -> dict:
|
||||
"""Ensure today's word search exists. Idempotent. Code places every word, so
|
||||
the puzzle is guaranteed solvable; only placed words are returned."""
|
||||
"""Ensure today's theme + word list exists (idempotent). The per-size grid is
|
||||
built at request time, so one LLM call serves all three sizes."""
|
||||
existing = conn.execute(
|
||||
"SELECT payload_json FROM daily_puzzles WHERE puzzle_date=? AND game='wordsearch' AND variant=''", (date,)
|
||||
).fetchone()
|
||||
@@ -207,34 +225,8 @@ def generate_wordsearch_puzzle(conn: sqlite3.Connection, date: str, client=None)
|
||||
rng = random.Random(_seed(date, "wordsearch"))
|
||||
proposed = _ws_propose(client)
|
||||
theme, words = proposed if proposed else _WS_FALLBACKS[rng.randrange(len(_WS_FALLBACKS))]
|
||||
words = [w.upper() for w in words]
|
||||
|
||||
size = WORDSEARCH_SIZE
|
||||
grid: list[list[str | None]] = [[None] * size for _ in range(size)]
|
||||
placed = []
|
||||
for word in sorted(words, key=len, reverse=True):
|
||||
for _ in range(400):
|
||||
dr, dc = rng.choice(_DIRS)
|
||||
r0, c0 = rng.randrange(size), rng.randrange(size)
|
||||
cells = [(r0 + dr * i, c0 + dc * i) for i in range(len(word))]
|
||||
if any(not (0 <= r < size and 0 <= c < size) for r, c in cells):
|
||||
continue
|
||||
if all(grid[r][c] in (None, word[i]) for i, (r, c) in enumerate(cells)):
|
||||
for i, (r, c) in enumerate(cells):
|
||||
grid[r][c] = word[i]
|
||||
placed.append({"word": word, "cells": cells})
|
||||
break
|
||||
for r in range(size):
|
||||
for c in range(size):
|
||||
if grid[r][c] is None:
|
||||
grid[r][c] = chr(65 + rng.randrange(26))
|
||||
payload = {
|
||||
"theme": theme,
|
||||
"words": sorted((p["word"] for p in placed), key=len, reverse=True),
|
||||
"grid": ["".join(row) for row in grid],
|
||||
"size": size,
|
||||
"placements": placed, # stored, not sent to the client
|
||||
}
|
||||
words = [w.upper() for w in dict.fromkeys(words) if w.isalpha() and 4 <= len(w) <= 8]
|
||||
payload = {"theme": theme, "words": words}
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO daily_puzzles (puzzle_date, game, variant, payload_json) VALUES (?, 'wordsearch', '', ?)",
|
||||
(date, json.dumps(payload)),
|
||||
@@ -246,12 +238,44 @@ def generate_wordsearch_puzzle(conn: sqlite3.Connection, date: str, client=None)
|
||||
return json.loads(row["payload_json"])
|
||||
|
||||
|
||||
def wordsearch_response(conn: sqlite3.Connection, date: str) -> dict:
|
||||
"""Public shape: theme + word list + grid. The grid is meant to be seen — the
|
||||
play is finding the words — so there's nothing to hide here (no placements)."""
|
||||
def _build_grid(words: list[str], size: int, seed: int) -> tuple[list[str], list[str]]:
|
||||
"""Place words in a size×size grid (date-seeded, deterministic) and fill the
|
||||
rest. Returns (rows, placed_words). Every returned word is genuinely placed."""
|
||||
rng = random.Random(seed)
|
||||
grid: list[list[str | None]] = [[None] * size for _ in range(size)]
|
||||
placed = []
|
||||
for word in sorted(words, key=len, reverse=True):
|
||||
if len(word) > size:
|
||||
continue
|
||||
for _ in range(400):
|
||||
dr, dc = rng.choice(_DIRS)
|
||||
r0, c0 = rng.randrange(size), rng.randrange(size)
|
||||
cells = [(r0 + dr * i, c0 + dc * i) for i in range(len(word))]
|
||||
if any(not (0 <= r < size and 0 <= c < size) for r, c in cells):
|
||||
continue
|
||||
if all(grid[r][c] in (None, word[i]) for i, (r, c) in enumerate(cells)):
|
||||
for i, (r, c) in enumerate(cells):
|
||||
grid[r][c] = word[i]
|
||||
placed.append(word)
|
||||
break
|
||||
for r in range(size):
|
||||
for c in range(size):
|
||||
if grid[r][c] is None:
|
||||
grid[r][c] = chr(65 + rng.randrange(26))
|
||||
return ["".join(row) for row in grid], placed
|
||||
|
||||
|
||||
def wordsearch_response(conn: sqlite3.Connection, date: str, size: str = "med") -> dict:
|
||||
"""Public shape for a size tier: theme + placed words + grid. The grid is meant
|
||||
to be seen — the play is finding the words — so there's nothing to hide."""
|
||||
if size not in WS_TIERS:
|
||||
size = "med"
|
||||
p = generate_wordsearch_puzzle(conn, date) # on-demand (curated fallback) if missing
|
||||
return {"game": "wordsearch", "date": date, "theme": p["theme"],
|
||||
"words": p["words"], "grid": p["grid"], "size": p["size"]}
|
||||
tier = WS_TIERS[size]
|
||||
usable = [w for w in p["words"] if len(w) <= tier["grid"]][:tier["count"]]
|
||||
grid, placed = _build_grid(usable, tier["grid"], _seed(date, "wordsearch", size))
|
||||
return {"game": "wordsearch", "date": date, "size": size, "theme": p["theme"],
|
||||
"words": placed, "grid": grid}
|
||||
|
||||
|
||||
def generate_daily_puzzles(conn: sqlite3.Connection, date: str, client=None) -> int:
|
||||
|
||||
Reference in New Issue
Block a user