projection.test.js (4278B)
1 const { describe, it } = require('node:test'); 2 const assert = require('node:assert/strict'); 3 const { 4 buildIsland, 5 pickPromptMove, 6 } = require('../../games/fire-truck/lib.js'); 7 8 const fixedRng = { pick: arr => arr[0], next: () => 0.2 }; 9 10 function bfsReachableRoads(grid, roads) { 11 const seen = new Set(); 12 const queue = [roads[0]]; 13 seen.add(roads[0].x + ',' + roads[0].y); 14 const D = { N: [0, -1], E: [1, 0], S: [0, 1], W: [-1, 0] }; 15 16 while (queue.length) { 17 const cell = queue.shift(); 18 for (const dir of ['N', 'E', 'S', 'W']) { 19 if (!cell.exits[dir]) continue; 20 const [dx, dy] = D[dir]; 21 const nx = cell.x + dx; 22 const ny = cell.y + dy; 23 const key = nx + ',' + ny; 24 if (!seen.has(key)) { 25 seen.add(key); 26 queue.push(grid[ny][nx]); 27 } 28 } 29 } 30 return seen.size; 31 } 32 33 describe('buildIsland()', () => { 34 it('has fixed rings', () => { 35 const island = buildIsland({ seed: 1 }); 36 for (let x = 0; x < 50; x++) { 37 assert.equal(island.grid[0][x].type, 'water'); 38 assert.equal(island.grid[49][x].type, 'water'); 39 } 40 for (let y = 0; y < 50; y++) { 41 assert.equal(island.grid[y][0].type, 'water'); 42 assert.equal(island.grid[y][49].type, 'water'); 43 } 44 for (let x = 1; x < 49; x++) { 45 assert.equal(island.grid[1][x].type, 'beach'); 46 assert.equal(island.grid[48][x].type, 'beach'); 47 } 48 for (let y = 1; y < 49; y++) { 49 assert.equal(island.grid[y][1].type, 'beach'); 50 assert.equal(island.grid[y][48].type, 'beach'); 51 } 52 for (let x = 2; x < 48; x++) { 53 assert.equal(island.grid[2][x].type, 'road'); 54 assert.equal(island.grid[47][x].type, 'road'); 55 } 56 for (let y = 2; y < 48; y++) { 57 assert.equal(island.grid[y][2].type, 'road'); 58 assert.equal(island.grid[y][47].type, 'road'); 59 } 60 }); 61 62 it('has only building or road in interior', () => { 63 const island = buildIsland({ seed: 2 }); 64 for (let y = 3; y <= 46; y++) { 65 for (let x = 3; x <= 46; x++) { 66 const t = island.grid[y][x].type; 67 assert.ok(t === 'building' || t === 'road', `expected building or road at ${x},${y}, got ${t}`); 68 } 69 } 70 }); 71 72 it('has a fully connected road graph', () => { 73 const island = buildIsland({ seed: 3 }); 74 const reachable = bfsReachableRoads(island.grid, island.roads); 75 assert.equal(reachable, island.roads.length); 76 }); 77 78 it('has no dead-end roads', () => { 79 const island = buildIsland({ seed: 4 }); 80 for (const cell of island.roads) { 81 const degree = Object.values(cell.exits).filter(Boolean).length; 82 assert.ok(degree >= 2, `dead-end at ${cell.x},${cell.y} with degree ${degree}`); 83 } 84 }); 85 86 it('has varied intersections', () => { 87 const island = buildIsland({ seed: 5 }); 88 let tCount = 0; 89 let fourCount = 0; 90 for (const cell of island.roads) { 91 if (cell.meta.kind === 't') tCount++; 92 if (cell.meta.kind === 'four') fourCount++; 93 } 94 const decisionCount = tCount + fourCount; 95 assert.ok(decisionCount > 0, 'expected some decision cells'); 96 const tRatio = tCount / decisionCount; 97 const fourRatio = fourCount / decisionCount; 98 assert.ok(tRatio >= 0.55, `expected t ratio >= 0.55, got ${tRatio.toFixed(3)}`); 99 assert.ok(fourRatio >= 0.06, `expected four-way ratio >= 0.06, got ${fourRatio.toFixed(3)}`); 100 }); 101 102 it('has average road degree at least 2.0', () => { 103 const island = buildIsland({ seed: 6 }); 104 const total = island.roads.reduce((sum, c) => sum + Object.values(c.exits).filter(Boolean).length, 0); 105 const avg = total / island.roads.length; 106 assert.ok(avg >= 2.0, `expected avg degree >= 2.0, got ${avg.toFixed(2)}`); 107 }); 108 109 it('has correct building count', () => { 110 const island = buildIsland({ seed: 8 }); 111 let interiorRoadCount = 0; 112 for (let y = 3; y <= 46; y++) { 113 for (let x = 3; x <= 46; x++) { 114 if (island.grid[y][x].type === 'road') interiorRoadCount++; 115 } 116 } 117 assert.equal(island.buildings.length + interiorRoadCount, 44 * 44); 118 }); 119 }); 120 121 describe('pickPromptMove()', () => { 122 it('always returns a legal move', () => { 123 assert.equal(pickPromptMove(['left', 'right'], fixedRng), 'left'); 124 assert.equal(pickPromptMove(['straight'], fixedRng), 'straight'); 125 }); 126 });