"""Build the guess and answer lists from two permissively-licensed sources. Deterministic and offline: the inputs are committed beside this script, so a rebuild on any machine reproduces the shipped JSON byte for byte. See PROVENANCE.md for the licences and why the lists are constructed this way rather than copied from the original game. """ from __future__ import annotations import json import re from pathlib import Path HERE = Path(__file__).parent # Words we never make the hidden answer. Guesses stay permissive — the original # game accepts these too — but no boardroom demo should ever reveal one as the # solution. Deliberately short and hand-curated: a large imported blocklist # would drag in its own licence and its own false positives. BLOCKED = { "bitch", "boner", "chink", "cocks", "coons", "crack", "cunts", "dagos", "dicks", "dildo", "dykes", "fagot", "farts", "gooks", "gypsy", "harem", "hussy", "incel", "junky", "kikes", "lynch", "micks", "nazis", "negro", "nonce", "pussy", "queer", "raped", "rapes", "retch", "sperm", "spick", "spics", "tards", "titty", "twats", "wench", "whore", "wogs", } FIVE = re.compile(r"^[a-z]{5}$") def five_letter(path: Path) -> set[str]: """Every five-letter lowercase ASCII word in a whitespace/quote-delimited file.""" raw = path.read_text(encoding="utf-8", errors="ignore") return {w for w in raw.replace('"', " ").replace(",", " ").lower().split() if FIVE.match(w)} def main() -> None: guesses = five_letter(HERE / "wordnik-20210729.txt") common = five_letter(HERE / "scowl-wamerican-2020.12.07.txt") # Answers are the intersection: a word must be in Wordnik (so it is a real # headword) AND in SCOWL's common-American tier (so a non-specialist has # plausibly met it). The intersection is what makes this list ours — it is # derived from two permissive sources by a stated rule, not copied from # anyone's editorial selection. answers = sorted((guesses & common) - BLOCKED) # Every answer must also be guessable, or the game is unwinnable. assert set(answers) <= guesses, "answer list escaped the guess list" out = HERE (out / "guesses.json").write_text(json.dumps(sorted(guesses)) + "\n") (out / "answers.json").write_text(json.dumps(answers) + "\n") print(f"guesses={len(guesses)} answers={len(answers)} blocked={len(BLOCKED & (guesses & common))}") if __name__ == "__main__": main()