diff --git a/README.md b/README.md index 8e4496d..faa13c0 100644 --- a/README.md +++ b/README.md @@ -144,17 +144,24 @@ once per turn and never over text the player has already typed, so no compositio progress when it happens. The chain is listed newest first, and a finished game can be downloaded as a plain-text -transcript — the chain in playing order, who played what, and the final score. +transcript — the chain in playing order, who played what, and the final score. Each word in +the chain shows the named terms its points came from, since the client has no wordlist of its +own to re-derive that from a single number. + +A player handed a syllable nothing answers is not left to wait out the clock: a "Bí từ" button +next to the input claims the dead end, and the server settles it immediately — correctly if the +position really is one, refused with the clock still running if it is not. Every Vietnamese string lives in `web/src/lib/i18n/vi.js`, including the map from `RejectReason` to a message. That is why `ServerError.code` is a UI key such as `room_not_found` and never prose. A test walks the generated enums and fails when a value has no message, so a schema change cannot quietly ship an untranslated screen. -The rules — the chain, the clock, a dead end, elimination, scoring, room codes and reconnect -grace — are written out once on `/rules`, a single anchored page linked from the landing -screen and from a small "Luật chơi" link beside the connection badge in the game header and -the lobby. It is not a button: reading the rules is not a way to start playing. +The rules — the chain, the clock, a dead end and the claim button for it, elimination, scoring +and its visible breakdown, room codes and reconnect grace — are written out once on `/rules`, a +single anchored page linked from the landing screen and from a small "Luật chơi" link beside +the connection badge in the game header and the lobby. It is not a button: reading the rules is +not a way to start playing. The countdown is drawn against the server's clock, estimated from the `Ping`/`Pong` round trip, and settles 300ms early so the ring never claims more time than the server allows. diff --git a/docs/deployment.md b/docs/deployment.md index a11ef0a..d96bcf8 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -174,9 +174,11 @@ as not having it. The counters, all prefixed `noitu_`: connections open and total; rooms live and total, each split `bot`/`pvp`; games started and finished the same way; words submitted, accepted, and rejected by reason; eliminations by reason; chat lines; join attempts refused, by whether it was -the rate limit, an unknown code, or a full room; bot moves by difficulty; and -resumes attempted versus succeeded. None of it is read by the game itself — -it is a second write next to a decision already made, not an input to one. +the rate limit, an unknown code, or a full room; bot moves by difficulty; dead- +end claims by whether the position actually had no legal move; words reported +as real by `ReportWord`; and resumes attempted versus succeeded. None of it is +read by the game itself — it is a second write next to a decision already +made, not an input to one. Every rejected word also gets one structured log line at `Info`, `word_rejected`, carrying `reason`, `word`, `link` (the syllable it had to @@ -187,6 +189,16 @@ collect and is exactly the corpus-review question this project has open: which words players type that the dictionary does not have. Nothing else a player types is logged: not chat, not a nickname, not an accepted word. +A player who disputes a rejection this way — `ReportWord` — gets the same +treatment: one `Info` line, `word_reported`, carrying `word` (normalized the +same way), `link` (the syllable in play, empty when the report was not filed +mid-game), `mode` (`bot`/`pvp`/`none`) and `room`. Only words of at least two +syllables are recorded, and a session may file at most 20 distinct ones — never +the player's nickname, on both counts for the same reason `word_rejected` +never carries one. Between the two, this is the whole of the corpus dispute +loop: triaged by hand today, into whatever curated word list eventually +applies the fix. + ## Health and readiness `GET /healthz` returns 200 once the dictionary has loaded, for the rest of the