# Lettercross - third-party provenance and the risk decisions behind it

The game code here is MIT, like the rest of the repository. The word list is
not ours, so it gets its own notice. Written 2026-08-24 from a sourced legal
review; the review is at
`~/.claude/reports/research-20-more-games-by-reviews-and-popularity-2026-08-23.md`'s
sibling run, and the findings below each cite what they rest on.

## The word list

`words.ts` is GENERATED by `scripts/build-lettercross-words.mjs` from **ENABLE1**.

ENABLE1 was assembled by **Alan Beale** and **M. Leo Cooper** and formally
released into the **public domain**: *"The ENABLE master word list, WORD.LST, is
herewith formally released into the Public Domain. Anyone is free to use it or
distribute it in any manner they see fit."* It exists precisely because the
tournament lists are not free.

Nothing here derives from any of these, directly or through an intermediary:

| List | Owner | Status |
|---|---|---|
| TWL / TWL06 | Hasbro / Merriam-Webster lineage | not redistributable |
| NWL - NASPA Word List | NASPA, licensed to developers | not redistributable |
| OWL | NASPA / Merriam-Webster / Hasbro | not redistributable |
| OSPD | Merriam-Webster, Hasbro copyright | not redistributable |
| SOWPODS | composite of official lists | not redistributable |
| Collins Scrabble Words | Collins / Mattel | not redistributable |

**Individual words are facts and not copyrightable. A curated LIST can be** -
selection and arrangement attract copyright in the US and database rights in the
EU. So the rule here is not "words are free", it is "use a list whose licence is
documented, and keep the notice".

**Do not swap the source for a list found on GitHub without reading its
licence.** Two commonly-suggested ones (`dwyl/english-words`,
`google-10000-english`) could not be verified to a clean chain of title. The
mirror this build fetches ENABLE from carries **no LICENSE file** and its README
misdescribes ENABLE as a version of the OSPD; the *content* was verified as
genuine ENABLE (172,823 entries, `aa` ... `zyzzyvas`), but a better-attested
source should replace it when one is to hand.

## The filtering, and the limit of it

ENABLE contains **49 of 49** obscenities and slurs probed on 2026-08-24 -
including every racial slur tested. The generator strips them, and its negative
control fails the build if they survive.

**A blocklist is not provably complete, and this one demonstrably was not**: the
first generated output still carried `abo`, `gimp`, `retard` and `spaz`. They
are blocked now. Others will be found.

That is survivable only because of the threat model, which the code states and
which any change must preserve:

> This list **validates** what a player built from their own tiles. It is never
> used to **show** a player a word. A child who spells something unpleasant from
> their own rack already knew it; the game merely declines to score it.

**The moment anything displays a dictionary word - a hint button, an AI
opponent's move, a "possible words" panel - that surface must draw from an
affirmative allowlist, not from this file.** Same reasoning as
`.claude/rules/name-pool-convention.md`: constrain the space so the bad output
is not representable, rather than filtering it afterwards.

**That allowlist exists now: `puzzleWords.ts`**, written 2026-08-25 for the
bonus screens, which are the first thing here that shows a word rather than
judging one. 346 concrete words a five-year-old could point at, authored by hand
and grouped under labels so it can be audited by reading. `puzzle-words.test.ts`
holds the three rules that keep it honest - it never imports this file, every
word in it is also in this file, and it stays big enough to ask a puzzle from.

**Four of the five bonus screens draw from it, and none of them imports this
file.** `sharedLetter.ts` (two screens), `fillGaps.ts` and `anagram.ts` all take
what they SHOW from the pool and ask `patterns.ts` what is TRUE.

**TWO modules in the round tree import this one, not one.** `patterns.ts`, which
answers "is that a word" for the three pool-fed screens, and `bonusBoard.ts`,
the crossword - the fifth screen, and the only one that shows the player no
dictionary word at all, because every letter on it came off their own deal. It
imports this file to JUDGE what they built, which is the same thing `logic.ts`
does for the main board and is the use this file has always been safe for.

That sentence said "`patterns.ts` is the only module" until 2026-08-25 and it
was false the day it was written, in the one file here whose entire job is to be
precise about this list. The safety property it was reaching for held the whole
time - nothing shows a word from here - but a NOTICE that overstates its own
boundary is worth less than one that names the exception, because the next
reader checks the sentence rather than the tree.

`rounds-are-wired.test.ts` asserts the boundary from both ends now: no round
component may import `./words`, and the SET of round modules that do must be
exactly those two - so a third arrival is a red build rather than a sentence
that quietly stops being true.

The split is not symmetrical in cost, which is why it is written down twice.
Showing from here would eventually put an ugly word in front of a five-year-old
with nobody in the loop. Judging from the pool would call a puzzle settled when
a perfectly ordinary word the player knows also fits it - the game would be
right by its own list and wrong to the person playing.

**A measured gap in the mirror, found by that second rule**: `knife` is absent
from `words.ts` while `knifed`, `knifer` and `knifes` are all present. It is one
word out of 346 checked, so this is a hole in the ENABLE copy rather than a
pattern - but it is the concrete evidence behind the paragraph above asking for a
better-attested source, and it is why the kitchen row of the allowlist says
`fork`.

The two-letter words are authored rather than filtered, for the same reason and
one more: ENABLE's 96 two-letter entries are tournament cheese, and a child told
that `at` is invalid while `za` scores is being taught the game is arbitrary.

## The game itself

Game **mechanics** are not copyrightable - 17 U.S.C. 102(b) excludes any
"procedure, process, system, method of operation". Original **expression** is:
artwork, rule text, logos, trade dress, code.

There is no reported judgment holding that a 15x15 board, a premium-square
pattern, a letter distribution or tile values are each independently protected.
But the 2008 **Scrabulous** settlement had its authors change: tiles per turn,
at least half the letter values, at least half the letter distribution, the
special-square pattern, and the centre star. Whatever that does or does not
prove about the law, it is a clear map of what the rights holder objected to -
so this game differs on every one of them, deliberately, and `logic.test.ts`
pins the letter-value divergence so it cannot drift back.

| | ours |
|---|---|
| board | 11 x 11 |
| rack | 8 at the middle level |
| letter values | 14 of 26 differ, test-pinned |
| distribution | its own, from English letter frequency |
| premium squares | a diamond and a ring, generated by a rule - no X, no edge-to-edge diagonals |
| centre | a plain start square, no multiplier, no star |
| wilds | four |

Mattel's later action against Zynga was over the **name** (SCRAMBLE WITH
FRIENDS), not the mechanics - which is the useful lesson: the name carries more
risk than the rules do.

## The name

The game shipped its first build as **Joker**, for the four wildcard tiles. It
was renamed to **Lettercross** on 2026-08-24, before any URL existed, on the
operator's call.

**`JOKER` is a live US trademark of DC Comics in International Class 28, "games
and playthings"** - the exact class this sits in. The mitigations were real: the
word is the ordinary name of a wildcard playing card, there is no character
content, and an Irish DC registration in the same class expressly excludes
ordinary playing cards. But "it is free" is not a defence to trademark use, and
Mattel pursued Zynga over a NAME rather than over mechanics.

The deciding argument was not legal exposure, it was timing. This game emits
four URLs; a rename after indexing is an SEO reset rather than an edit. On
2026-08-24 the entire cost was 15 files and no player data, because nothing had
shipped. That window does not come back.

### What the naming screen found

A public-web screen - live apps and obvious marks, NOT a trademark search -
killed four whole families before one survived. It cannot clear a name; it did
find two landmines, so treat the survivor as "no red flag", never as "cleared".

| candidate | finding | verdict |
|---|---|---|
| Wild Words / Wildwords | 3 live word games (Blue Ox, iScool, SlyPuzzle) + wildwords.us | occupied |
| Wildtile / Wild Tiles | **Bananagrams Wildtiles** - a crossword-tile game whose wilds stand in for any letter. Our mechanic, our category, a real product | occupied |
| Word Tiles / Tile Words | live Play Store app | occupied |
| Word Grid / Lettergrid | ~8 live games; Grid Words(TM) registered | occupied |
| Word Board | live Play Store app, Red Games Co. | occupied |
| Word Cross | wooden retail product + a mobile game | occupied |
| Tilecross | nearest US marks both DEAD (TILES cancelled 1998, TILE MIND abandoned 1993); "Cross Tile" is a live app | runner-up |
| **Lettercross** | zero hits as a game name anywhere in the screen | **chosen** |

Deliberately NOT chosen: **Bonus**, the Hebrew original's own name. Nobody owns
a laudatory word for games, so it is cheap on paper - and it is the one move
that turns "a crossword-tile game" into "their game". That is the Scrabulous
trigger, not the mechanic.

### The tile is a "wild", in English only

`logic.ts` calls the field `wild` and the English page says "wild". Hebrew keeps
**ג'וקר**, Spanish keeps **comodin** and French keeps **joker**, because each is
the natural word for a wildcard in that language and an invented translation
reads machine-written (`.claude/rules/game-content-template.md`). English is the
language carrying the trademark concern, and it loses the word entirely.

Every sentence that said the game was NAMED for the wilds is now false, so each
was rewritten in all four languages to a claim that is true and still carries a
number: four wilds is twice the usual two.

The engine does not care what any of this is called. The name lives in
`meta.ts` and the four content files, and nowhere else.
