Show the whole Division mainline. district now has option for auto hiding on phases where not needed. scaffold for multi-player geometry showing division. regions on mainline cards implemented. bug fix on laying rail cards. optimized replay (carried links forward). Add sounds, first pass
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
||||
import { createGame } from '../src/engine/setup.ts';
|
||||
import type { GameConfig, GameState, GridCoord, TrackCard } from '../src/engine/state.ts';
|
||||
import { coordKey } from '../src/engine/state.ts';
|
||||
import { snapshot } from '../src/sim/view.ts';
|
||||
|
||||
const config: GameConfig = {
|
||||
mode: 'solitaire',
|
||||
@@ -146,7 +147,7 @@ describe('placing a Mainline modifier', () => {
|
||||
it('will not modify a card with a train on it', () => {
|
||||
const s = game();
|
||||
const node = pinned(s, 1, 'heavyGrade');
|
||||
node.transits.push({ tray: 'someone', stagesRemaining: 1, direction: 'east' });
|
||||
node.transits.push({ tray: 'someone', stagesRemaining: 1, stagesTotal: 1, direction: 'east' });
|
||||
drawTurn(s);
|
||||
const cardId = hand(s, 'mainlineModifier', 'brakeman');
|
||||
assert.equal(check(s, 0, { type: 'mainline.modify', cardId, node: 1 }), 'TRAIN_ON_CARD');
|
||||
@@ -207,7 +208,7 @@ describe('Red Flags protect a stopped train', () => {
|
||||
/** A slow train `behind` closing on a stopped train `ahead`, both eastbound on node 1. */
|
||||
function rearEnder(s: GameState) {
|
||||
const node = pinned(s, 1, 'plains');
|
||||
node.transits.push({ tray: 'ahead', stagesRemaining: 2, direction: 'east' });
|
||||
node.transits.push({ tray: 'ahead', stagesRemaining: 2, stagesTotal: 2, direction: 'east' });
|
||||
s.trays.set('ahead', {
|
||||
id: 'ahead', trainNumber: 4, trainIsExtra: false, engineFront: true,
|
||||
consist: [], direction: 'east', position: { at: 'mainline', index: 1 }, movesUsed: 0,
|
||||
@@ -534,3 +535,63 @@ describe("a train is made up to its card's consist (§8.2)", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('regions on a Mainline card (§2.1, §8.2)', () => {
|
||||
/** Put one train mid-crossing and ask the view where the map should draw it. */
|
||||
const regionFor = (stagesTotal: number, stagesRemaining: number): { region: number; regions: number } => {
|
||||
const s = createGame({
|
||||
id: 'reg',
|
||||
seed: 4,
|
||||
config: {
|
||||
mode: 'solitaire', victory: 'highestAfterDays', length: 'standard',
|
||||
optionalRules: { reducedVisibility: false, sisterTrains: false, employeeRotation: false, emergencyToolbox: false },
|
||||
},
|
||||
playerNames: ['Solitaire'],
|
||||
});
|
||||
const node = s.division.nodes.find((n) => n.kind === 'mainline');
|
||||
assert.ok(node && node.kind === 'mainline');
|
||||
const tray = [...s.trays.keys()][0]!;
|
||||
node.transits.push({ tray, stagesRemaining, stagesTotal, direction: 'east' });
|
||||
const ml = snapshot(s, [], null).division.find((n) => n.kind === 'ml');
|
||||
assert.ok(ml, 'no Mainline node in the view');
|
||||
const t = ml!.trains.flat()[0]!;
|
||||
return { region: t.region as number, regions: ml!.regions as number };
|
||||
};
|
||||
|
||||
it('divides every Mainline card into two regions', () => {
|
||||
// §2.1, and open-questions.md settled on two deliberately: at three, a Day of 12 Stages leaves
|
||||
// trains still on the road when the next Day begins, locking Crew Trays permanently.
|
||||
assert.equal(regionFor(2, 2).regions, 2);
|
||||
});
|
||||
|
||||
it('moves one region per Stage for an ordinary train on a 30 card', () => {
|
||||
// §8.2 exactly. This is the case the printed rule was written for.
|
||||
assert.equal(regionFor(2, 2).region, 0);
|
||||
assert.equal(regionFor(2, 1).region, 1);
|
||||
});
|
||||
|
||||
it('starts a fast train further along the card rather than moving it twice', () => {
|
||||
// The printed cards carry Start positions — a 60 card is crossed in one Stage, which the map
|
||||
// shows as entering at the second region. That is the same fact as "Brakemen moves your entry
|
||||
// point further along", which is how the modifier cards are drawn.
|
||||
assert.equal(regionFor(1, 1).region, 1);
|
||||
});
|
||||
|
||||
it('holds a slow train at the START, not at the exit', () => {
|
||||
// Three Stages will not fit three steps into two regions. The card's distance is fixed, so the
|
||||
// extra Stage is spent getting going — clamping the entry instead would park it at the exit and
|
||||
// read as a train that raced across and then waited.
|
||||
assert.equal(regionFor(3, 3).region, 0);
|
||||
assert.equal(regionFor(3, 2).region, 0);
|
||||
assert.equal(regionFor(3, 1).region, 1);
|
||||
});
|
||||
|
||||
it('never leaves the card it is on', () => {
|
||||
for (let total = 1; total <= 4; total++) {
|
||||
for (let left = total; left >= 1; left--) {
|
||||
const r = regionFor(total, left).region;
|
||||
assert.ok(r >= 0 && r <= 1, `total ${total}, ${left} left put the train in region ${r}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user