feat(game): void unreplayable games as a draw instead of erroring
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 46s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m13s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 46s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m13s
A committed move that becomes illegal under a tightened rule (the single-word connectivity fix) makes engine replay fail, which left such games unopenable — an empty screen and an 'illegal play' error. Now the first open closes the game gracefully as a draw (engine.EndAborted -> end_reason 'aborted', no winner), preserves the journal, and surfaces an impersonal organizer note at the end of the move history and in the GCG export. - engine: EndAborted + Abort() (draw, no rack adjustment; winner -1). - service: replay aborts on ErrIllegalPlay; liveGame persists the void once (lazy, on open); GameState re-reads for the settled view. - store: VoidGame finishes the game and stamps a draw without a journal row. - migration 00002: allow end_reason 'aborted'. - ui: organizer note under the history grid; i18n en/ru. - docs: ARCHITECTURE 6/9.1, FUNCTIONAL(+ru), PRERELEASE MW3.
This commit is contained in:
+10
-2
@@ -321,7 +321,9 @@ Key points:
|
||||
becomes an automatic resignation, applied by a background sweeper. The sweeper
|
||||
honours each player's **away window** — a daily local-time sleep interval on the
|
||||
account (default 00:00–07:00, midnight-cross aware) — so a player is never
|
||||
timed out while asleep.
|
||||
timed out while asleep. A game whose journal can no longer be replayed — a
|
||||
committed move made illegal by a later rule change — is instead closed as a
|
||||
**draw** (`aborted`) on the next open, never left unopenable (§9.1).
|
||||
- **Players**: auto-match is always 2 players; friend games are 2–4 players.
|
||||
`backend` owns turn order and the bag for any player count. A resignation or
|
||||
timeout in a two-player game ends it with the other player winning. In a game
|
||||
@@ -525,7 +527,13 @@ re-deriving it, so the rebuild matches the committed game) and to render history
|
||||
dictionary**: the board for visual replay is reconstructed by applying placements
|
||||
onto an empty grid, since moves were validated at play time and scores are
|
||||
stored. `variant` and `dict_version` are kept as **metadata only** (audit,
|
||||
complaint review), never as a replay dependency. **GCG export** is derived from
|
||||
complaint review), never as a replay dependency. Engine replay re-validates each
|
||||
move, so a committed move that **later becomes illegal** under a tightened rule
|
||||
(e.g. the single-word connectivity rule) would make the rebuild fail; rather than
|
||||
leave the game unopenable, the next open closes it gracefully as a **draw**
|
||||
(`engine.EndAborted` → `end_reason='aborted'`, all seats marked non-winners),
|
||||
preserves the journal intact, and surfaces an impersonal **organizer note** at the
|
||||
end of the history and in the GCG export (a free-text `#note`). **GCG export** is derived from
|
||||
the same rows and is likewise self-contained — we ship our own writer (the solver
|
||||
exposes none): the standard Poslfit dialect (UTF-8, `#player`/`#lexicon`
|
||||
pragmas, `8G`/`H8` coordinates, lower-case blanks, `.` pass-throughs, `-TILES`
|
||||
|
||||
@@ -177,6 +177,10 @@ would leak the move journal), and the client shares the `.gcg` file where the
|
||||
platform supports it, otherwise downloads it. Statistics (durable accounts only):
|
||||
wins, losses, draws, max points in a game, and max points for a single move (the
|
||||
best play, which already includes every word it formed plus the all-tiles bonus).
|
||||
A game that can no longer be continued — because a rule changed and an earlier
|
||||
move would now be illegal — is closed as a **draw** the moment someone opens it,
|
||||
never left stuck on an error: the move history shows an impersonal organizer note
|
||||
at its end (the game could not be continued), and the same note rides the GCG export.
|
||||
|
||||
### Administration
|
||||
Operators reach a server-rendered admin console at `${DOMAIN}/_gm` — the backend
|
||||
|
||||
@@ -182,6 +182,10 @@ UTC), суточного окна отсутствия (away; сетка по 10
|
||||
поддерживает, иначе скачивает его. Статистика (только у постоянных аккаунтов):
|
||||
победы, поражения, ничьи, макс. очков за партию и макс. очков за один ход (лучший
|
||||
ход, уже включающий все образованные им слова и бонус за все фишки).
|
||||
Партия, которую больше нельзя продолжить — из-за изменения правил более ранний ход
|
||||
стал бы недопустимым, — закрывается **ничьёй** в момент открытия её игроком, а не
|
||||
остаётся висеть с ошибкой: в конце истории ходов показывается обезличенная заметка
|
||||
организатора (партию не удалось продолжить), и та же заметка попадает в экспорт GCG.
|
||||
|
||||
### Администрирование
|
||||
Оператор открывает серверную админ-консоль по адресу `${DOMAIN}/_gm` — её рендерит
|
||||
|
||||
Reference in New Issue
Block a user