Fix parking trains, freight deadlock, and Whistle Post lock-in
This commit is contained in:
+52
-15
@@ -71,31 +71,47 @@ describe('grade modifiers change crossing time', () => {
|
||||
});
|
||||
|
||||
it('Brakeman speeds the descent but not the climb', () => {
|
||||
// ASSUMPTION (§10 Q11): grades climb eastward, so westbound is downhill.
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['brakeman'], 'west'), 1);
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['brakeman'], 'east'), 2);
|
||||
// Q11 — the card prints "(Up)" and "Player sets orientation", so the last argument is which
|
||||
// way is UPHILL. With up = east, a westbound train is descending.
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['brakeman'], 'west', 'east'), 1);
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['brakeman'], 'east', 'east'), 2);
|
||||
});
|
||||
|
||||
it('follows the orientation the player chose, not a fixed compass direction', () => {
|
||||
// The same train on the same card, with the card turned around: Brakeman helps a westbound
|
||||
// train on an east-climbing grade, and an eastbound one when the grade climbs west.
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['brakeman'], 'east', 'west'), 1);
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['brakeman'], 'west', 'west'), 2);
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['helpers'], 'west', 'west'), 1);
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['helpers'], 'east', 'west'), 2);
|
||||
});
|
||||
|
||||
it('Helpers speed the climb but not the descent', () => {
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['helpers'], 'east'), 1);
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['helpers'], 'west'), 2);
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['helpers'], 'east', 'east'), 1);
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['helpers'], 'west', 'east'), 2);
|
||||
});
|
||||
|
||||
it('Airbrakes stack with Brakeman on a slow train', () => {
|
||||
// A slow train pays 3 on a grade; Brakeman and Airbrakes take one Stage each.
|
||||
assert.equal(crossingStages('heavyGrade', 'slow', false, [], 'west'), 3);
|
||||
assert.equal(crossingStages('heavyGrade', 'slow', false, ['brakeman'], 'west'), 2);
|
||||
assert.equal(crossingStages('heavyGrade', 'slow', false, ['brakeman', 'airbrakes'], 'west'), 1);
|
||||
assert.equal(crossingStages('heavyGrade', 'slow', false, [], 'west', 'east'), 3);
|
||||
assert.equal(crossingStages('heavyGrade', 'slow', false, ['brakeman'], 'west', 'east'), 2);
|
||||
assert.equal(
|
||||
crossingStages('heavyGrade', 'slow', false, ['brakeman', 'airbrakes'], 'west', 'east'),
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it('never lets a train cross in no time', () => {
|
||||
assert.equal(crossingStages('heavyGrade', 'fast', false, ['brakeman', 'airbrakes'], 'west'), 1);
|
||||
assert.equal(
|
||||
crossingStages('heavyGrade', 'fast', false, ['brakeman', 'airbrakes'], 'west', 'east'),
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it('leaves non-grade cards alone', () => {
|
||||
// Brakeman on Plains would be an illegal placement anyway; the maths must not move regardless.
|
||||
assert.equal(crossingStages('plains', 'fast', false, ['brakeman'], 'west'), 1);
|
||||
assert.equal(crossingStages('curves', 'fast', false, ['helpers'], 'east'), 2);
|
||||
assert.equal(crossingStages('plains', 'fast', false, ['brakeman'], 'west', 'east'), 1);
|
||||
assert.equal(crossingStages('curves', 'fast', false, ['helpers'], 'east', 'east'), 2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -265,8 +281,10 @@ describe('Flying Switch rolls a cut into an industry', () => {
|
||||
/** A crew on a straight with a facility next door, mid-switch. */
|
||||
function setup(s: GameState) {
|
||||
const area = areaOf(s, 0);
|
||||
// A flying switch rolls the cut ALONG THE TRACK, so the spur and the industry must be joined
|
||||
// north-south. Built east-west, they are two unconnected cards that merely look adjacent.
|
||||
const spur: TrackCard = {
|
||||
geometry: { kind: 'track', geometry: 'straight' },
|
||||
geometry: { kind: 'track', geometry: 'straight', axis: 'ns' },
|
||||
baseOperationalRail: true,
|
||||
standing: [],
|
||||
facility: null,
|
||||
@@ -275,7 +293,7 @@ describe('Flying Switch rolls a cut into an industry', () => {
|
||||
};
|
||||
area.grid.set(coordKey(at(-1, 0)), spur);
|
||||
const industry: TrackCard = {
|
||||
geometry: { kind: 'track', geometry: 'straight' },
|
||||
geometry: { kind: 'track', geometry: 'straight', axis: 'ns' },
|
||||
baseOperationalRail: true,
|
||||
standing: [],
|
||||
facility: {
|
||||
@@ -296,7 +314,11 @@ describe('Flying Switch rolls a cut into an industry', () => {
|
||||
s.trays.set('crew', {
|
||||
id: 'crew', trainNumber: 1, trainIsExtra: false, engineFront: true,
|
||||
consist: [{ type: 'hopper', loaded: false }, { type: 'boxcar', loaded: false }],
|
||||
direction: 'east', position: { at: 'grid', owner: 0, coord: at(-1, 0) }, movesUsed: 0,
|
||||
// Facing SOUTH, down the spur. `direction` only carries east/west, so a crew on north-south
|
||||
// track needs its actual port — without it the engine looks for an east exit the card has not
|
||||
// got, and the crew has no legal moves at all.
|
||||
direction: 'east', facing: 's',
|
||||
position: { at: 'grid', owner: 0, coord: at(-1, 0) }, movesUsed: 0,
|
||||
});
|
||||
s.clock.phase = 'localOps';
|
||||
s.clock.currentActor = 0;
|
||||
@@ -340,13 +362,28 @@ describe('Flying Switch rolls a cut into an industry', () => {
|
||||
const s = game();
|
||||
setup(s);
|
||||
const cardId = hand(s, 'maneuver', 'flyingSwitch');
|
||||
// (0,0) is the Office, adjacent to the crew but not a freight industry.
|
||||
// (0,0) is the Office — track-connected to the crew, but not a freight industry.
|
||||
assert.equal(
|
||||
check(s, 0, { type: 'maneuver.flyingSwitch', cardId, trayId: 'crew', count: 1, to: at(0, 0) }),
|
||||
'CANNOT_DROP_HERE',
|
||||
);
|
||||
});
|
||||
|
||||
it('will not roll onto a card with no track connection', () => {
|
||||
const s = game();
|
||||
setup(s);
|
||||
const area = areaOf(s, 0);
|
||||
// An industry sitting beside the crew, but joined east-west while the spur runs north-south.
|
||||
const stranded = { ...area.grid.get(coordKey(at(-2, 0)))! };
|
||||
stranded.geometry = { kind: 'track', geometry: 'straight', axis: 'ew' };
|
||||
area.grid.set(coordKey(at(-1, 1)), stranded);
|
||||
const cardId = hand(s, 'maneuver', 'flyingSwitch');
|
||||
assert.equal(
|
||||
check(s, 0, { type: 'maneuver.flyingSwitch', cardId, trayId: 'crew', count: 1, to: at(-1, 1) }),
|
||||
'NOT_CONNECTED',
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot cut more cars than it is hauling', () => {
|
||||
const s = game();
|
||||
setup(s);
|
||||
|
||||
Reference in New Issue
Block a user