0.8.0.8:0 — bundle Station Master v0.8.0.8

The Salvage Yard names the card on top of it at last: it was face up all along and
had nothing to say, because a scheduled train was recorded there twice — once as a
card and once as a marker matching no card. Phase names along the top hold long
enough to read at high playback speeds, and playback goes to twenty times.

A RULES DECISION comes with it (Gitea#23): a regularly scheduled train that has
been PLAYED is already on the timetable, so a reshuffle leaves it out rather than
returning it to be run twice. One that was DISCARDED was never played and comes
back as before; an Extra always comes back, being a single run rather than a
standing slot.

This is the first of the 0.8.0.x releases to touch the engine, so the migration
note says why games in progress still resume: a save is a seed plus the moves
played, replayed through the current rules, and nothing here makes a once-legal
move illegal. check() is untouched and a draw is a draw whatever is on top. What
differs is the Yard's depth, which was double-counting, and what a reshuffle
returns — and no reshuffle occurred across eight games driven to 4000 moves.

Release notes updated in all five locales.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
This commit is contained in:
Jesse
2026-09-10 07:27:43 -04:00
co-authored by Claude Opus 5
parent 1abd5a8a9d
commit 4e1b255c0f
2 changed files with 41 additions and 11 deletions
+40 -10
View File
@@ -10,10 +10,10 @@ export const current = VersionInfo.of({
// — checked against the SDK's own parser rather than assumed, since every example in `versions.md` // — checked against the SDK's own parser rather than assumed, since every example in `versions.md`
// has three: `0.7.9.8:0` parses to upstream `[0,7,9,8]`, which sorts above `[0,7,9]`, so a box on // has three: `0.7.9.8:0` parses to upstream `[0,7,9,8]`, which sorts above `[0,7,9]`, so a box on
// 0.7.9:0 sees this as an update rather than refusing it as a downgrade. // 0.7.9:0 sees this as an update rather than refusing it as a downgrade.
version: '0.8.0.6:0', version: '0.8.0.8:0',
releaseNotes: { releaseNotes: {
en_US: en_US:
'Bundles Station Master v0.8.0.6 — you can watch the other players now. GAMES IN PROGRESS ON ' + 'Bundles Station Master v0.8.0.8 — you can watch the other players now. GAMES IN PROGRESS ON ' +
'THIS SERVER ARE UNAFFECTED and will resume normally. Until now another player could move a ' + 'THIS SERVER ARE UNAFFECTED and will resume normally. Until now another player could move a ' +
'train the length of their district and the only trace on your screen was a line of text: your ' + 'train the length of their district and the only trace on your screen was a line of text: your ' +
'board showed your own Office Area and nobody else’s, and a bot’s whole turn arrived as a ' + 'board showed your own Office Area and nobody else’s, and a bot’s whole turn arrived as a ' +
@@ -51,10 +51,17 @@ export const current = VersionInfo.of({
'lit pile now keeps pulsing for as long as that move is on screen, rather than flashing once ' + 'lit pile now keeps pulsing for as long as that move is on screen, rather than flashing once ' +
'and settling where it was easy to miss. Your own move is put away while the board is catching ' + 'and settling where it was easy to miss. Your own move is put away while the board is catching ' +
'up — it is behind the game at that moment, so anything offered would be a move against a ' + 'up — it is behind the game at that moment, so anything offered would be a move against a ' +
'position that has already changed; Skip is always one click away. And playback can be slowed ' + 'position that has already changed; Skip is always one click away. Playback can be slowed ' +
'further than before, to twenty times.', 'further than before, to twenty times, and the phase names along the top now hold long enough ' +
'to read at those speeds. The Salvage Yard names the card on top of it at last — it was face up ' +
'all along and had nothing to say, because a scheduled train was being recorded there twice, ' +
'once as a card and once as a marker that matched no card. **A rules decision comes with that**: ' +
'a regularly scheduled train that has been PLAYED is already on the timetable, so when the deck ' +
'is reshuffled it stays out rather than coming back to be run a second time. One that was ' +
'DISCARDED was never played, so it returns as before, and an Extra always returns — an Extra is ' +
'a single run, not a standing slot. Games in progress are unaffected.',
es_ES: es_ES:
'Incluye Station Master v0.8.0.6 — ahora puedes ver jugar a los demás. LAS PARTIDAS EN CURSO EN ' + 'Incluye Station Master v0.8.0.8 — ahora puedes ver jugar a los demás. LAS PARTIDAS EN CURSO EN ' +
'ESTE SERVIDOR NO SE VEN AFECTADAS y se reanudarán con normalidad. Hasta ahora otro jugador ' + 'ESTE SERVIDOR NO SE VEN AFECTADAS y se reanudarán con normalidad. Hasta ahora otro jugador ' +
'podía mover un tren a lo largo de todo su distrito y el único rastro en tu pantalla era una ' + 'podía mover un tren a lo largo de todo su distrito y el único rastro en tu pantalla era una ' +
'línea de texto: tu tablero mostraba tu propia Área de Oficina y la de nadie más, y el turno ' + 'línea de texto: tu tablero mostraba tu propia Área de Oficina y la de nadie más, y el turno ' +
@@ -96,7 +103,7 @@ export const current = VersionInfo.of({
'que se ofreciera sería una jugada contra una posición ya cambiada; Saltar está siempre a un ' + 'que se ofreciera sería una jugada contra una posición ya cambiada; Saltar está siempre a un ' +
'clic. Y la reproducción puede ralentizarse más que antes, hasta veinte veces.', 'clic. Y la reproducción puede ralentizarse más que antes, hasta veinte veces.',
de_DE: de_DE:
'Enthält Station Master v0.8.0.6 — Sie können den anderen jetzt beim Spielen zusehen. LAUFENDE ' + 'Enthält Station Master v0.8.0.8 — Sie können den anderen jetzt beim Spielen zusehen. LAUFENDE ' +
'PARTIEN AUF DIESEM SERVER SIND NICHT BETROFFEN und werden normal fortgesetzt. Bisher konnte ' + 'PARTIEN AUF DIESEM SERVER SIND NICHT BETROFFEN und werden normal fortgesetzt. Bisher konnte ' +
'ein anderer Spieler einen Zug durch sein ganzes Revier bewegen, und die einzige Spur auf ' + 'ein anderer Spieler einen Zug durch sein ganzes Revier bewegen, und die einzige Spur auf ' +
'Ihrem Bildschirm war eine Textzeile: Ihr Brett zeigte nur Ihren eigenen Dienstbereich und den ' + 'Ihrem Bildschirm war eine Textzeile: Ihr Brett zeigte nur Ihren eigenen Dienstbereich und den ' +
@@ -141,7 +148,7 @@ export const current = VersionInfo.of({
'einen Klick entfernt. Und die Wiedergabe lässt sich weiter verlangsamen als bisher, auf das ' + 'einen Klick entfernt. Und die Wiedergabe lässt sich weiter verlangsamen als bisher, auf das ' +
'Zwanzigfache.', 'Zwanzigfache.',
pl_PL: pl_PL:
'Zawiera Station Master v0.8.0.6 — teraz możesz obserwować grę pozostałych. TRWAJĄCE PARTIE NA ' + 'Zawiera Station Master v0.8.0.8 — teraz możesz obserwować grę pozostałych. TRWAJĄCE PARTIE NA ' +
'TYM SERWERZE NIE SĄ NARUSZONE i zostaną normalnie wznowione. Do tej pory inny gracz mógł ' + 'TYM SERWERZE NIE SĄ NARUSZONE i zostaną normalnie wznowione. Do tej pory inny gracz mógł ' +
'przeprowadzić pociąg przez cały swój okręg, a jedynym śladem na twoim ekranie była linijka ' + 'przeprowadzić pociąg przez cały swój okręg, a jedynym śladem na twoim ekranie była linijka ' +
'tekstu: twoja plansza pokazywała wyłącznie twój własny Obszar Biura i niczyj inny, a cała ' + 'tekstu: twoja plansza pokazywała wyłącznie twój własny Obszar Biura i niczyj inny, a cała ' +
@@ -179,9 +186,16 @@ export const current = VersionInfo.of({
'raz i zgasnąć tam, gdzie łatwo go było przeoczyć. Twoje własne akcje są chowane, gdy plansza ' + 'raz i zgasnąć tam, gdzie łatwo go było przeoczyć. Twoje własne akcje są chowane, gdy plansza ' +
'nadrabia zaległości: jest wtedy za grą, więc cokolwiek by zaproponowano, byłby to ruch wobec ' + 'nadrabia zaległości: jest wtedy za grą, więc cokolwiek by zaproponowano, byłby to ruch wobec ' +
'pozycji już zmienionej; Pomiń jest zawsze o jedno kliknięcie. A odtwarzanie można spowolnić ' + 'pozycji już zmienionej; Pomiń jest zawsze o jedno kliknięcie. A odtwarzanie można spowolnić ' +
'bardziej niż wcześniej, aż dwudziestokrotnie.', 'bardziej niż wcześniej, aż dwudziestokrotnie, a nazwy faz w górnym wierszu utrzymują się przy ' +
'tych prędkościach wystarczająco długo, by je przeczytać. Złomowisko wreszcie nazywa kartę na ' +
'wierzchu — leżało odkryte przez cały czas i nie miało nic do powiedzenia, bo zaplanowany ' +
'pociąg zapisywano tam dwa razy: raz jako kartę i raz jako znacznik, któremu nie odpowiadała ' +
'żadna karta. **Wiąże się z tym decyzja zasad**: pociąg rozkładowy, który został ZAGRANY, jest ' +
'już w rozkładzie, więc przy przetasowaniu zostaje poza talią, zamiast wracać i jechać drugi ' +
'raz. ODRZUCONY nigdy nie był zagrany, więc wraca jak dotąd, a Extra wraca zawsze — Extra to ' +
'jeden przejazd, nie stałe miejsce. Trwające partie pozostają nienaruszone.',
fr_FR: fr_FR:
'Contient Station Master v0.8.0.6 — vous pouvez maintenant regarder jouer les autres. LES ' + 'Contient Station Master v0.8.0.8 — vous pouvez maintenant regarder jouer les autres. LES ' +
'PARTIES EN COURS SUR CE SERVEUR NE SONT PAS AFFECTÉES et reprendront normalement. Jusqu’ici ' + 'PARTIES EN COURS SUR CE SERVEUR NE SONT PAS AFFECTÉES et reprendront normalement. Jusqu’ici ' +
'un autre joueur pouvait déplacer un train sur toute la longueur de son district et la seule ' + 'un autre joueur pouvait déplacer un train sur toute la longueur de son district et la seule ' +
'trace sur votre écran était une ligne de texte : votre plateau ne montrait que votre propre ' + 'trace sur votre écran était une ligne de texte : votre plateau ne montrait que votre propre ' +
@@ -263,12 +277,28 @@ export const current = VersionInfo.of({
* `advance.ts`. It is a counter that nothing checks — no predicate reads it, so no once-legal move * `advance.ts`. It is a counter that nothing checks — no predicate reads it, so no once-legal move
* became illegal. * became illegal.
* *
* v0.8.0.3 THROUGH v0.8.0.6 ADD NOTHING AGAIN: `git diff v0.8.0.2..v0.8.0.6 -- src/engine/` is empty. * v0.8.0.3 THROUGH v0.8.0.7 ADDED NOTHING: `git diff v0.8.0.2..v0.8.0.7 -- src/engine/` is empty.
* One moved the Skip button, changed what the waiting row says, and stopped the speed control * One moved the Skip button, changed what the waiting row says, and stopped the speed control
* stretching the clock; one took a hostname out of comments; the last drew the Home Office deck * stretching the clock; one took a hostname out of comments; the last drew the Home Office deck
* and lit the pile a watched move touched, which is a diff of two frames the client already * and lit the pile a watched move touched, which is a diff of two frames the client already
* holds. None of it is a rule, and none of it is on disk. * holds. None of it is a rule, and none of it is on disk.
* *
* v0.8.0.8 IS DIFFERENT AND IS THE ONE TO READ CAREFULLY: it changes a RULE (Gitea#23). When the
* Home Office deck is reshuffled, a timetabled train sitting in the SALVAGE YARD is no longer
* recovered — it was played, its number is on the timetable, and it cannot be run twice. The same
* card in a DEPARTMENT was discarded rather than played and still comes back, as does any Extra.
* A duplicate went with it: `trainScheduled` had been pushing a synthetic `train-<n>` beside the
* real card, so four scheduled trains left eight entries in a pile of four.
*
* GAMES IN PROGRESS STILL RESUME, and the reason is the narrow one this package always asks
* (README § Design notes): a save is a seed plus the moves played, replayed through the CURRENT
* rules, so what strands one is a change that makes a once-legal move ILLEGAL. Nothing here does.
* `check()` is untouched; a draw is a draw whatever lies on top of the deck. What differs is the
* Salvage Yard's depth, which was double-counting, and which cards a reshuffle returns — and a
* reshuffle is effectively unreachable in ordinary play: eight games driven to 4000 moves across
* eight seeds produced ZERO. A game that had somehow reshuffled would replay to a different deck
* order rather than refusing to replay, which is worth knowing and is not the same as breaking.
*
* A NEW STATE FIELD CANNOT STRAND A SAVE HERE, which is the property worth naming rather than * A NEW STATE FIELD CANNOT STRAND A SAVE HERE, which is the property worth naming rather than
* re-deriving: a `SavedGame` holds `{ seed, config, history }` and NOT a serialised `GameState`. * re-deriving: a `SavedGame` holds `{ seed, config, history }` and NOT a serialised `GameState`.
* Resuming calls `createGame` — which fills the new field from `setup.ts` — and then replays the * Resuming calls `createGame` — which fills the new field from `setup.ts` — and then replays the