Found while fixing the Salvage tile for v0.8.0.7. The display half is fixed; this is the engine half, which is a rules question rather than a display one.
That is a synthetic id, not the id of the card that was played. Nothing in s.cards matches it.
The immediate symptom was cosmetic and is now handled in sim/view.ts: cardName() resolves train-<n> to Train <n>, so the face-up pile names its top card instead of reading "a card". A test pins it.
The part that is not cosmetic
reshuffleIfDepleted() sweeps the Salvage Yard back into the Home Office deck when it runs out:
So a synthetic train-<n> can be shuffled into the draw deck and then drawn into a hand, where it is an id with no card behind it. What keepReason, handPlayable and playing it would do with that has not been established.
How reachable
Not, in ordinary play. Eight games driven to 4000 moves each across eight seeds produced zero reshuffles — the deck does not run out in a normal game. So this is latent rather than live, which is why it is filed rather than fixed.
Two defensible fixes, hence the issue
Push the real card id on trainScheduled, so the Yard holds only real cards. Needs the event to carry it, and changes what a replay reconstructs.
Filter synthetics in reshuffleIfDepleted, leaving the id as a marker that a train was scheduled. Smaller, but keeps a non-card in a card pile.
Which is right depends on what the synthetic id is for — whether the Salvage Yard is meant to record scheduled trains at all, or whether the played train card should be going there like any other played card. That is the call to make first.
Found while fixing the Salvage tile for v0.8.0.7. The display half is fixed; this is the engine half, which is a rules question rather than a display one.
## What happens
`apply.ts`, on `trainScheduled`:
```ts
s.decks.salvageYard.push(`train-${e.trainNumber}`);
```
That is a **synthetic id**, not the id of the card that was played. Nothing in `s.cards` matches it.
The immediate symptom was cosmetic and is now handled in `sim/view.ts`: `cardName()` resolves `train-<n>` to `Train <n>`, so the face-up pile names its top card instead of reading "a card". A test pins it.
## The part that is not cosmetic
`reshuffleIfDepleted()` sweeps the Salvage Yard back into the Home Office deck when it runs out:
```ts
const collected = [...s.decks.salvageYard, ...s.decks.departments.flat()];
```
So a synthetic `train-<n>` can be shuffled into the draw deck and then **drawn into a hand**, where it is an id with no card behind it. What `keepReason`, `handPlayable` and playing it would do with that has not been established.
## How reachable
Not, in ordinary play. Eight games driven to 4000 moves each across eight seeds produced **zero** reshuffles — the deck does not run out in a normal game. So this is latent rather than live, which is why it is filed rather than fixed.
## Two defensible fixes, hence the issue
1. **Push the real card id** on `trainScheduled`, so the Yard holds only real cards. Needs the event to carry it, and changes what a replay reconstructs.
2. **Filter synthetics in `reshuffleIfDepleted`**, leaving the id as a marker that a train was scheduled. Smaller, but keeps a non-card in a card pile.
Which is right depends on what the synthetic id is *for* — whether the Salvage Yard is meant to record scheduled trains at all, or whether the played train card should be going there like any other played card. That is the call to make first.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found while fixing the Salvage tile for v0.8.0.7. The display half is fixed; this is the engine half, which is a rules question rather than a display one.
What happens
apply.ts, ontrainScheduled:That is a synthetic id, not the id of the card that was played. Nothing in
s.cardsmatches it.The immediate symptom was cosmetic and is now handled in
sim/view.ts:cardName()resolvestrain-<n>toTrain <n>, so the face-up pile names its top card instead of reading "a card". A test pins it.The part that is not cosmetic
reshuffleIfDepleted()sweeps the Salvage Yard back into the Home Office deck when it runs out:So a synthetic
train-<n>can be shuffled into the draw deck and then drawn into a hand, where it is an id with no card behind it. WhatkeepReason,handPlayableand playing it would do with that has not been established.How reachable
Not, in ordinary play. Eight games driven to 4000 moves each across eight seeds produced zero reshuffles — the deck does not run out in a normal game. So this is latent rather than live, which is why it is filed rather than fixed.
Two defensible fixes, hence the issue
trainScheduled, so the Yard holds only real cards. Needs the event to carry it, and changes what a replay reconstructs.reshuffleIfDepleted, leaving the id as a marker that a train was scheduled. Smaller, but keeps a non-card in a card pile.Which is right depends on what the synthetic id is for — whether the Salvage Yard is meant to record scheduled trains at all, or whether the played train card should be going there like any other played card. That is the call to make first.