16 KiB
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.funandwordfight.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/sqlitedependency. protovalidate(Apache-2.0, Go+JS viaprotovalidate-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,
/leaderboardand/stats(streak, personal record, wins),/tratudictionary lookup backed by 357k+ definitions viadict.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/dictionaryplus 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.dbbuild) 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/sqliteneeds 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 onmodernc.org/sqlitefor 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-bucketLimiter(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/metricsnext to the existing/healthzendpoint 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/rateper-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
$derivedwrapping 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/$derivedfor 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) plusprotovalidate-esgives schema-level field constraints (e.g. string length/pattern) enforced identically from the same.protofile 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/protobufbreaking-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.