Skip to main content

what-town-two-words

⚠️ 🤖 this tool was vibe coded with Codex

Cartoon die showing a house, strong arm, and carrot

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, and Other Settlement; the stricter alternative keeps only City and Town.
  • Word source: SCOWL/ESDB British words, with SCOWL size used 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:

  1. Directly comic or playful meanings: tickle, giggle, joke, clown, farce.
  2. Comic mishap, awkwardness, slapstick, embarrassment, lewdness, bodily comedy, or haplessness: klutz, wobble, pratfall, bonk.
  3. Words frequent in jokes, pub chat, comic scenes, innuendo, or stock comic situations: bar, priest, banana, trousers.
  4. Cute, endearing, or inherently odd referents: penguin, aardvark.
  5. Funny sound or mouthfeel: pickle, noodle, kazoo.
  6. Neutral vivid concrete words: rocket, lantern.
  7. 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)

Source distribution for what-town-two-words 0.1.0
File Size Uploaded
what_town_two_words-0.1.0.tar.gz 22.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for what-town-two-words 0.1.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page