The game never volunteers that

The Norwegian AI opponent played a profanity as its move. The fix keeps every word valid for players and just stops the game from choosing one on its own.

A report came in that the Norwegian AI opponent had played a profanity as its move. The dictionary that word came from has to stay complete, since removing it would also make it invalid for a player who genuinely typed it, so the fix couldn’t touch the dictionary at all. The locked design calls it the Wordle model: the game never volunteers a profanity as its own choice, but nothing about what a player can type changes.

That framing turned the fix into a selection filter rather than a data problem, and a quick inventory found exactly two places in the whole codebase where the game chooses a dictionary word to display on its own: the AI opponent’s move, and the PvP timeout fallback that plays a word for a player who ran out the clock. Both already funneled through the same word-candidate service, so the filter only needed one chokepoint, not two.

The blocklist itself is vendored, not generated: a snapshot of a public offensive-word list per language, plus a repo-owned additions file as the place to add anything the snapshot misses, compiled straight into the server bundle via a raw-text import at build time rather than read from disk or a database table at runtime, so an edit to either file ships on the next ordinary deploy with no migration and no rebuild. A word gets filtered if it matches or contains a blocklist entry in its own language, since Norwegian compounds words freely enough that exact matching alone would miss the profanity glued onto an otherwise ordinary word; over-flagging costs almost nothing under the Wordle model, since a flagged word just never gets offered by the AI and stays fully playable if someone types it themselves.

Left out on purpose: a player’s own initials or display name can carry exactly the same kind of profanity, visible to the other player rather than chosen by the game, but that’s content moderation with the opposite tradeoff, a false positive there blocks a real person rather than an AI, and it stayed a separate issue rather than folding two different problems into one fix.