hangman: persist last 30 words to disk to prevent duplicates
Lint / Shell (shellcheck) (push) Successful in 9s
Lint / JS (eslint) (push) Successful in 6s
Lint / Python (ruff) (push) Successful in 5s
Lint / Python deps (pip-audit) (push) Failing after 43s
Lint / Secret scan (gitleaks) (push) Successful in 5s

Mirrors riddle/trivia cache pattern: loads hangman_cache.json on startup,
appends each new word, caps at 30, saves after each game. Recent words
are passed to the model prompt to avoid repeats.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 15:33:54 -04:00
parent 6eb435010d
commit 22313b4a41
+32 -1
View File
@@ -1384,12 +1384,39 @@ def _hangman_board_html(game: dict, status_line: str = "") -> tuple[str, str]:
return plain, html return plain, html
_HANGMAN_RECENT_MAX = 30
_HANGMAN_CACHE_FILE = Path("hangman_cache.json")
def _load_hangman_cache() -> list[str]:
try:
data = json.loads(_HANGMAN_CACHE_FILE.read_text())
return data.get("words", [])
except Exception:
return []
def _save_hangman_cache(words: list[str]) -> None:
try:
_HANGMAN_CACHE_FILE.write_text(json.dumps({"words": words}, indent=2))
except Exception as e:
logger.warning("Failed to save hangman cache: %s", e)
_hangman_recent: list[str] = _load_hangman_cache()
async def _generate_hangman_word() -> dict | None: async def _generate_hangman_word() -> dict | None:
avoid_clause = (
" Do NOT use any of these recently used words: "
+ ", ".join(f'"{w}"' for w in _hangman_recent[-20:])
+ "."
) if _hangman_recent else ""
system_msg = ( system_msg = (
"You are a hangman game generator. Always respond with ONLY a JSON object — no markdown, no explanation. " "You are a hangman game generator. Always respond with ONLY a JSON object — no markdown, no explanation. "
'Format: {"word": "example", "hint": "short category or hint"}' 'Format: {"word": "example", "hint": "short category or hint"}'
) )
user_msg = "Pick a common English word between 5 and 8 letters (lowercase letters only, no hyphens or spaces) and give a short hint." user_msg = f"Pick a common English word between 5 and 8 letters (lowercase letters only, no hyphens or spaces) and give a short hint.{avoid_clause}"
for attempt in range(2): for attempt in range(2):
try: try:
timeout = aiohttp.ClientTimeout(total=60) timeout = aiohttp.ClientTimeout(total=60)
@@ -1419,6 +1446,10 @@ async def _generate_hangman_word() -> dict | None:
word = parsed.get("word", "").lower().strip() word = parsed.get("word", "").lower().strip()
hint = parsed.get("hint", "").strip() hint = parsed.get("hint", "").strip()
if word.isalpha() and 5 <= len(word) <= 8 and hint: if word.isalpha() and 5 <= len(word) <= 8 and hint:
_hangman_recent.append(word)
if len(_hangman_recent) > _HANGMAN_RECENT_MAX:
_hangman_recent.pop(0)
_save_hangman_cache(_hangman_recent)
return {"word": word, "hint": hint} return {"word": word, "hint": hint}
logger.warning("hangman: validation failed (attempt %d): word=%r hint=%r", attempt + 1, word, hint) logger.warning("hangman: validation failed (attempt %d): word=%r hint=%r", attempt + 1, word, hint)
except Exception as e: except Exception as e: