Fix parking trains, freight deadlock, and Whistle Post lock-in
This commit is contained in:
+25
-11
@@ -49,17 +49,19 @@ const newSolitaireGame = (seed = 1234) =>
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('card catalogue (component 1)', () => {
|
||||
it('composes the 133-card deck from the design', () => {
|
||||
// Transcribed from docs/Deck cards2.xlsx, whose own total is 115 — plus the 18 extra industry
|
||||
// cards added for Gap 12, taking industries from 9 to 27 (see INDUSTRY_PROFILES).
|
||||
assert.equal(DECK_SIZE, 133);
|
||||
it('composes the 140-card deck from the design', () => {
|
||||
// Transcribed from docs/Deck cards2.xlsx, whose own total is 115 — plus 18 extra industry cards
|
||||
// (Gap 12, industries 9 → 27) and 7 extra office cards (Q12, offices 7 → 14). Both are
|
||||
// deliberate departures from the sheet and both are flagged provisional in content.ts.
|
||||
assert.equal(DECK_SIZE, 140);
|
||||
assert.equal(buildDeck().length, DECK_SIZE);
|
||||
});
|
||||
|
||||
it('matches the design deck composition exactly', () => {
|
||||
const byCategory = Object.fromEntries(deckComposition().map((c) => [c.category, c.count]));
|
||||
assert.deepEqual(byCategory, {
|
||||
office: 7,
|
||||
// 14, not the sheet's 7 — Q12 office density; see OFFICE_PROFILES.
|
||||
office: 14,
|
||||
// 27, not the sheet's 9 — Gap 12 industry density; see INDUSTRY_PROFILES.
|
||||
industry: 27,
|
||||
modifier: 23,
|
||||
@@ -74,13 +76,13 @@ describe('card catalogue (component 1)', () => {
|
||||
|
||||
it('removes opponent-directed cards from a solitaire deck', () => {
|
||||
// Q6 — Space-use and Action cards can only be played AT another player, so in a one-player
|
||||
// game they would be 22 of 133 draws (17%) that do nothing.
|
||||
assert.equal(SOLITAIRE_DECK_SIZE, 111);
|
||||
// game they would be 22 of 140 draws (16%) that do nothing.
|
||||
assert.equal(SOLITAIRE_DECK_SIZE, 118);
|
||||
const solo = buildDeck('solitaire');
|
||||
assert.equal(solo.length, 111);
|
||||
assert.equal(solo.length, 118);
|
||||
assert.ok(!solo.some((c) => c.kind.kind === 'spaceUse' || c.kind.kind === 'action'));
|
||||
// A competitive deck keeps them.
|
||||
assert.equal(buildDeck('competitive').length, 133);
|
||||
assert.equal(buildDeck('competitive').length, 140);
|
||||
});
|
||||
|
||||
it('keeps track OUT of the deck, as a per-player supply', () => {
|
||||
@@ -181,8 +183,20 @@ describe('card catalogue (component 1)', () => {
|
||||
});
|
||||
|
||||
it('forms an office pyramid so the strict upgrade sequence cannot stall', () => {
|
||||
// Assert the PROPERTY, not the literal counts. Upgrades are strictly sequential (Gap 3b), so
|
||||
// every tier must be at least as common as the one above it — otherwise players reach a rung
|
||||
// whose next card is scarcer than the one that got them there. Densities are provisional (Q12)
|
||||
// and expected to move again; the pyramid shape is what must survive.
|
||||
const inDeck = OFFICE_PROFILES.filter((o) => o.copiesInDeck > 0).map((o) => o.copiesInDeck);
|
||||
assert.deepEqual(inDeck, [4, 2, 1]);
|
||||
assert.ok(inDeck.length >= 2, 'there must be an upgrade ladder at all');
|
||||
for (let i = 1; i < inDeck.length; i++) {
|
||||
assert.ok(
|
||||
inDeck[i]! <= inDeck[i - 1]!,
|
||||
`tier ${i} has ${inDeck[i]} copies but the tier below it has ${inDeck[i - 1]}`,
|
||||
);
|
||||
}
|
||||
// The Whistle Post is the starting state, never a card.
|
||||
assert.equal(OFFICE_PROFILES.find((o) => o.tier === 'whistlePost')!.copiesInDeck, 0);
|
||||
});
|
||||
|
||||
it('walks the upgrade sequence without skipping', () => {
|
||||
@@ -344,7 +358,7 @@ describe('game setup (component 2)', () => {
|
||||
...g.decks.salvageYard,
|
||||
...[...g.decks.hands.values()].flat(),
|
||||
];
|
||||
// A solitaire deck omits the 22 opponent-directed cards (Q6), so it holds 111, not 133.
|
||||
// A solitaire deck omits the 22 opponent-directed cards (Q6), so it holds 118, not 140.
|
||||
assert.equal(all.length, SOLITAIRE_DECK_SIZE, 'cards lost or duplicated');
|
||||
assert.equal(new Set(all).size, SOLITAIRE_DECK_SIZE, 'duplicate card ids');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user