what-town-two-words
⚠️ 🤖 this tool was vibe coded with Codex
A local, hackable town.word.word encoder inspired by what3words-style codes,
but built from your own word list and British town names.
The reason for doing this is because it's funnier with a placename!
The package includes:
- profanity blacklist filtering (not uploaded herein)
- optional CMUdict homophone rejection via
pronouncing - minimum Levenshtein distance filtering
- optional metaphone collision rejection via
Metaphone - plural/singular pair rejection
- reversible integer and approximate lat/lon grid encoding
Install
python3 -m pip install -e ".[phonetics,dev]"
The package works without optional dependencies, but CMUdict and true metaphone
checks are best with .[phonetics].
Quick Use
from what_town_two_words import LexiconBuilder, LocalWhat2Words
words = ["meeting", "penguin", "klutz", "button", "rocket", "pickle"]
towns = ["Bristol", "York", "Bath"]
lexicon = LexiconBuilder(min_levenshtein=3).build(words)
coder = LocalWhat2Words(towns=towns, words=lexicon.words)
code = coder.encode_int(12)
assert coder.decode_code(code) == 12
For coordinates:
coder = LocalWhat2Words.from_builtin()
code = coder.encode_latlon(51.5074, -0.1278, resolution_m=5000)
cell = coder.decode_latlon_code(code, resolution_m=5000)
print(code, cell.center)
Comedy Scoring Experiment
There is experimental Ollama helper code for scoring words by comic, positive, or smile-value. It was used to explore the "funny" angle of the word list, not as a required part of the build pipeline.
Run a small local model, for example:
ollama pull llama3.2:1b
Then:
from what_town_two_words import score_words_with_ollama
scores = score_words_with_ollama(
["meeting", "penguin", "klutz"],
model="llama3.2:1b",
)
The expected direction for the comedy experiment is:
meeting < penguin < klutz
Treat these scores as exploratory data. The normal build should rely on word rank, morphology, and collision filters rather than broad LLM weighting.
Data Pipeline
what-town-two-words extract-towns data/parsed/os-open-names/*.csv \
--include-multiword \
--out data/build/city-towns-villages-hamlets.txt
what-town-two-words extract-towns data/parsed/os-open-names/*.csv \
--include-multiword \
--allowed-type City \
--allowed-type Town \
--out data/build/cities-towns.txt
what-town-two-words extract-scowl data/parsed/scowl/final/english-words.* \
--out data/build/candidate_words.txt
what-town-two-words filter-kaikki \
--kaikki data/parsed/kaikki/kaikki.org-dictionary-English.jsonl.gz \
--words data/build/candidate_words.txt \
--out data/build/morph_words.txt \
--metadata-out data/build/morph_metadata.tsv \
--rejected-out data/build/morph_rejected.tsv
what-town-two-words extract-scowl-ranked \
--db data/parsed/scowl/wordlist/scowl.db \
--out data/build/word_ranks.tsv \
--max-size 60 \
--spelling B
what-town-two-words build \
--words data/build/morph_words.txt \
--ranks data/build/word_ranks.tsv \
--out data/build/filtered_words.txt \
--rejected-out data/build/rejected_words.tsv
what-town-two-words encode-int 12345 \
--words data/build/filtered_words.txt \
--towns data/build/city-towns-villages-hamlets.txt
what-town-two-words encode-latlon 51.5074 -0.1278 \
--words data/build/filtered_words.txt \
--towns data/build/city-towns-villages-hamlets.txt
Use data/build/cities-towns.txt instead for a stricter City/Town-only first
component.
This is intentionally local-first: no central lookup service, no remote API, and no baked-in global address database.
The bundled lists are only seeds for demos and tests. For fine coordinate
resolutions, use a larger filtered word list so town_count * word_count^2
comfortably exceeds the number of grid cells in your chosen bounding box.
When collision filters find similar words, the builder processes lower-ranked
words first, so the more common/preferred word is kept. SCOWL size is a coarse
rank where lower means more common. A stronger corpus-frequency rank file in
word<TAB>rank format should still be preferred whenever available.
Design Notes
This project came out of a few experiments rather than a finished theory. The current bias is deliberately collision-first:
- First component: OS Open Names settlements. The broad file keeps
City,Town,Village,Hamlet,Suburban Area, andOther Settlement; the stricter alternative keeps onlyCityandTown. - Word source: SCOWL/ESDB British words, with SCOWL
sizeused as a rough commonness rank. Lower SCOWL size wins collisions before any aesthetic preference is considered. - Morphology: Kaikki/Wiktionary JSONL can remove inflected junk more cleanly than suffix rules. The intended policy keeps noun lemmas, adjective lemmas, and gerunds/present participles; it rejects plurals, pure adverbs, pure base verbs, third-person forms, past forms, and most comparative or superlative forms.
- Collisions: words are removed if they are too close by Levenshtein distance, share a metaphone key, are CMUdict homophones when that optional dependency is installed, appear in a user-supplied blacklist, or are plural/singular collisions.
The "funny" angle was an experiment, not a production rule. Early experiments used an Ollama model to score words for fun/comical/positive energy, but broad scoring was too noisy and risked skewing the address space. The working definition of smile-value preferred, in order:
- Directly comic or playful meanings:
tickle,giggle,joke,clown,farce. - Comic mishap, awkwardness, slapstick, embarrassment, lewdness, bodily comedy,
or haplessness:
klutz,wobble,pratfall,bonk. - Words frequent in jokes, pub chat, comic scenes, innuendo, or stock comic
situations:
bar,priest,banana,trousers. - Cute, endearing, or inherently odd referents:
penguin,aardvark. - Funny sound or mouthfeel:
pickle,noodle,kazoo. - Neutral vivid concrete words:
rocket,lantern. - Abstract, administrative, technical, medical, hostile, or bleak words, unless the concept itself is comic.
This means tickle scored above pickle in the comedy experiment, while
clearly more common words still belong ahead of funnier rare words in the actual
address vocabulary.
Metadata
Release files for what-town-two-words 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| what_town_two_words-0.1.0.tar.gz | 22.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| what_town_two_words-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 43.1 kB
Release files / what_town_two_words-0.1.0.tar.gz
| Download URL | what_town_two_words-0.1.0.tar.gz |
|---|---|
| Size | 22.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
862a4b42c05a492c710734f2168e4f109e4008eac5501ed0ea8d6322aba57602
|
|
BLAKE2b-256 checksum How to use checksums |
493468d0dfee26fadcf5c0c6177984e7c6e88a0a2e1325b58dd78611e1423b82
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 8, 2026.
Transparency logRelease files / what_town_two_words-0.1.0-py3-none-any.whl
| Download URL | what_town_two_words-0.1.0-py3-none-any.whl |
|---|---|
| Size | 21.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c5b8a637c3cc9a164d3bfbe2d00d8bb25c58c620d574f0d600b1262cb9892d9a
|
|
BLAKE2b-256 checksum How to use checksums |
2a337e81979880132a9482f417855b333885477bc240f3e7870717cbceb21064
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 8, 2026.
Transparency log