Three game releases at once, because the last package was 0.8.0.13:0: the Modifier limits ruling and the coach ratchet made legible (v0.8.0.14), the reference documents rewritten against the game as built (v0.8.0.15), and the Quickstart published where somebody handed this box can actually reach it (v0.8.0.16). GAMES ALREADY RUNNING ON THIS SERVER MAY NOT RESUME. One rule changed in v0.8.0.14 — a Modifier card may no longer be placed outside your Limits — and a game whose saved history contains such a move stops replaying at that move. The server names the move and leaves the save file alone, so a game worth finishing can be finished by putting 0.8.0.13:0 back. On the game this was tested against it stopped eleven moves from where the table had got to. README, instructions.md and the release notes in all five locales say so rather than burying it. The coach ratchet is NOT a rule change and is not presented as one. Coaches only travel one way — boarding discards the emptied coach into the Classification Yard, detraining draws a fresh empty out of the Division Yard, and Classification returns only when the Division Yard runs bare — so over one three-Day game every coach ended in Classification by Day 2 and nobody could board or get off anywhere thereafter. Running out of cars is part of the game; nothing on screen said any of it. A train made up short now reports what it wanted and why none is coming, and the yard panel warns while the shortage lasts. The Interchange stops advertising an action the game does not have. The card said cars could be sorted into any new order there, which has never been implemented, so the only thing it achieved was sending a player hunting for a button that was not there. What it really offers is the one place on the Mainline where an Extra may be made up and started; a consist is re-ordered at a Small Yard, which now shows the train each option would build, laid out west to east as the board draws it. A QUICKSTART SHIPS WITH THE PACKAGE and is linked from the splash page, so a tester who has never played has something to read that is not the repository. The rules and card references beside it were rewritten against the game as built — several had been describing a version a month old. Packaging is the submodule pin, the version and the words: no action, route, file model or interface changed, and the whole release is inside the bundled game. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUizFYCMHRWhbWwXhp7WPR
371 lines
32 KiB
TypeScript
371 lines
32 KiB
TypeScript
import { IMPOSSIBLE, VersionInfo } from '@start9labs/start-sdk'
|
||
|
||
export const current = VersionInfo.of({
|
||
// `<upstream>:<downstream>` — the upstream half tracks the bundled Station Master tag exactly
|
||
// (`versions.md`'s consistency checklist), so bumping the submodule pin and bumping this are one
|
||
// action. The downstream half moves only for packaging-only changes, and RESETS to `:0` when the
|
||
// upstream half moves.
|
||
//
|
||
// FOUR COMPONENTS IS FINE. ExVer's upstream half is an arbitrary-length dotted number, not semver
|
||
// — 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
|
||
// 0.7.9:0 sees this as an update rather than refusing it as a downgrade.
|
||
version: '0.8.0.16:0',
|
||
releaseNotes: {
|
||
en_US:
|
||
'Bundles Station Master v0.8.0.16 — six fixes from a live playtest, a card that stopped ' +
|
||
'advertising something it cannot do, and a reference pack for new players. **GAMES ALREADY ' +
|
||
'RUNNING ON THIS SERVER MAY NOT RESUME.** One rule changed: a Modifier card may no longer be ' +
|
||
'placed outside your Limits. A game whose saved history contains such a move stops replaying ' +
|
||
'at that move, and the server says which one and leaves the file alone — so a game worth ' +
|
||
'finishing can be finished by putting v0.8.0.13 back. Measured on the test game: it stopped ' +
|
||
'eleven moves from where it had got to. **Passenger trains that ran empty, explained.** ' +
|
||
'Coaches only travel one way: boarding sends the emptied coach to the Classification Yard, ' +
|
||
'unloading passengers needs an empty coach from the Division Yard, and the Classification ' +
|
||
'Yard comes back only when the Division Yard is completely bare. Over one three-Day game ' +
|
||
'every coach ended up in Classification by the second Day and stayed there, so no passengers ' +
|
||
'could board or get off anywhere for the rest of the game, and a train whose card calls for ' +
|
||
'coaches was made up empty. **The rule has not changed** — running out of cars is part of the ' +
|
||
'game — but nothing on screen said any of it. Now a train made up short says what it wanted ' +
|
||
'and why none is coming, and the yard panel warns while the shortage lasts. **The Interchange ' +
|
||
'no longer offers a sorting action it does not have.** The card said "cars may be sorted into ' +
|
||
'any new order here", which has never been implemented — a player could only go looking for a ' +
|
||
'button that was not there. What the card really offers is the one place on the Mainline ' +
|
||
'where an Extra Train can be made up and started. Consists are re-ordered at a Small Yard in ' +
|
||
'your own district. **The Small Yard tells you what each option would build.** Re-ordering a ' +
|
||
'train offered a list of numbers; each option now shows the train it would make, laid out ' +
|
||
'west to east exactly as the board draws it, with the engine in place, and says whether the ' +
|
||
'result is ready to leave the Office or would be held there. A train may also be sorted with ' +
|
||
'cars ahead of its engine now, for shoving a cut into an industry. **A guide for new ' +
|
||
'players.** The package now carries a Quickstart — what the game is, how you win, how a Stage ' +
|
||
'runs, what is on the screen and a first twenty minutes — alongside rules and card references ' +
|
||
'brought up to date with how the game actually runs. **Smaller.** Making up a train names the ' +
|
||
'train in the history. A switching turn no longer fills the history with grid coordinates: it ' +
|
||
'keeps the first move, work done at an industry, the yard sort, and a closing line saying ' +
|
||
'what the turn cost and where the crew was left. The Freight Agent, Porters and Laborers each ' +
|
||
'say what that role is for — passengers get on and off with a Porter, not the Freight Agent.',
|
||
es_ES:
|
||
'Incluye Station Master v0.8.0.16 — seis correcciones de una partida de prueba, una carta que ' +
|
||
'deja de ofrecer algo que no puede hacer, y documentación para jugadores nuevos. **LAS ' +
|
||
'PARTIDAS EN CURSO EN ESTE SERVIDOR PUEDEN NO REANUDARSE.** Ha cambiado una regla: una carta ' +
|
||
'de Modificador ya no puede colocarse fuera de tus Límites. Una partida cuyo historial ' +
|
||
'contenga una jugada así deja de reproducirse en ese punto; el servidor indica cuál y no toca ' +
|
||
'el archivo, así que una partida que merezca terminarse puede acabarse reinstalando la ' +
|
||
'v0.8.0.13. Medido en la partida de prueba: se detuvo a once jugadas del punto al que había ' +
|
||
'llegado. **Por qué los trenes de pasajeros iban vacíos.** Los coches solo circulan en un ' +
|
||
'sentido: al subir pasajeros el coche vacío va al Patio de Clasificación, bajarlos exige un ' +
|
||
'coche vacío del Patio de División, y Clasificación solo vuelve cuando el Patio de División ' +
|
||
'queda totalmente vacío. En una partida de tres Días todos los coches acabaron en ' +
|
||
'Clasificación el segundo Día y allí se quedaron: nadie pudo subir ni bajar en el resto de la ' +
|
||
'partida, y un tren cuya carta pide coches se formó vacío. **La regla no cambia** — quedarse ' +
|
||
'sin material es parte del juego — pero nada en pantalla lo decía. Ahora un tren formado a ' +
|
||
'medias explica qué le faltaba y por qué no llegará, y el panel de patios avisa mientras dure ' +
|
||
'la escasez. **El Intercambiador ya no ofrece una acción de clasificación que no existe.** La ' +
|
||
'carta decía que allí podían reordenarse los vagones, algo que nunca se implementó: el ' +
|
||
'jugador solo podía buscar un botón inexistente. Lo que la carta ofrece de verdad es el único ' +
|
||
'punto de la Línea Principal donde se puede formar y arrancar un Tren Extra. Los trenes se ' +
|
||
'reordenan en un Pequeño Patio de tu propio distrito. **El Pequeño Patio dice qué formaría ' +
|
||
'cada opción.** Reordenar un tren ofrecía una lista de números; ahora cada opción muestra el ' +
|
||
'tren resultante, de oeste a este tal como lo dibuja el tablero y con la locomotora en su ' +
|
||
'sitio, e indica si podrá salir de la Oficina o quedará retenido. También se puede dejar ' +
|
||
'vagones por delante de la locomotora, para empujarlos a una industria. **Una guía para ' +
|
||
'empezar.** El paquete incluye ahora una Guía rápida — qué es el juego, cómo se gana, cómo ' +
|
||
'transcurre una Etapa, qué hay en pantalla y los primeros veinte minutos — junto con las ' +
|
||
'reglas y las referencias de cartas actualizadas a cómo funciona realmente el juego. ' +
|
||
'**Menores.** Al formar un tren, el historial lo nombra. Un turno de maniobras ya no llena el ' +
|
||
'historial de coordenadas: conserva el primer movimiento, el trabajo hecho en una industria, ' +
|
||
'la reordenación en el patio y una línea final con lo que costó el turno y dónde quedó la ' +
|
||
'cuadrilla. El Agente de Mercancías, los Mozos y los Peones dicen para qué sirve cada ' +
|
||
'función: los pasajeros suben y bajan con un Mozo, no con el Agente de Mercancías.',
|
||
de_DE:
|
||
'Enthält Station Master v0.8.0.16 — sechs Korrekturen aus einer Testpartie, eine Karte, die ' +
|
||
'nicht mehr anbietet, was sie nicht kann, und Unterlagen für neue Spieler. **LAUFENDE SPIELE ' +
|
||
'AUF DIESEM SERVER WERDEN MÖGLICHERWEISE NICHT FORTGESETZT.** Eine Regel hat sich geändert: ' +
|
||
'eine Modifikatorkarte darf nicht mehr außerhalb Ihrer Limits liegen. Ein Spiel, dessen ' +
|
||
'Verlauf einen solchen Zug enthält, bricht dort beim Wiedereinlesen ab; der Server nennt den ' +
|
||
'Zug und lässt die Datei unberührt, sodass eine erhaltenswerte Partie mit v0.8.0.13 zu Ende ' +
|
||
'gespielt werden kann. Gemessen an der Testpartie: sie stoppte elf Züge vor dem erreichten ' +
|
||
'Stand. **Warum Personenzüge leer fuhren.** Personenwagen laufen nur in eine Richtung: beim ' +
|
||
'Einsteigen geht der geleerte Wagen in den Rangierbahnhof, zum Aussteigen braucht es einen ' +
|
||
'leeren Wagen aus dem Bezirksbahnhof, und der Rangierbahnhof kehrt erst zurück, wenn der ' +
|
||
'Bezirksbahnhof völlig leer ist. In einer Drei-Tage-Partie lagen am zweiten Tag alle Wagen im ' +
|
||
'Rangierbahnhof und blieben dort: den Rest des Spiels konnte niemand ein- oder aussteigen, ' +
|
||
'und ein Zug, dessen Karte Personenwagen verlangt, wurde leer gebildet. **Die Regel bleibt** ' +
|
||
'— auszugehen gehört zum Spiel —, aber nichts auf dem Bildschirm sagte es. Jetzt nennt ein zu ' +
|
||
'kurz gebildeter Zug, was ihm fehlte und warum nichts nachkommt, und die Bahnhofsanzeige ' +
|
||
'warnt, solange der Mangel anhält. **Der Übergabebahnhof bietet keine Sortierfunktion mehr ' +
|
||
'an, die es nicht gibt.** Auf der Karte stand, Wagen ließen sich hier neu ordnen — das war ' +
|
||
'nie umgesetzt, und man konnte nur nach einer Schaltfläche suchen, die es nicht gab. Was die ' +
|
||
'Karte wirklich bietet, ist die einzige Stelle der Hauptstrecke, an der ein Extrazug gebildet ' +
|
||
'und gestartet werden kann. Umgeordnet wird in einem Kleinen Bahnhof im eigenen Bezirk. **Der ' +
|
||
'Kleine Bahnhof zeigt, was jede Option bilden würde.** Das Umordnen bot eine Liste von ' +
|
||
'Zahlen; jede Option zeigt nun den entstehenden Zug, von West nach Ost genau so, wie das ' +
|
||
'Brett ihn zeichnet, mit der Lok an ihrem Platz, und sagt, ob er die Betriebsstelle verlassen ' +
|
||
'darf oder dort festgehalten wird. Wagen dürfen jetzt auch vor der Lok stehen, um sie in ' +
|
||
'einen Anschluss zu drücken. **Eine Anleitung für den Anfang.** Das Paket enthält jetzt eine ' +
|
||
'Kurzanleitung — worum es geht, wie man gewinnt, wie eine Etappe abläuft, was auf dem ' +
|
||
'Bildschirm steht und die ersten zwanzig Minuten — samt Regeln und Kartenreferenzen auf dem ' +
|
||
'Stand des tatsächlichen Spiels. **Kleineres.** Beim Bilden eines Zuges nennt der Verlauf den ' +
|
||
'Zug. Eine Rangierrunde füllt den Verlauf nicht mehr mit Koordinaten: sie behält den ersten ' +
|
||
'Zug, die Arbeit an einem Anschluss, das Umordnen im Bahnhof und eine Schlusszeile mit den ' +
|
||
'verbrauchten Zügen und dem Standort der Mannschaft. Güteragent, Porter und Arbeiter sagen, ' +
|
||
'wofür sie da sind: Fahrgäste steigen mit einem Porter ein und aus, nicht mit dem ' +
|
||
'Güteragenten.',
|
||
pl_PL:
|
||
'Zawiera Station Master v0.8.0.16 — sześć poprawek z partii testowej, kartę, która przestaje ' +
|
||
'oferować to, czego nie potrafi, oraz materiały dla nowych graczy. **GRY TRWAJĄCE NA TYM ' +
|
||
'SERWERZE MOGĄ SIĘ NIE WZNOWIĆ.** Zmieniła się jedna zasada: karty Modyfikatora nie wolno już ' +
|
||
'położyć poza twoimi Granicami. Gra, której historia zawiera taki ruch, przestaje się ' +
|
||
'odtwarzać w tym miejscu; serwer podaje który to ruch i nie rusza pliku, więc partię wartą ' +
|
||
'dokończenia można dograć po ponownej instalacji v0.8.0.13. Zmierzone na grze testowej: ' +
|
||
'zatrzymała się jedenaście ruchów przed osiągniętym stanem. **Dlaczego pociągi pasażerskie ' +
|
||
'jeździły puste.** Wagony osobowe krążą tylko w jedną stronę: przy wsiadaniu opróżniony wagon ' +
|
||
'trafia na Stację Rozrządową, wysiadanie wymaga pustego wagonu ze Stacji Rejonowej, a ' +
|
||
'Rozrządowa wraca dopiero wtedy, gdy Rejonowa całkiem opustoszeje. W trzydniowej partii ' +
|
||
'wszystkie wagony wylądowały na Rozrządowej już drugiego Dnia i tam zostały: przez resztę gry ' +
|
||
'nikt nie mógł wsiąść ani wysiąść, a pociąg, którego karta wymaga wagonów osobowych, ' +
|
||
'formowano pusty. **Zasada się nie zmienia** — kończące się zasoby to część gry — ale nic na ' +
|
||
'ekranie tego nie mówiło. Teraz pociąg sformowany za krótko podaje, czego mu zabrakło i ' +
|
||
'dlaczego nic nie nadjedzie, a panel stacji ostrzega, dopóki trwa niedobór. **Stacja ' +
|
||
'Przeładunkowa nie oferuje już sortowania, którego nie ma.** Karta mówiła, że można tu ' +
|
||
'przestawić wagony w dowolnej kolejności — nigdy tego nie zaimplementowano, więc gracz mógł ' +
|
||
'tylko szukać nieistniejącego przycisku. Naprawdę karta daje jedyne miejsce na Magistrali, ' +
|
||
'gdzie można sformować i wyprawić Pociąg Dodatkowy. Składy przestawia się na Małej Stacji we ' +
|
||
'własnym okręgu. **Mała Stacja pokazuje, co zbudowałaby każda opcja.** Przestawianie składu ' +
|
||
'oferowało listę liczb; teraz każda opcja pokazuje powstający pociąg, z zachodu na wschód ' +
|
||
'dokładnie tak, jak rysuje go plansza, z lokomotywą na swoim miejscu, i mówi, czy będzie mógł ' +
|
||
'opuścić Biuro, czy zostanie tam zatrzymany. Wagony mogą teraz stać również przed lokomotywą, ' +
|
||
'by wepchnąć je do zakładu. **Przewodnik na start.** Pakiet zawiera teraz Szybki start — czym ' +
|
||
'jest gra, jak się wygrywa, jak przebiega Etap, co jest na ekranie i pierwsze dwadzieścia ' +
|
||
'minut — wraz z zasadami i opisami kart zaktualizowanymi do tego, jak gra działa naprawdę. ' +
|
||
'**Drobniejsze.** Przy formowaniu pociągu historia podaje jego numer. Tura manewrowa nie ' +
|
||
'zasypuje już historii współrzędnymi: zostaje pierwszy ruch, praca przy zakładzie, ' +
|
||
'przestawienie składu i wiersz końcowy z kosztem tury i miejscem, gdzie została drużyna. ' +
|
||
'Agent Towarowy, Bagażowi i Robotnicy mówią, do czego służą: pasażerowie wsiadają i wysiadają ' +
|
||
'z Bagażowym, a nie z Agentem Towarowym.',
|
||
fr_FR:
|
||
'Inclut Station Master v0.8.0.16 — six corrections issues d’une partie de test, une carte qui ' +
|
||
'cesse de proposer ce qu’elle ne sait pas faire, et de la documentation pour les nouveaux ' +
|
||
'joueurs. **LES PARTIES EN COURS SUR CE SERVEUR PEUVENT NE PAS REPRENDRE.** Une règle a ' +
|
||
'changé : une carte Modificateur ne peut plus être posée hors de vos Limites. Une partie dont ' +
|
||
'l’historique contient un tel coup cesse de se rejouer à ce coup ; le serveur indique lequel ' +
|
||
'et ne touche pas au fichier, si bien qu’une partie qui mérite d’être finie peut l’être en ' +
|
||
'réinstallant la v0.8.0.13. Mesuré sur la partie de test : elle s’est arrêtée onze coups ' +
|
||
'avant le point atteint. **Pourquoi les trains de voyageurs roulaient à vide.** Les voitures ' +
|
||
'ne circulent que dans un sens : à l’embarquement la voiture vidée part à la Gare de Triage, ' +
|
||
'faire descendre les voyageurs exige une voiture vide de la Gare de Division, et le Triage ne ' +
|
||
'revient que lorsque la Gare de Division est entièrement vide. Sur une partie de trois Jours, ' +
|
||
'toutes les voitures étaient au Triage dès le deuxième Jour et y sont restées : plus personne ' +
|
||
'n’a pu monter ni descendre, et un train dont la carte demande des voitures a été formé à ' +
|
||
'vide. **La règle ne change pas** — manquer de matériel fait partie du jeu — mais rien à ' +
|
||
'l’écran ne le disait. Désormais un train formé trop court indique ce qui lui manquait et ' +
|
||
'pourquoi rien n’arrivera, et le panneau des gares avertit tant que dure la pénurie. **La ' +
|
||
'gare d’Interchange ne propose plus un tri qui n’existe pas.** La carte annonçait que les ' +
|
||
'wagons pouvaient y être remis dans n’importe quel ordre : cela n’a jamais été implémenté, et ' +
|
||
'le joueur ne pouvait que chercher un bouton absent. Ce que la carte offre réellement, c’est ' +
|
||
'le seul endroit de la Grande Ligne où un Train Supplémentaire peut être formé et lancé. Les ' +
|
||
'rames se réordonnent à une Petite Gare de votre district. **La Petite Gare dit ce que chaque ' +
|
||
'option formerait.** Réordonner un train proposait une liste de numéros ; chaque option ' +
|
||
'montre maintenant le train obtenu, d’ouest en est exactement comme le plateau le dessine, la ' +
|
||
'locomotive à sa place, et précise s’il pourra quitter le Bureau ou y sera retenu. On peut ' +
|
||
'aussi laisser des wagons devant la locomotive, pour les pousser dans une industrie. **Un ' +
|
||
'guide pour débuter.** Le paquet contient désormais un Démarrage rapide — ce qu’est le jeu, ' +
|
||
'comment on gagne, le déroulé d’une Étape, ce qu’on voit à l’écran et les vingt premières ' +
|
||
'minutes — ainsi que les règles et les références de cartes mises à jour sur le jeu réel. ' +
|
||
'**Plus petit.** À la formation d’un train, l’historique le nomme. Un tour de manœuvre ne ' +
|
||
'remplit plus l’historique de coordonnées : il garde le premier mouvement, le travail fait à ' +
|
||
'une industrie, le tri en gare et une ligne finale disant ce que le tour a coûté et où ' +
|
||
'l’équipe a été laissée. L’Agent de Fret, les Porteurs et les Ouvriers disent à quoi sert ' +
|
||
'chaque rôle : les voyageurs montent et descendent avec un Porteur, pas avec l’Agent de Fret.',
|
||
},
|
||
migrations: {
|
||
/**
|
||
* NOTHING TO CARRY, AND CHECKED THE SAME NARROW WAY v0.7.9.8's ENTRY WAS.
|
||
*
|
||
* The rule this package lives by is in the game's README: a save is a seed plus the moves
|
||
* played, replayed through the CURRENT rules, so the only thing that strands an older save is a
|
||
* change that makes a once-legal move illegal. So the question is never "how big was the
|
||
* release" — v0.8.0 is a large one — but "did any predicate change its answer".
|
||
*
|
||
* Measured with `git diff v0.7.9.8..v0.8.0.1 -- src/engine/`. The WHOLE engine diff is eight
|
||
* lines:
|
||
* four `GameEvent` types (`trayMoved`, `carsCoupled`, `carsDropped`, `consistSorted`) gained a
|
||
* `player` field, and the four places that build them pass it. `check()` is untouched, `legal.ts`
|
||
* and `advance.ts` are untouched, and no predicate anywhere was edited. Games in progress resume,
|
||
* and the release notes say so in all five locales rather than leaving it to be discovered.
|
||
*
|
||
* EVENTS ARE NOT PERSISTED, so widening four of them cannot invalidate anything on disk: a
|
||
* `SavedGame` is `{ seed, config, history: Intent[], … }` and the events are derived by replaying
|
||
* it. The narration TEXT of two lines changed with them ("Moved Train 3 …" where it used to read
|
||
* "CREW moved …"), which a resumed game simply regenerates — the log is not stored either.
|
||
*
|
||
* WHAT IS NOT COVERED BY THAT ARGUMENT, and why it still does not need a migration: the wire
|
||
* between this server and a browser gained a lot. `Push` now carries ordered presentation steps
|
||
* and a public-board baseline, and the board deltas changed shape. None of it is on disk, and a
|
||
* browser reloads onto the new page as part of the update, so there is no version skew to
|
||
* migrate — the same reasoning v0.7.9.8 used for its own `Frame` changes.
|
||
*
|
||
* v0.8.0.1 ADDED NOTHING TO THIS ARGUMENT: `git diff v0.8.0..v0.8.0.1 -- src/engine/` is empty.
|
||
* It changed `sim/pacing.ts`, `web/step-queue.ts` and `web/main.ts` — how long a step is shown
|
||
* and when the queue calls itself idle — which is presentation and touches neither the rules nor
|
||
* anything on disk.
|
||
*
|
||
* v0.8.0.2 DOES TOUCH THE ENGINE, and it is worth being exact about why that is still nothing to
|
||
* migrate. `git diff v0.8.0.1..v0.8.0.2 -- src/engine/` is three added lines: `collisionsPrevDay`
|
||
* declared on `GameState`, initialised to 0 in `setup.ts`, and assigned at the Day rollover in
|
||
* `advance.ts`. It is a counter that nothing checks — no predicate reads it, so no once-legal move
|
||
* became illegal.
|
||
*
|
||
* 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
|
||
* 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
|
||
* 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.
|
||
*
|
||
* v0.8.0.9 TOUCHES THE ENGINE FOR SPEED, AND PROVED IT CHANGED NOTHING. `git diff v0.8.0.8..v0.8.0.9
|
||
* -- src/engine/` adds a route cache scoped to one unchanged position (`withRouteCache`), splits
|
||
* `applyIntent` into `prepareIntent` + `commitEvents`, trims allocation in `exploreMoves`, adds
|
||
* `legalSwitchingActions`, and exports `badlyMadeUp`, `protoCard` and `extendLimitsIfNeeded` for the
|
||
* bot. No predicate's answer changed: every event and intent of 32 seeded games hashed identical before
|
||
* and after each step, and the game's `test/route-cache.test.ts` pins the split. Everything else is the
|
||
* developer bot, whose past moves are already intents in a save. Games in progress resume.
|
||
*
|
||
* v0.8.0.10 IS THE FIRST BUMP THAT BREAKS RESUMES, AND IT DOES SO DELIBERATELY. It corrects WHEN §8.1
|
||
* asks the Superintendent to rule (Gitea#26): a train on a card BEHIND the one departing is nobody's
|
||
* concern, and was being counted. A save is the seed and the moves replayed through the CURRENT rules,
|
||
* so a game holding a `mainline.clearance` the rules no longer ask for is refused at that move with
|
||
* NO_PENDING_DECISION — `tryResumeSession` reports which move, and `index.ts` logs the refusal and
|
||
* leaves the file alone. Measured before shipping: 40 four-seat co-op games recorded by bots under
|
||
* v0.8.0.9 and replayed under this release — 28 stop, usually 5-40% of the way in. Jesse's call
|
||
* (2026-09-15): no games in progress need keeping, so this ships without a per-game switch for the old
|
||
* rule, and the release notes say so in all five locales. A game worth finishing can be finished by
|
||
* putting v0.8.0.9 back — the save file is untouched.
|
||
*
|
||
* The rest of the release is the screen and the log — who the board says it is waiting on, what the
|
||
* history says and when it says it, a flash on a realigned Mainline card — plus one new read-only
|
||
* route (`GET /api/save?token=…`) that hands a seated player their own save. None of it is on disk.
|
||
*
|
||
* 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`.
|
||
* Resuming calls `createGame` — which fills the new field from `setup.ts` — and then replays the
|
||
* intents through the current rules, so the field is populated on the way past. There is no old
|
||
* shape on disk for a new field to be missing from.
|
||
*
|
||
* THE ONE THING THAT WOULD HAVE NEEDED CARE, had it been built the way the plan first described:
|
||
* the display-step collector runs inside `submit()`, and the resume path (`fromMultiplayerSave`)
|
||
* deliberately does not go through `submit()`. So replaying a saved game emits no steps and burns
|
||
* no sequence numbers. A test in the game pins that, because it is a property rather than an
|
||
* accident.
|
||
*
|
||
* v0.8.0.11 RESUMES, AND IT WAS MEASURED THE SAME NARROW WAY — which matters more than usual
|
||
* here, because the release immediately before it is the one that did NOT.
|
||
* `git diff v0.8.0.10..v0.8.0.11 -- src/engine/` is three files and nineteen insertions:
|
||
* `trainArrived` gains an `owner` field (`events.ts`), the one line in `advance.ts` that fills
|
||
* it from `playerAtSeat`, and a Mainline card's printed description losing a sentence about a
|
||
* speed that is not drawn on the card (`content.ts`). `check()` and `legal.ts` are untouched and
|
||
* no predicate was edited, so nothing once legal became illegal. Events are not persisted — a
|
||
* `SavedGame` is `{ seed, config, history }` and the events are derived by replaying it — so
|
||
* widening one of them cannot invalidate anything on disk, exactly as the v0.8.0.1 entry above
|
||
* argues for four of them.
|
||
*
|
||
* THE REST OF THE RELEASE IS THE SCREEN: a Pause control for the catch-up queue, a one-render
|
||
* look at another player's Office Area, the turn chart following the step being replayed rather
|
||
* than the live game, "still needs" on the make-up panel, the amber on an addable yard chip, and
|
||
* save filenames carrying the join code. None of it is a rule and none of it is on disk.
|
||
*
|
||
* v0.8.0.12 TOUCHES THE ENGINE NOT AT ALL, which is the shortest entry this comment has ever
|
||
* had a right to: `git diff v0.8.0.11..v0.8.0.12 -- src/engine/` is EMPTY. The release is the
|
||
* server's HTTP surface, the browser client and the docs — a seat recovery flow (Gitea#33) for
|
||
* a player whose browser has lost the token that is their only identity.
|
||
*
|
||
* WHAT IT ADDS, AND WHY NONE OF IT IS A MIGRATION: two routes (`POST /api/games/<id>/claim`
|
||
* behind the admin secret, `POST /api/claim` for the player following the link), a `?claim=`
|
||
* entry path in the client, and `seatedPlayers` on the administrative game listing. The codes
|
||
* themselves live in memory and are deliberately NOT persisted — minted on demand and spent
|
||
* within minutes, so a restart dropping them is the right failure rather than a loss. Nothing
|
||
* new is written to the volume, no predicate was edited, and `check()` and `legal.ts` are
|
||
* untouched, so no once-legal move became illegal. Games in progress resume, and the release
|
||
* notes say so in all five locales.
|
||
*
|
||
* v0.8.0.13 TOUCHES THE ENGINE AGAIN, AND THE WHOLE DIFF IS TWO LINES.
|
||
* `git diff v0.8.0.12..v0.8.0.13 -- src/engine/` adds a `superintendentChanged` variant to the
|
||
* `GameEvent` union (`events.ts`) and the one `events.push` in `advance.ts` that emits it beside
|
||
* the `actorChanged` that was already there — §5's handover of the Fedora, which the log had been
|
||
* dropping as turn bookkeeping. Nothing was removed and nothing was edited: `check()`, `legal.ts`
|
||
* and every predicate are untouched, so no once-legal move became illegal, and a widened event
|
||
* cannot invalidate anything on disk because events are DERIVED by replaying a `SavedGame` rather
|
||
* than stored in one — the same argument the v0.8.0.1 and v0.8.0.11 entries above make for
|
||
* theirs. Games in progress resume, and the release notes say so in all five locales.
|
||
*
|
||
* THE REST IS THE SCREEN AND THE LOG, none of it on disk: the Division Yard chips now obey the
|
||
* catch-up queue (they did not, and a click during it submitted a real intent against a stale
|
||
* board), the yard counts are read from the shown board rather than the live game, the district
|
||
* picker gained your own seat and is ordered by seat rather than by player index, collisions name
|
||
* the district's owner and the 5 Revenue they lose, a Depot that cannot stock an outbound
|
||
* passenger explains which car it is short of, and the history panel keeps 90 lines. The
|
||
* passenger case is a RULING rather than a fix — §6.3 wants a loaded coach in the Division Yard,
|
||
* and Jesse kept the shortage (Gitea#2's ruling) while ending the silence about it.
|
||
*
|
||
* v0.8.0.14 IS THE SECOND BUMP THAT BREAKS RESUMES, AND LIKE v0.8.0.10 IT DOES SO DELIBERATELY.
|
||
* A Modifier card may no longer be placed outside a player's Limits (§2.1). That REVERSES an
|
||
* earlier ruling of Jesse's — Modifiers were exempt because a Facility standing at the limit has
|
||
* three of its nine spots beyond the sign — and it is exactly the shape of change this package's
|
||
* `UPDATING.md` says to look for: a predicate in `apply.ts` changed its answer, so a move that
|
||
* was legal when it was played is refused on replay.
|
||
*
|
||
* MEASURED, NOT ESTIMATED. The Day 3 playtest save stops at intent 528 of 539 —
|
||
* `card.play c55 → (-2,4)` with the sign at column 3 — eleven moves short of where the table had
|
||
* got to. `tryResumeSession` names the move, `index.ts` logs the refusal, and the file is left
|
||
* untouched, so re-installing v0.8.0.13 finishes any game worth finishing. Jesse's call, knowing
|
||
* it strands the game that was on the box.
|
||
*
|
||
* THE REST OF THE ENGINE DIFF STRANDS NOTHING, and is worth separating from the part that does.
|
||
* `makeUpShort` is a new event reporting that the make-up round could give a train nothing;
|
||
* `switchingEnded` closes a switching turn; `trayMoved` carries the Stage's Move allowance and
|
||
* `consistSorted` carries where the engine ended up; `switch.sortConsist` gained an OPTIONAL
|
||
* `engineAt` (absent means the nose, which is what every sort did before, so an older save
|
||
* replays to the same train). Events are derived by replaying a save rather than stored in one,
|
||
* and an optional intent field defaulting to the old behaviour changes no past move.
|
||
*
|
||
* THE SMALL YARD RULE CHANGED IN THE PERMISSIVE DIRECTION, which cannot strand a save either: a
|
||
* train there may now be sorted with cars ahead of its engine (`implications.md` against the
|
||
* v0.4.5 card text, Jesse's ruling). Nothing once legal became illegal; §8.2's departure check is
|
||
* untouched and still holds a broken-backed train at the Office.
|
||
*
|
||
* v0.8.0.15 AND v0.8.0.16 CHANGE NO RULE AND STRAND NOTHING.
|
||
* `git diff v0.8.0.14..v0.8.0.16 -- src/engine/` is one sentence of card TEXT and one doc
|
||
* comment. The Interchange stopped telling players "Cars may be sorted into any new order here"
|
||
* — a printed capability that has never been implemented, reaching the board through
|
||
* `mainlineDescription`, which `view.ts` renders as a Mainline card's `what`. Nothing reads
|
||
* `sortsCars` to permit a sort; its only live use is marking the card an Extra may be made up
|
||
* on. No predicate was touched, so no once-legal move became illegal.
|
||
*
|
||
* The rest of those two releases is documentation inside the repository — a Quickstart for a new
|
||
* player, and the four hand-written references brought up to the game as it runs. None of it is
|
||
* served by the site or read by the service; it rides along in the image because the Dockerfile
|
||
* copies the game's tree in.
|
||
*
|
||
* SO THE RESUME WARNING IN THESE RELEASE NOTES IS ENTIRELY v0.8.0.14's, and it still applies:
|
||
* a box on 0.8.0.13:0 takes this update in one hop and meets the Modifier rule on the way.
|
||
*/
|
||
up: async ({ effects }) => {},
|
||
down: IMPOSSIBLE,
|
||
},
|
||
})
|