import fs from 'node:fs'; import assert from 'node:assert/strict'; import { generateMap } from '../src/mapPipeline.js'; function prefectureMunicipalityCounts(map) { const groups = new Map(); for (let i = 0; i < map.adminId.length; i++) { if (map.sea[i] || map.adminId[i] < 0 || map.prefectureRegionId[i] < 0) continue; let set = groups.get(map.prefectureRegionId[i]); if (!set) groups.set(map.prefectureRegionId[i], set = new Set()); set.add(map.adminId[i]); } return [...groups.values()].map((set) => set.size); } function pathLength(paths) { return (paths || []).reduce((sum, path) => sum + (path?.length || 0), 0); } function maxSeaRun(path, sea, width) { let run = 0, best = 0; for (const [x, y] of path || []) { const isSea = x < 0 || y < 0 || x >= width || y >= sea.length / width || sea[y * width + x]; if (isSea) { run++; best = Math.max(best, run); } else run = 0; } return best; } function maxExtremeTurnRun(path) { let run = 0, best = 0; for (let k = 2; k < (path?.length || 0) - 2; k += 2) { const a = path[k - 2], b = path[k], c = path[k + 2]; const ux = b[0] - a[0], uy = b[1] - a[1], vx = c[0] - b[0], vy = c[1] - b[1]; const ud = Math.hypot(ux, uy), vd = Math.hypot(vx, vy); if (!ud || !vd) { run = 0; continue; } const dot = Math.max(-1, Math.min(1, (ux * vx + uy * vy) / (ud * vd))); const angle = Math.acos(dot) * 180 / Math.PI; if (angle >= 82) { run++; best = Math.max(best, run); } else run = 0; } return best; } function pathTangent(path, k) { const a = path[Math.max(0, k - 2)], b = path[Math.min(path.length - 1, k + 2)]; const dx = b[0] - a[0], dy = b[1] - a[1], d = Math.hypot(dx, dy) || 1; return [dx / d, dy / d]; } function longestDistinctParallelRun(paths, radius) { let worst = 0; for (let a = 0; a < (paths?.length || 0); a++) for (let b = a + 1; b < paths.length; b++) { let run = 0; for (let k = 0; k < paths[a].length; k += 2) { const p = paths[a][k], t = pathTangent(paths[a], k); let parallel = false; for (let q = 0; q < paths[b].length; q += 2) { const z = paths[b][q], dx = p[0] - z[0], dy = p[1] - z[1], d2 = dx * dx + dy * dy; // Exact shared alignment is intentional multiplexing, not parallel duplication. if (d2 < 0.75 || d2 > radius * radius) continue; const u = pathTangent(paths[b], q); if (Math.abs(t[0] * u[0] + t[1] * u[1]) >= 0.90) { parallel = true; break; } } run = parallel ? run + 1 : 0; worst = Math.max(worst, run); } } return worst; } for (const seed of [114514, 999]) { const map = generateMap(seed, { onProgress() {}, initialGenerationOverscan: false }); const populations = (map.adminCenters || []).map((p) => Number(p.municipalityPopulation || 0)); assert(populations.length >= 30, `seed ${seed}: enough municipalities`); assert(Math.min(...populations) >= 1000, `seed ${seed}: municipality population floor is at least ~1000`); assert(populations.filter((p) => p > 10000).length / populations.length <= 0.30, `seed ${seed}: >10k municipalities are not dominant`); const names = (map.adminCenters || []).map((p) => String(p.name || '')); assert(names.filter((n) => n.endsWith('市')).length / names.length <= 0.35, `seed ${seed}: 市 does not dominate municipality types`); const prefCounts = prefectureMunicipalityCounts(map); assert(prefCounts.length >= 2 && Math.min(...prefCounts) >= 10, `seed ${seed}: no tiny five-municipality prefecture`); const finalTransport = map.transportDebug?.postAdminTransportFinalization || {}; assert.equal(finalTransport.postDedupeMajorCityService?.missing?.length || 0, 0, `seed ${seed}: all same-land major cities have national/rail/expressway service`); assert.equal(map.transportDebug?.layers?.syntheticUrbanStreetMeshDisabled, true, `seed ${seed}: synthetic urban grid is disabled`); assert.equal(map.transportDebug?.layers?.urbanStreetMeshes?.algorithm, 'existing-local-access', `seed ${seed}: urban local roads reuse the existing local-access algorithm`); const nationalLen = pathLength(map.nationalRoads); const railLen = pathLength([...(map.railways || []), ...(map.branchRailways || [])]); assert(nationalLen > 0 && railLen / nationalLen >= 0.65 && railLen / nationalLen <= 1.02, `seed ${seed}: rail density is high but approximately below national-road density`); for (const path of map.expressways || []) { assert(maxSeaRun(path, map.sea, map.width) <= 3, `seed ${seed}: expressway does not ignore a large strait`); assert(maxExtremeTurnRun(path) <= 1, `seed ${seed}: expressway avoids repeated extreme turns`); } const branch = finalTransport.finalExpresswayBranchSimplification; assert(branch && branch.branchNodesAfter <= branch.branchNodesBefore, `seed ${seed}: expressway branch simplifier never increases branching`); assert(finalTransport.postServiceNationalSharedAlignment && finalTransport.postServiceExpresswaySharedAlignment, `seed ${seed}: final same-direction corridor sharing pass is active`); assert(longestDistinctParallelRun(map.expressways || [], 4) <= 5, `seed ${seed}: expressways do not remain in long close parallel corridors`); assert(longestDistinctParallelRun(map.nationalRoads || [], 3) <= 5, `seed ${seed}: national roads do not remain in long close parallel corridors`); } const featureSource = fs.readFileSync(new URL('../src/mapFeatures.js', import.meta.url), 'utf8'); const rendererSource = fs.readFileSync(new URL('../src/renderer.js', import.meta.url), 'utf8'); assert(/allowMillionPlusMap\s*=.*<\s*0\.50/.test(featureSource), 'million-plus city map-level probability gate is 50%'); assert(rendererSource.includes('municipalFallbackLabels') && rendererSource.includes('!p.suppressMunicipalLabel') && rendererSource.includes('!p.seatOutsideVisibleCrop'), 'renderer suppresses municipal-seat labels whose true seat is outside the published crop instead of pinning them to a visible representative cell'); const generationWorkerSource = fs.readFileSync(new URL('../src/generationWorker.js', import.meta.url), 'utf8'); const cropSource = fs.readFileSync(new URL('../src/initialGenerationCrop.js', import.meta.url), 'utf8'); assert(generationWorkerSource.includes('__JAPAN_MAP_GENERATION_DIMENSIONS__') && generationWorkerSource.includes('cropInitialGenerationMap'), 'production initial generation uses a genuinely larger raster then crops the center'); assert(cropSource.includes('literal-hidden-raster-center-crop-v1'), 'published initial map records literal hidden-raster overscan provenance'); assert(cropSource.includes('suppressMunicipalLabel: true') && cropSource.includes('seatOutsideVisibleCrop: true'), 'crop metadata marks off-screen municipal seats as non-label anchors'); console.log('All r11.4 transport/demography/overscan regression checks passed.');