# Research: noitu landscape scan — competitors, dictionary disputes, small-scale server practice Prior reports (`research-260904-1058-noi-tu-game.md`, `research-2609{07,08,10}-*`) already cover rules engine, bot AI, Vietnamese normalization, and an exhaustive dictionary-source comparison (Wiktionary/undertheseanlp/Hoàng Phê). This report does not repeat those; it adds competitor feature/complaint evidence, dispute-handling patterns, small-scale server practice, and ecosystem notes. ## Summary - Direct web competitors exist: `noitu.fun` and `wordfight.online` — both have ranked/Elo modes, neither has noitu's spectator-on-elimination or bot-difficulty design. - The #1 recurring complaint across mobile nối từ apps is dictionary coverage ("từ điển quá ít"), not rules or UX — validates prior dictionary-focused work as the highest-leverage area. - Every competitor with a fixed wordlist ships a manual "report word" channel; none of the ones found do live player voting mid-game. One Discord bot layers a separate community-wordlist repo on top of a base corpus — closest precedent to an allowlist overlay. - Ranked/Elo matchmaking with wait-time-based window widening is the standard pattern for small pools (chess.com/lichess-style); directly applicable to a 2-4 player room-code game with low CCU. - SQLite-as-embedded-store (not just read-only dictionary) is a well-documented pattern for adding room/stat persistence without a DB service — fits the existing `modernc.org/sqlite` dependency. - `protovalidate` (Apache-2.0, Go+JS via `protovalidate-es`) is a concrete, license-compatible fit for schema-level proto validation, complementing (not replacing) noitu's Vietnamese-specific sanitization. - Card-mechanic "Extended Mode" (skip/reverse/swap turn) and Elo ranked ladders are the clearest "features players expect but noitu lacks" signal found. - Casual house-rule turn timers run 5-10s vs noitu's 30s default — worth noting as a design choice, not a defect. - No changes found to Svelte 5/adapter-static or buf/protobuf-es that are breaking or urgent for noitu's current setup. ## 1. Competing implementations — rules, features, complaints ### Direct web competitors - **noitu.fun** — solo vs AI, "Ranked" mode with a leaderboard, and group/room-code mode (closest analog to noitu's own room model). Also ships non-chain minigames under one brand: "Extended Mode" adds card mechanics (reverse turn, assign-next-responder, swap opponent's word, skip turn), plus a trivia-style "Character Arena" and a Wheel-of-Fortune-style "Letter Assembly" mode. Rules text explicitly bans slang/shorthand and misspelled tone marks. [noitu.fun] - **wordfight.online** — 3 modes: English word chain (shows IPA + Vietnamese gloss per word), Vietnamese word chain (2-syllable, same rule as noitu), and a separate "King of Vietnamese" word-puzzle mode. Turn timer 10-30s. **Elo-based leaderboard with skill-matched opponents**, progressive difficulty across up to 999 levels, 3-star mastery rating per level, replay-resistant randomized word lists, private shareable room links. [wordfight.online] - Neither site documents spectator viewing of eliminated players, reconnect grace windows, or per-seat colored room chat — these look like noitu differentiators, not table stakes to add. ### Discord bots (adjacent platform, same game) - **minhqnd/Noi-Tu-Discord** ("Moi Nối Từ") — bot-vs-player and PvP-with-bot-as-referee modes, DM play, `/leaderboard` and `/stats` (streak, personal record, wins), `/tratu` dictionary lookup backed by 357k+ definitions via `dict.minhqnd.com`, emoji-reaction feedback per submission (✅ correct / ❌ can't chain / 🔴 duplicate / ⚠️ format error). [github.com/minhqnd/Noi-Tu-Discord] - **lvdat/bot-noi-tu** ("RaHub") — dictionary sourced from `undertheseanlp/dictionary` plus a **separate community-contribution repo** (`phobo-contribute-words`) merged in at build/runtime. This is the clearest real-world precedent for a base-corpus + community-overlay split (see §2). [github.com/lvdat/bot-noi-tu] ### Mobile apps — features and complaints - Galaxy Team "Nối từ - Word Chain": Survival / Time-Limited(3min) / Level-Challenge-vs-AI modes. [play.google.com/.../com.galaxteam.wordchain] - "Nối từ tiếng Việt" (iOS, MWM/id6449588406): 3 modes — Challenge (timed rounds + score threshold), Duel (2p turn-based), Arena (4p elimination — same shape as noitu's 2-4 elimination room), Game Center leaderboard integration, scoring by chain-word character count. **Rating 1.2/5 (19 reviews)**, dominant complaint is a too-small dictionary ("từ điển quá ít") rejecting valid words and occasionally accepting non-Vietnamese junk; developer response cites Vietnamese's richness as an excuse rather than fixing it. [apps.apple.com/vn/.../id6449588406] - Cross-app pattern found via search: apps expose an in-app "Report Error" / "báo lỗi" flow for a rejected word rather than any live dispute; devs publicly acknowledge coverage gaps as unavoidable rather than committing to fixes. [search results, ktcc.blog context] ### Rule-variant survey (Vietnamese how-to-play content) - Casual/offline house rules commonly run **5-10s per turn** (ktcc.blog), notably faster than noitu's 30s default — a deliberate design choice for noitu given typed Vietnamese input (Telex/VNI composition), not evidence of a gap. - "Từ hiểm" (dead-end/trap words — syllables that start almost nothing) is a named, well-understood strategic concept across the community, not something apps hide. This affirms noitu's design of showing the leftover words in a dead-end position rather than treating it as a bug. - Recurring dispute across communities: "từ ghép" (compound) vs "từ đơn" (single-syllable) and which authority resolves disputes; common advice is to pre-agree a single reference dictionary (often citing the Institute of Linguistics / Hoàng Phê) as sole arbiter — already covered in `research-260910-0939-hoang-phe-in-noitu.md`. ### Implications for noitu - Dictionary coverage is the make-or-break axis for player satisfaction industry-wide; any roadmap item competing for effort against dictionary work should clear a high bar. - A lightweight "report this rejection" action (word + turn context) is standard and cheap; noitu has no such flow today per the README. Low-risk, high-precedent addition. - Elo/ranked ladder and light "chaos" mechanics (skip/reverse/swap) are the two concrete feature gaps versus the nearest web competitors, if PvP breadth is a goal. ## 2. Dictionary disputes — patterns beyond corpus choice (Corpus/source comparison already exhaustively covered in prior reports; this is new: process patterns for *handling disputes at runtime*, and licensing-separation precedent.) - **Static base + community overlay repo** (lvdat/bot-noi-tu + phobo-contribute-words): base dictionary from a versioned upstream corpus, disputed/missing words tracked in a *separate* repository merged at build or load time. Mirrors an allowlist-overlay design: keeps the overlay's provenance and license distinct from the base corpus, which matters for noitu's Apache-2.0-code / CC BY-SA-4.0-data split — an overlay of player-submitted words would need its own license decision (CC BY-SA if derived/mixed with Wiktionary text, or a fresh license if pure word-list-no-definition additions, which are likely uncopyrightable facts). [github.com/lvdat] - **Scrabble's live challenge**: any player may challenge a just-played word before the next turn; in double-challenge scoring the *loser* of the challenge (wrong challenger or wrong player) loses their turn — a real cost that discourages frivolous challenges. [en.wikipedia.org/Challenge_(Scrabble)] - **Words With Friends' approach**: no live human challenge at all — the client just keeps resubmitting until the server's own dictionary accepts something; the dictionary itself (Zynga's ENABLE-derived ~173k word list, deliberately different from tournament Scrabble lists) is the sole and silent arbiter. [wordfinder.yourdictionary.com; word.tips] - No evidence found of any nối từ implementation running **live in-match player voting** on a disputed word (i.e., other seated players vote accept/reject before the turn resolves). The Scrabble-style post-hoc challenge with a real cost, or WWF's silent-server-arbiter, are the two patterns actually used in the wild — an async "report → maintainer/community review → next dictionary build" queue (closer to WWF, deferred) is simpler to implement correctly than a live vote and has no real-time consistency/latency problem to solve. ### Implications for noitu - If a self-serve "report word" pipeline is ever built, model it as WWF/lvdat's deferred pattern (report now, reviewed and merged into a future `noitu.db` build) rather than live voting — no new consensus/anti-brigading problem, and it fits the existing single-file, rebuilt-not-mutated dictionary architecture. - Any player-submitted word overlay must get its own explicit license decision before merging with the CC BY-SA data tree; do not assume submitted words inherit Wiktionary's license by default — bare word forms without wiktionary-derived definitions are likely factual/uncopyrightable, but a submission that carries a definition text is a new derivative work needing its own care. ## 3. Small-scale realtime game server practice - **Matchmaking for small pools**: standard pattern (chess.com/lichess-style, per Awesomenauts postmortem) is `window = base_window + widen_rate × seconds_waited`, anchored on whichever queued player has waited longest, capped at a max window — trades match quality for queue time as the pool thins out. Directly portable to a "quick match" queue for noitu's current room-code-only online mode. [joostdevblog.blogspot.com; medium.com/@deephavendatalabs] - Batch pooling ("gather for ~1-2 min then match everyone at once") is the standard fallback for genuinely tiny regional pools, cited as improving match quality up to ~300 concurrent queuers — likely overkill for noitu's expected scale, worth knowing only if online play grows. - **Persistence without a DB service**: SQLite is a well-documented fit for exactly this ("zero-configuration... ideal for small to medium services," pure-Go `modernc.org/sqlite` needs no CGO, single static binary). Patterns seen: JSON-blob-per-row for full game/room state (simplest, fine at noitu's scale), or row-per-update table if write volume grows. A SQLite changelog-table + trigger + Go worker pattern exists for push-driven updates but is unnecessary complexity for a single authoritative in-process game engine like noitu's. noitu already depends on `modernc.org/sqlite` for the dictionary — reusing it (a second read-write DB file, or a second schema in-process) for room/series-stat persistence across restarts costs no new dependency. [oneuptime.com/.../sqlite-go; gist.github.com/rusco] - **Rate limiting / abuse**: `golang.org/x/time/rate`'s token-bucket `Limiter` (Allow/Wait/Reserve) is the standing idiomatic choice for per-connection message throttling in a Go WebSocket server; described as covering "90% of cases" without needing Redis-backed distributed limiting, which is irrelevant to a single-binary deployment. Combine with a concurrent-connection cap per IP. [dev.to/lovestaco; oneuptime.com/.../websocket-rate-limiting] - **Anonymous vs identity**: industry best practice (PlayFab, AWS Games Industry Lens) is zero-friction anonymous login by default, with an *optional* upgrade path to a recoverable identity later — matches noitu's current no-account, nickname-only model; nothing here argues for forcing accounts. Reconnect best practice: identity (not the transient socket/session token) is the thing that says two connections are the same player, and the server-issued session token should stay stable across a reconnect within the grace window — consistent with noitu's existing disconnect-grace-window design per its README. [learn.microsoft.com/playfab; docs.aws.amazon.com] - **Observability**: not surfaced as a distinct concern in results beyond generic Prometheus/metrics advice seen in one unrelated Go arena-server repo (`nguyenbatam/arena_game_server`, Redis-backed, 10k-CCU target) — that project's scale and dependency footprint (Redis, k8s) is not a fit for noitu's single-binary constraint; flagging only because Prometheus text-format `/metrics` next to the existing `/healthz` endpoint is a low-cost, dependency-light addition if observability becomes a goal. ### Implications for noitu - A quick-match queue (as opposed to room-code-only) is implementable with the widen-by-wait-time formula and no new infrastructure; it's a pure in-memory queue in the existing Go process. - Room/series persistence across restarts (currently implied in-memory, since README describes rooms closing after 10 idle minutes with no persistence mention) could reuse the SQLite dependency already in the binary rather than adding Redis/Postgres. - `x/time/rate` per-connection + a connection-count cap per IP is the standard, low-effort answer to WS abuse; no evidence any competitor does more than this at this scale. ## 4. Ecosystem notes (brief) - **SvelteKit/Svelte 5 + adapter-static**: no breaking change found for 2026. One caution surfaced repeatedly in 2026 guides: module-scope runes and `$derived` wrapping a store's `$` auto-subscription can break specifically under prerendering/hydration; recommended fix is keeping runes component-local and bridging global stores explicitly via `$effect`. Only relevant if noitu's frontend uses module-level `$state`/`$derived` for anything beyond the documented client-owned state (theme, personal best, input box) — worth a quick grep, not a redesign. [svelte.dev/docs/kit/adapter-static; khromov.se] - **buf/protobuf-es**: `protovalidate` (Apache-2.0) plus `protovalidate-es` gives schema-level field constraints (e.g. string length/pattern) enforced identically from the same `.proto` file on both the Go and JS generated types — same "one wire contract" philosophy noitu already uses for message shape. Could formalize bounds already enforced by hand (e.g. nickname length) as proto annotations instead of duplicated Go+JS logic — but noitu's nickname sanitization (control chars, whitespace collapse, combining-mark cap) is Vietnamese-text-specific logic CEL/protovalidate doesn't replace; treat it as a complement for simple bounds, not a replacement for sanitization. [github.com/bufbuild/protovalidate] - No 2026 buf CLI or `protoc-gen-es`/`@bufbuild/protobuf` breaking-change advisory found that affects noitu's current generation setup. ## Unresolved questions - Does noitu currently persist rooms/series scores across a server restart at all, or is everything in-memory? README doesn't say; determines whether §3's SQLite-persistence point is a new feature or filling a known gap. - Would a "report rejected word" flow be worth the review-queue maintenance cost given the project has one apparent maintainer, versus just improving corpus coverage directly (already the subject of 5 prior reports)? - Is competitive/ranked PvP actually a goal for noitu, or is vs-bot + casual room-code play the intended scope? The Elo/quick-match findings only pay off if ranked play is in scope. - Could not verify `phobo-contribute-words`' actual review/merge workflow or license (GitHub fetch returned only nav chrome, no README text) — the "allowlist overlay" characterization is inferred from the bot's own README description, not confirmed from that repo directly. Status: DONE Summary: Competitor scan of noitu.fun, wordfight.online, and 4 Discord/mobile nối từ implementations plus small-scale server-practice research (matchmaking, SQLite persistence, rate limiting) written to the report; dictionary coverage confirmed as the industry's #1 complaint, and a base+overlay community-wordlist precedent and protovalidate found as new, concrete inputs. Concerns: none blocking; one item (phobo-contribute-words internals) unverified due to GitHub README not rendering through WebFetch — noted in Unresolved questions.