v0.4.5 - fix bug with not being able to drop cars
This commit is contained in:
@@ -1402,6 +1402,44 @@ describe('a train that rounds a curve points where the curve took it', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('can still back out of the curve it just rounded', () => {
|
||||
/**
|
||||
* THE OTHER HALF OF THE SAME MISTAKE, and the one that bites hardest.
|
||||
*
|
||||
* Forward exits by `facing`; reverse used to exit by `opposite(facing)` — which, again, is the
|
||||
* other end of a STRAIGHT and of nothing else. A crew standing on a north-west curve facing
|
||||
* north reverses out through WEST, the way it came; `opposite('n')` is south, a port the curve
|
||||
* does not have, so backing up found nothing.
|
||||
*
|
||||
* Fixing only the forward case moved the problem rather than solving it: before, a crew that
|
||||
* rounded a curve could only back out; after, it could only carry on. A crew must be able to do
|
||||
* both, which is what makes a siding reachable and therefore what makes setting out a cut
|
||||
* possible at all.
|
||||
*/
|
||||
const s = game();
|
||||
addCard(s, at(0, 0), straight());
|
||||
addCard(s, at(0, 1), curve('nw')); // joins north and west
|
||||
addCard(s, at(1, 1), curve('se')); // the spur carries on north
|
||||
const id = placeTray(s, at(0, 0), []);
|
||||
s.trays.get(id)!.facing = 'e';
|
||||
s.clock.phase = 'localOps';
|
||||
s.clock.currentActor = 0;
|
||||
turnOf(s, 0).option = 'switch';
|
||||
|
||||
assert.ok(applyIntent(s, 0, { type: 'switch.move', trayId: id, to: at(0, 1), reverse: false }).ok);
|
||||
const { to } = movesFor(s, 0, id);
|
||||
assert.ok(
|
||||
to.some((c) => c.row === 0 && c.col === 0),
|
||||
'the crew cannot back off the curve onto the track it came from',
|
||||
);
|
||||
assert.ok(
|
||||
to.some((c) => c.row === 1 && c.col === 1),
|
||||
'the crew cannot carry on round the curve either',
|
||||
);
|
||||
// And backing up really does go there, rather than merely being offered.
|
||||
assert.ok(applyIntent(s, 0, { type: 'switch.move', trayId: id, to: at(0, 0), reverse: true }).ok);
|
||||
});
|
||||
|
||||
it('turns the DRAWN direction when the curve really does turn the engine round', () => {
|
||||
// South down a spur into a curve that turns west. The engine genuinely now points west, and the
|
||||
// board has to say so — this is the mirrored consist that was reported.
|
||||
@@ -1419,3 +1457,73 @@ describe('a train that rounds a curve points where the curve took it', () => {
|
||||
assert.equal(out.drawn, 'e', 'backing up changed which end the engine is on');
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('the switching job a player actually does: put a car in a siding, take an empty to an industry', () => {
|
||||
/**
|
||||
* REPORTED from play: "I'm trying to switch to get an empty car to an industry, but I can't drop
|
||||
* any cars on the siding first."
|
||||
*
|
||||
* Setting out a cut needs no Move and is refused almost nowhere — but you can only set out where
|
||||
* the train IS, and the Office square itself is barred (§A.4). So "I cannot drop" is nearly always
|
||||
* "I cannot GET there", and getting there means leaving the Running Track through a turnout and a
|
||||
* curve. That is exactly where `opposite(facing)` was wrong in both directions.
|
||||
*
|
||||
* This walks the whole errand rather than any one rule, because each individual rule passed its
|
||||
* own test while the errand was impossible.
|
||||
*/
|
||||
const curve = (arc: 'ne' | 'nw' | 'se' | 'sw'): TrackCard => ({
|
||||
geometry: { kind: 'track', geometry: 'curved', arc, hand: 'right' },
|
||||
baseOperationalRail: true, standing: [], facility: null, modifiers: [], enhancements: [],
|
||||
});
|
||||
const turnout = (o: { stem: 'n' | 's' | 'e' | 'w'; through: 'n' | 's' | 'e' | 'w'; diverge: 'n' | 's' | 'e' | 'w' }): TrackCard => ({
|
||||
geometry: { kind: 'track', geometry: 'turnout', turnout: o, hand: 'right' },
|
||||
baseOperationalRail: true, standing: [], facility: null, modifiers: [], enhancements: [],
|
||||
});
|
||||
|
||||
it('takes a cut off the Running Track into a siding and sets it out', () => {
|
||||
const s = game();
|
||||
// Running Track east from the Office, with a turnout diverging north onto a short siding.
|
||||
addCard(s, at(0, 0), straight());
|
||||
addCard(s, at(0, 1), turnout({ stem: 'w', through: 'e', diverge: 'n' }));
|
||||
addCard(s, at(0, 2), straight());
|
||||
addCard(s, at(1, 1), curve('se')); // up off the turnout, then east along the siding
|
||||
addCard(s, at(1, 2), straight());
|
||||
|
||||
const id = placeTray(s, at(0, 0), [
|
||||
{ type: 'boxcar', loaded: false },
|
||||
{ type: 'tank', loaded: false },
|
||||
] as never);
|
||||
s.trays.get(id)!.facing = 'e';
|
||||
s.clock.phase = 'localOps';
|
||||
s.clock.currentActor = 0;
|
||||
turnOf(s, 0).option = 'switch';
|
||||
|
||||
// Out of the Office and up onto the siding. A turnout is not somewhere a train may stop, so the
|
||||
// first square it can finish on is the curve beyond it.
|
||||
const reachable = movesFor(s, 0, id).to;
|
||||
assert.ok(
|
||||
reachable.some((c) => c.row === 1 && c.col === 1),
|
||||
'the siding is not reachable from the Running Track at all',
|
||||
);
|
||||
assert.ok(applyIntent(s, 0, { type: 'switch.move', trayId: id, to: at(1, 1), reverse: false }).ok);
|
||||
|
||||
// Set the cut out. This is the step that was reported as impossible.
|
||||
const drop = applyIntent(s, 0, { type: 'switch.dropCars', trayId: id, count: 1 });
|
||||
assert.ok(drop.ok, `setting out on the siding was refused: ${drop.ok ? '' : drop.code}`);
|
||||
assert.deepEqual(
|
||||
areaOf(s, 0).grid.get(coordKey(at(1, 1)))!.standing.map((c) => c.type),
|
||||
['tank'],
|
||||
'the car did not end up on the siding',
|
||||
);
|
||||
assert.equal(s.trays.get(id)!.consist.length, 1, 'the train still has the car it set out');
|
||||
|
||||
// And it can get back off the siding again, carrying the empty on toward an industry.
|
||||
const after = movesFor(s, 0, id).to;
|
||||
assert.ok(
|
||||
after.some((c) => c.row === 0 && c.col === 0) || after.some((c) => c.row === 0 && c.col === 2),
|
||||
'the crew is stranded on the siding — it cannot return to the Running Track',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user