// The map is plain data: check it hangs together. import { test } from 'node:test'; import assert from 'node:assert/strict'; import { BUILDING } from '../src/world.ts'; import { PATH_LIST, PATHS, dataType } from '../src/sim.ts'; const L = BUILDING; const ids = new Set(L.machines.map(m => m.id)); const rooms = new Set(L.rooms.map(r => r.id)); test('machine ids are unique', () => { assert.equal(ids.size, L.machines.length); }); test('every machine sits in a real room and has a spot on the network view', () => { for (const m of L.machines) { assert.ok(rooms.has(m.room), `${m.id} is in unknown room ${m.room}`); assert.ok(L.net.nodes[m.id] || m.net, `${m.id} has no network position`); } }); test('links only name machines that exist', () => { for (const [a, b] of L.links) assert.ok(ids.has(a) && ids.has(b), `bad link ${a}-${b}`); }); test('every machine can be reached from home', () => { const adj: Record = {}; const lines = L.map.regions.flatMap(r => r.line ? [r.line] : []); // lit by a deal, mid-game for (const [a, b] of [...L.links, ...lines]) { (adj[a] ??= []).push(b); (adj[b] ??= []).push(a); } const seen = new Set(['home']), queue = ['home']; while (queue.length) for (const n of adj[queue.shift()!] ?? []) if (!seen.has(n)) { seen.add(n); queue.push(n); } for (const m of L.machines) assert.ok(seen.has(m.id), `${m.id} is cut off`); }); test('a region out of reach is cut off until its line is lit', () => { const site = (id: string) => L.map.sites.find(s => s.rooms.includes(L.machines.find(m => m.id === id)!.room)); for (const r of L.map.regions) { const here = L.map.sites.filter(s => s.region === r.name); if (r.reachable) { assert.ok(!r.line, `${r.name} needs no line`); continue; } if (!here.length) continue; assert.ok(r.line && ids.has(r.line[0]) && ids.has(r.line[1]), `${r.name} has sites but no line to light`); assert.equal(site(r.line[1])?.region, r.name, `${r.name}'s line lands in it`); for (const [a, b] of L.links) assert.equal(site(a)?.region === r.name, site(b)?.region === r.name, `${a}-${b} crosses into ${r.name} from the start`); } }); test('every capability path has something that feeds it', () => { for (const p of PATH_LIST) assert.ok(L.machines.some(m => m.data && dataType(m) === PATHS[p].from), `nothing trains ${p}`); }); test('people sit at desks that exist', () => { for (const p of L.people) assert.ok(ids.has(p.desk), `${p.id} has no desk`); }); // The map: each site's box holds its rooms, and sites keep room for their names. const sites = L.map.sites, siteOfRoom = (r: string) => sites.find(s => s.rooms.includes(r)); const inside = (s: { x: number; y: number; w: number; h: number }, x: number, y: number) => x >= s.x && x <= s.x + s.w && y >= s.y && y <= s.y + s.h; test('site ids are unique and every room belongs to one site', () => { assert.equal(new Set(sites.map(s => s.id)).size, sites.length); for (const r of L.rooms) assert.equal(sites.filter(s => s.rooms.includes(r.id)).length, 1, `room ${r.id} is in ${sites.filter(s => s.rooms.includes(r.id)).length} sites`); }); test("a site's box holds its rooms and machines", () => { for (const s of sites) for (const r of s.rooms) { const n = L.net.subnets[r]; assert.ok(n, `${s.id} names room ${r} with no subnet`); assert.ok(inside(s, n.x, n.y) && inside(s, n.x + n.w, n.y + n.h), `room ${r} sticks out of ${s.id}`); } for (const m of L.machines) { const s = siteOfRoom(m.room), at = L.net.nodes[m.id]; if (s && at) assert.ok(inside(s, ...at), `${m.id} sits outside ${s.id}`); } }); test("rooms in a site don't overlap", () => { for (const s of sites) for (const a of s.rooms) for (const b of s.rooms) { if (a >= b) continue; const p = L.net.subnets[a], q = L.net.subnets[b]; assert.ok(p.x + p.w <= q.x || q.x + q.w <= p.x || p.y + p.h <= q.y || q.y + q.h <= p.y, `${a} and ${b} overlap`); } }); test('sites keep clear of each other, with room above for their names', () => { for (const a of sites) for (const b of sites) { if (a === b) continue; // b's clear zone: 120 to the sides and below, 200 above for its name const x0 = b.x - 120, x1 = b.x + b.w + 120, y0 = b.y - 200, y1 = b.y + b.h + 120; assert.ok(a.x + a.w <= x0 || a.x >= x1 || a.y + a.h <= y0 || a.y >= y1, `${a.id} is too close to ${b.id}`); } }); test('every site has a line out wired to another site', () => { const site = (id: string) => siteOfRoom(L.machines.find(m => m.id === id)!.room)?.id ?? 'office'; for (const s of sites) { if (s.id === 'office') continue; assert.ok(L.links.some(([a, b]) => (site(a) === s.id) !== (site(b) === s.id)), `${s.id} has no line to another site`); } });