fix tracks to all be EW. no more NS tracks
This commit is contained in:
+91
-4
@@ -209,7 +209,7 @@ describe('the board shows freight work happening', () => {
|
||||
const game = newGame(5);
|
||||
const area = game.state.officeAreas.get(0)!;
|
||||
area.grid.set('-1,0', {
|
||||
geometry: { kind: 'facility', facility: 'mineTipple', axis: 'ns' },
|
||||
geometry: { kind: 'facility', facility: 'mineTipple' },
|
||||
baseOperationalRail: true, standing: [], modifiers: [], enhancements: [],
|
||||
facility: {
|
||||
kind: 'freight', subtype: 'mineTipple',
|
||||
@@ -248,7 +248,7 @@ describe('the board shows freight work happening', () => {
|
||||
const game = newGame(5);
|
||||
const area = game.state.officeAreas.get(0)!;
|
||||
area.grid.set('-1,0', {
|
||||
geometry: { kind: 'facility', facility: 'mineTipple', axis: 'ns' },
|
||||
geometry: { kind: 'facility', facility: 'mineTipple' },
|
||||
baseOperationalRail: true, standing: [], modifiers: [], enhancements: [],
|
||||
facility: {
|
||||
kind: 'freight', subtype: 'mineTipple',
|
||||
@@ -270,7 +270,7 @@ describe('the board shows freight work happening', () => {
|
||||
const game = newGame(5);
|
||||
const area = game.state.officeAreas.get(0)!;
|
||||
area.grid.set('-1,0', {
|
||||
geometry: { kind: 'facility', facility: 'mineTipple', axis: 'ns' },
|
||||
geometry: { kind: 'facility', facility: 'mineTipple' },
|
||||
baseOperationalRail: true, standing: [], modifiers: [], enhancements: ['overpass'],
|
||||
facility: {
|
||||
kind: 'freight', subtype: 'mineTipple',
|
||||
@@ -295,6 +295,77 @@ describe('the board shows freight work happening', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('the board draws the printed card (docs/tracks.png)', () => {
|
||||
/** One card, alone, so the measurements are in card coordinates. */
|
||||
const draw = (links: string[]): { x1: number; y1: number; x2: number; y2: number }[] => {
|
||||
const cell = {
|
||||
row: 0, col: 0, kind: 'trk', label: 'x', running: true, enhancements: [],
|
||||
tray: null, cars: [], facility: null, links, what: '',
|
||||
};
|
||||
const svg = officeSvg([cell] as never, 0);
|
||||
return [...svg.matchAll(/<line class="bs-rail" x1="([-\d.]+)" y1="([-\d.]+)" x2="([-\d.]+)" y2="([-\d.]+)"\/>/g)]
|
||||
.map((m) => ({ x1: Number(m[1]), y1: Number(m[2]), x2: Number(m[3]), y2: Number(m[4]) }));
|
||||
};
|
||||
|
||||
/** Undirected segment angles in degrees, deduplicated. 0 is horizontal. */
|
||||
const angles = (links: string[]): number[] => [
|
||||
...new Set(
|
||||
draw(links).map((r) => {
|
||||
const a = (Math.atan2(r.y2 - r.y1, r.x2 - r.x1) * 180) / Math.PI;
|
||||
return Math.round((((a % 180) + 180) % 180) * 10) / 10;
|
||||
}),
|
||||
),
|
||||
].sort((a, b) => a - b);
|
||||
|
||||
it('runs the through track dead centre, edge to edge', () => {
|
||||
// The measurement from the printed sheet: on every one of its nine rows the east-west rail sits
|
||||
// at the card's exact vertical middle. It used to be drawn at 31% down the card, on the belief
|
||||
// that the art put it along the top edge.
|
||||
const rails = draw(['ew']);
|
||||
assert.equal(rails.length, 2, 'a track is two rails');
|
||||
const H = 96;
|
||||
for (const r of rails) {
|
||||
assert.equal(r.y1, r.y2, 'the through track must be level');
|
||||
assert.ok(Math.abs(r.y1 - H / 2) <= 3, `through rail at y=${r.y1}, not the card's middle (${H / 2})`);
|
||||
assert.deepEqual([Math.min(r.x1, r.x2), Math.max(r.x1, r.x2)], [0, 166], 'it must span the whole card');
|
||||
}
|
||||
});
|
||||
|
||||
it('leaves at exactly 45°, through the middle of the north or south edge', () => {
|
||||
for (const links of [['ne'], ['nw'], ['se'], ['sw'], ['ew', 'ws'], ['ew', 'en']]) {
|
||||
const found = angles(links);
|
||||
assert.ok(found.includes(0), `${links} has no level run along the centre line`);
|
||||
assert.ok(
|
||||
found.includes(45) || found.includes(135),
|
||||
`${links} draws its leg at ${found.join('/')}° — the card prints 45°`,
|
||||
);
|
||||
assert.equal(found.length, 2, `${links} draws ${found.join('/')}° — a leg is a run and one 45° angle`);
|
||||
}
|
||||
});
|
||||
|
||||
it('draws the two diagonals as two different diagonals', () => {
|
||||
// The matching rule made visible. `ne` and `sw` lie on one line and `nw` and `se` on the other,
|
||||
// so a stacked pair on the same diagonal reads as one unbroken rail and a mismatched pair reads
|
||||
// as the V it is. Drawn alike, the picture would claim a join the engine refuses.
|
||||
assert.deepEqual(angles(['ne']), angles(['sw']), 'ne and sw must lie on the same diagonal');
|
||||
assert.deepEqual(angles(['nw']), angles(['se']), 'nw and se must lie on the same diagonal');
|
||||
assert.notDeepEqual(angles(['ne']), angles(['nw']), 'the two diagonals must be distinguishable');
|
||||
});
|
||||
|
||||
it('meets the card edge at its midpoint, so abutting cards line up', () => {
|
||||
// A leg that met the edge anywhere else would join to nothing, however right its angle.
|
||||
const W = 166;
|
||||
const H = 96;
|
||||
// Only the sloping segments, and only the end of each that lands on the south edge.
|
||||
const atEdge = draw(['sw'])
|
||||
.filter((r) => r.y1 !== r.y2)
|
||||
.map((r) => (Math.abs(r.y1 - H) < Math.abs(r.y2 - H) ? r.x1 : r.x2));
|
||||
assert.equal(atEdge.length, 2, 'the leg is two rails');
|
||||
const mid = (atEdge[0]! + atEdge[1]!) / 2;
|
||||
assert.ok(Math.abs(mid - W / 2) < 0.5, `the leg crosses the south edge at x=${mid}, not the middle (${W / 2})`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('board highlighting', () => {
|
||||
it('gives every spot a coordinate to highlight', () => {
|
||||
const game = newGame(555);
|
||||
@@ -316,6 +387,22 @@ describe('board highlighting', () => {
|
||||
const game = newGame(555);
|
||||
submit(game, actionGroups(game).options.find((o) => o.type === 'localOps.choose' && o.option === 'draw')!);
|
||||
|
||||
/**
|
||||
* Open the district first, with a TURNOUT on the Running Track.
|
||||
*
|
||||
* Every office and industry card is a plain east-west straight and the Office carries no stub,
|
||||
* so on the opening board the only legal squares are the two Limits signs — on the Running Track
|
||||
* itself, inside the existing bounds, and this test would prove nothing. A turnout is what puts
|
||||
* a square OFF the main in reach. The lay flag is then cleared rather than playing out a second
|
||||
* turn: the subject here is the drawn canvas, not the turn economy.
|
||||
*/
|
||||
const lay = actionGroups(game).options.find(
|
||||
(o) => o.type === 'track.lay' && o.geometry === 'turnout' && o.placement !== undefined,
|
||||
);
|
||||
assert.ok(lay, 'no turnout could be laid — the district can never open');
|
||||
assert.ok(submit(game, lay), 'the turnout was refused');
|
||||
game.state.turn.laidThisTurn = false;
|
||||
|
||||
const cells = view(game).cells;
|
||||
const minCol = Math.min(...cells.map((c) => c.col));
|
||||
const maxCol = Math.max(...cells.map((c) => c.col));
|
||||
@@ -489,7 +576,7 @@ describe('the page explains itself', () => {
|
||||
const game = newGame(9);
|
||||
const area = game.state.officeAreas.get(0)!;
|
||||
const industry = {
|
||||
geometry: { kind: 'facility' as const, facility: 'grocersWarehouse' as const, axis: 'ew' as const },
|
||||
geometry: { kind: 'facility' as const, facility: 'grocersWarehouse' as const },
|
||||
baseOperationalRail: true, standing: [], modifiers: [], enhancements: [],
|
||||
facility: {
|
||||
kind: 'freight' as const, subtype: 'grocersWarehouse',
|
||||
|
||||
Reference in New Issue
Block a user