Files
miti99bot/src/modules/semantle/README.md
T
tiennm99 458c979fe0 refactor(semantle,doantu): drop ConceptNet vestiges, trim wordlist API
Now that both modules run on Workers AI embeddings, drop the legacy
Word2SimError alias, the unused wordlist helpers (getLine, LINE_COUNT,
pickFromPool), and every comment/README section still describing the
removed ConceptNet backend. Fix the bge-small doc typo in semantle/index.js
and align the semantle api-client test fake-vector dim with the real
384-dim output.
2026-04-23 00:19:28 +07:00

4.0 KiB

Semantle Module

Semantic-similarity guessing game. A secret word is picked from a local curated pool and each guess is scored by cosine similarity between embedding vectors produced by Cloudflare Workers AI. Unlimited guesses per round — you play until you get the exact word (case-insensitive).

Commands

Command Visibility Description
/semantle public Show current board or submit a word guess
/semantle_giveup public Reveal the answer and end the round (next /semantle starts a fresh one)
/semantle_stats public Show wins / best count / averages

Submit with /semantle <word> (e.g. /semantle ocean). Matching is case-insensitive. Out-of-vocabulary words don't count toward the guess tally. Repeating a prior guess replies with a 🔁 already guessed notice and is ignored (no cost, no stat inflation).

Data source

Target + vocabulary: words-data.js ships the full google-10000-english list (~9.9k entries), ordered by Google Ngram frequency, normalized to lowercase and deduped but otherwise unfiltered. The same list is both the target pool and the vocabulary — so every legal guess could itself have been the answer, and OOV detection is an O(1) Set.has() with no upstream round-trip. Regenerate with node scripts/build-semantle-words.js.

Similarity: @cf/baai/bge-small-en-v1.5 text embeddings via the env.AI binding. Each in-vocab guess runs one inference call batching target + guess (384-dim vectors) and the module scores them with local cosine similarity. At ~0.0037 Neurons per guess, the Workers Free plan cap of 10k Neurons/day covers ~2.7M guesses/day.

OOV guesses short-circuit before inference — the player sees "isn't in the vocabulary" instead of a noisy subword-based score.

Architecture

  • api-client.js — Workers AI wrapper: randomWord() picks from the local pool, similarity(a, b) runs env.AI.run() and returns { in_vocab_b, similarity } along with canonical forms. UpstreamError carries status/body metadata when inference fails.
  • words-data.js — auto-generated dictionary (~9.9k entries).
  • wordlist.js — one-function module exposing randomLine().
  • state.js — KV persistence for game + stats. Target stored lowercased.
  • lookup.js — guess normalization (trim + lowercase + collapse spaces) and shape validation (/^[a-z]+$/, max 64 chars).
  • format.js — warmth-percent and emoji-bucket formatters.
  • render.js — Telegram HTML <pre> monospace board, sorted by similarity desc, capped at top 15 rows to stay under Telegram's message-length limit.
  • handlers.js — subject resolution (user in DMs, chat in groups) + the three command entry points.

Subject resolution: private chats track per-user games; groups track per-chat shared games. Mirrors loldle/wordle.

Storage

KV namespace prefix: semantle:

Key Value
game:<subject> { target, startedAt, solved, guesses[] } — active round (TTL 7 days). target stored lowercased.
stats:<subject> { played, solved, totalGuesses, bestGuessCount, lastResultAt }

Each guesses[] entry is { word, canonical, similarity }.

Config

No env vars. Model defaults to @cf/baai/bge-small-en-v1.5; override with createClient(env.AI, { model: "@cf/baai/bge-base-en-v1.5" }) in a test or alternative deploy.

Why unlimited guesses?

Classic Semantle offers up to 100s of guesses per day, and the fun is in the hunt — not the timer. Rounds stay open (TTL 7 days on KV) and skill is tracked via bestGuessCount — fewest guesses to solve across all rounds.

Credits