This commit is contained in:
33333-33333 2026-08-11 21:51:07 +09:00
commit de7c6c32bd
123 changed files with 13201 additions and 6213 deletions

View file

@ -1,3 +1,4 @@
import assert from "node:assert/strict";
import { Worker } from "node:worker_threads";
import { performance } from "node:perf_hooks";
import { generateMap } from "../src/mapPipeline.js";
@ -9,15 +10,16 @@ const baseline = createWorldMap(initial);
const rect = { x0: 20, y0: 120, x1: 180, y1: 230 };
const terrainType = initial.terrainTemplate?.terrainType || initial.terrainDebug?.terrainType || "auto";
function runWorkerCase(patchMode, id) {
function runWorkerCase(patchMode, id, overrides = {}) {
return new Promise((resolve, reject) => {
const worker = new Worker(new URL("./browser-worker-node-shim.mjs", import.meta.url), { type: "module" });
const timer = setTimeout(async () => {
try { await worker.terminate(); } catch {}
reject(new Error(`${patchMode} coverage regression timed out`));
}, 60_000);
}, overrides.timeoutMs ?? 90_000);
const startedAt = performance.now();
const seed = 123;
const seed = overrides.seed ?? 123;
const candidatePlan = overrides.candidatePlan || [{ candidateId: `coverage-${patchMode}:0`, candidateOrdinal: 1, variant: 0, seed }];
worker.on("error", reject);
worker.on("message", async (message) => {
if (message.id !== id || message.type === "progress") return;
@ -35,7 +37,7 @@ function runWorkerCase(patchMode, id) {
variant: 0,
seed,
maxQualityRetries: 0,
qualityTerrainAttempts: 1,
qualityTerrainAttempts: candidatePlan.length,
acceptBestAvailableQuality: false,
includeSeamVisualization: false,
},
@ -45,16 +47,46 @@ function runWorkerCase(patchMode, id) {
committedRevision: 1,
workerEpoch: 1,
executionAttempt: 1,
totalCandidateCount: 1,
candidatePlan: [{ candidateId: `coverage-${patchMode}:0`, candidateOrdinal: 1, variant: 0, seed }],
totalCandidateCount: candidatePlan.length,
candidatePlan,
...(overrides.search || {}),
},
});
});
}
const bestOfPlan = [0, 1, 2].map((variant, index) => ({
candidateId: `coverage-bestof:${variant}`,
candidateOrdinal: index + 1,
variant,
seed: (123 + variant) >>> 0,
}));
const { message: bestOfMessage } = await runWorkerCase("expansion", 3, {
candidatePlan: bestOfPlan,
search: {
draftSelection: true,
selectBestCandidate: true,
parallelDrafts: false,
resolvedPatchMode: "expansion",
},
});
assert.equal(bestOfMessage.ok, true, bestOfMessage.error || bestOfMessage.code || "best-of worker failed");
const bestOfResult = bestOfMessage.result || {};
assert.equal(bestOfResult.ok, true, bestOfResult.reason || bestOfResult.code || "best-of candidate search failed");
assert.equal(bestOfResult.searchStatus, "succeeded");
assert.equal(bestOfResult.bestOfCandidates, true, "best-of selection remains enabled");
assert.equal(bestOfResult.draftSelection?.enabled, true, "Branch-and-Bound ranking remains enabled");
assert.equal(Number(bestOfResult.candidateUnmappedActiveCells || 0), 0, "best candidate covers every active write cell");
assert.ok((bestOfResult.searchAttempts || []).length >= bestOfPlan.length, "every planned candidate remains visible to selection");
assert.equal((bestOfResult.searchAttempts || []).some((attempt) => attempt.code === "candidate-execution-error" && /did not cover/i.test(attempt.reason || "")), false,
"parent operation context never leaks into the canonical internal tile");
for (const [index, patchMode] of ["auto", "expansion"].entries()) {
const { message, elapsedMs } = await runWorkerCase(patchMode, index + 1);
const result = message.result || {};
const topology = result.candidateQuality?.finalMerge?.transportTopology;
const prefectureCoherence = result.candidateQuality?.finalMerge?.prefectureCoherence;
const demandedTopologyConnected = Object.values(topology?.byClass || {}).every((entry) => entry?.hardPass !== false);
const ok = message.ok === true
&& result.ok === true
&& result.searchStatus === "succeeded"
@ -62,7 +94,10 @@ for (const [index, patchMode] of ["auto", "expansion"].entries()) {
&& result.tiledExpansion === true
&& Number(result.tileCount) === 1
&& Number(result.candidateUnmappedActiveCells || 0) === 0
&& result.seamDiagnostics?.hardPass === true;
&& result.seamDiagnostics?.hardPass === true
&& topology?.hardPass === true
&& demandedTopologyConnected
&& prefectureCoherence?.hardPass === true;
console.log(JSON.stringify({
patchMode,
ok,
@ -75,6 +110,9 @@ for (const [index, patchMode] of ["auto", "expansion"].entries()) {
tileCount: Number(result.tileCount || 0),
candidateUnmappedActiveCells: Number(result.candidateUnmappedActiveCells || 0),
seamPass: result.seamDiagnostics?.hardPass === true,
transportTopologyPass: topology?.hardPass === true,
transportTopology: topology?.byClass || null,
prefectureCoherencePass: prefectureCoherence?.hardPass === true,
reason: result.reason || message.error || null,
}, null, 2));
if (!ok) process.exitCode = 1;

View file

@ -1,19 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Additional generation browser E2E</title>
<style>
body { margin: 0; padding: 24px; background: #111827; color: #e5e7eb; font: 14px/1.5 ui-monospace, monospace; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; padding: 18px; border-radius: 10px; background: #030712; }
[data-status="pass"] pre { border: 2px solid #22c55e; }
[data-status="fail"] pre { border: 2px solid #ef4444; }
</style>
</head>
<body>
<h1>Additional generation browser E2E</h1>
<pre id="result">RUNNING</pre>
<script type="module" src="./additional-generation-e2e.js"></script>
</body>
</html>

View file

@ -1,231 +0,0 @@
import { createWorldMap } from "../src/worldMap.js";
const params = new URLSearchParams(location.search);
const resultEl = document.getElementById("result");
const seed = (Number(params.get("seed")) || 114514) >>> 0;
const startVariant = (Number(params.get("variant")) || 0) >>> 0;
const candidateLimit = Math.max(1, Math.min(3, Number(params.get("candidates")) || 2));
const selectionWidth = Math.max(48, Math.floor(Number(params.get("width")) || 60));
const selectionHeight = Math.max(48, Math.floor(Number(params.get("height")) || 60));
const patchBudgetMs = Math.max(1000, Number(params.get("budgetMs")) || 60000);
function deriveSeed(worldSeed, terrainType, variant) {
let h = (worldSeed >>> 0) ^ 0x9e3779b9;
h = Math.imul(h ^ (variant >>> 0), 668265263) >>> 0;
for (const ch of String(terrainType || "auto")) h = Math.imul(h ^ ch.charCodeAt(0), 16777619) >>> 0;
return h >>> 0;
}
function waitForGeneration(worker) {
return new Promise((resolve, reject) => {
const id = 1;
const progress = [];
const onMessage = (event) => {
if (event.data?.id !== id) return;
if (event.data.type === "progress") {
progress.push({ at: performance.now(), ...(event.data.progress || event.data.event || {}) });
return;
}
worker.removeEventListener("message", onMessage);
if (event.data.ok) resolve({ map: event.data.map, progress });
else reject(new Error(event.data.error || "Initial generation failed"));
};
worker.addEventListener("message", onMessage);
worker.addEventListener("error", (event) => reject(new Error(event.message || "Initial generation Worker crashed")), { once: true });
worker.postMessage({ id, seed, options: { terrainType: params.get("terrain") || "auto" } });
});
}
function waitForApplyAck(worker, patch) {
return new Promise((resolve, reject) => {
const applyToken = patch.result?.applyToken;
if (!applyToken) {
reject(new Error("Accepted patch did not provide a transactional Apply token."));
return;
}
const ackId = `e2e-apply-${Date.now()}`;
const timer = setTimeout(() => reject(new Error("Transactional Apply ACK timed out.")), 30000);
const onMessage = (event) => {
const data = event.data || {};
if (data.type !== "patch-apply-ack-result" || data.ackId !== ackId) return;
clearTimeout(timer);
worker.removeEventListener("message", onMessage);
if (data.ok) resolve(data);
else reject(new Error(data.error || "Transactional Apply ACK failed."));
};
worker.addEventListener("message", onMessage);
worker.postMessage({
type: "patch-apply-ack",
ackId,
applyToken,
baseCommittedRevision: 1,
committedRevision: 2,
});
});
}
function heapSnapshot(label) {
return performance.memory ? {
label,
usedJSHeapSize: performance.memory.usedJSHeapSize,
totalJSHeapSize: performance.memory.totalJSHeapSize,
jsHeapSizeLimit: performance.memory.jsHeapSizeLimit,
} : null;
}
function waitForPatch(worker, message) {
return new Promise((resolve, reject) => {
const progress = [];
const startedAt = performance.now();
const onMessage = (event) => {
if (event.data?.id !== message.id) return;
if (event.data.type === "progress") {
progress.push({ at: performance.now(), ...event.data.progress });
return;
}
worker.removeEventListener("message", onMessage);
if (event.data.ok) resolve({ ...event.data, progress, wallMs: performance.now() - startedAt });
else reject(new Error(event.data.error || "Patch Worker failed"));
};
worker.addEventListener("message", onMessage);
worker.addEventListener("messageerror", () => reject(new Error("Patch result could not be deserialized")), { once: true });
worker.addEventListener("error", (event) => reject(new Error(event.message || "Patch Worker crashed")), { once: true });
worker.postMessage(message);
});
}
function maxProgressGap(progress, start, end) {
const times = [start, ...(progress || []).map((entry) => entry.at), end];
let max = 0;
for (let index = 1; index < times.length; index++) max = Math.max(max, times[index] - times[index - 1]);
return max;
}
async function main() {
const heap = [heapSnapshot("start")].filter(Boolean);
const initialWorker = new Worker(new URL("../src/generationWorker.js", import.meta.url), { type: "module" });
const initialStartedAt = performance.now();
const initial = await waitForGeneration(initialWorker);
const afterInitialHeap = heapSnapshot("after-initial");
if (afterInitialHeap) heap.push(afterInitialHeap);
const initialEndedAt = performance.now();
initialWorker.terminate();
const world = createWorldMap(initial.map);
const rect = {
x0: world.originX + Math.max(0, Math.floor((258 - selectionWidth) / 2)),
y0: world.originY + Math.max(0, Math.floor((183 - selectionHeight) / 2)),
x1: world.originX + Math.max(0, Math.floor((258 - selectionWidth) / 2)) + selectionWidth,
y1: world.originY + Math.max(0, Math.floor((183 - selectionHeight) / 2)) + selectionHeight,
};
if (params.get("shape") === "lasso") {
const insetX = Math.max(4, Math.floor(selectionWidth * 0.16));
const insetY = Math.max(4, Math.floor(selectionHeight * 0.16));
rect.kind = "lasso";
rect.polygon = [
{ x: rect.x0 + insetX, y: rect.y0 },
{ x: rect.x1 - 1, y: rect.y0 + insetY },
{ x: rect.x1 - insetX, y: rect.y1 - 1 },
{ x: rect.x0, y: rect.y1 - insetY },
];
}
const terrainType = params.get("patchTerrain") || initial.map.terrainTemplate?.terrainType || "auto";
const candidatePlan = Array.from({ length: candidateLimit }, (_, index) => {
const variant = (startVariant + index) >>> 0;
return { candidateId: `e2e:${variant}`, candidateOrdinal: index + 1, variant, seed: deriveSeed(world.seed, terrainType, variant) };
});
const patchWorker = new Worker(new URL("../src/mapPatchWorker.js", import.meta.url), { type: "module" });
const patchStartedAt = performance.now();
const patch = await waitForPatch(patchWorker, {
id: 2,
world,
rect,
options: {
patchMode: params.get("mode") || "regeneration",
terrainType,
variant: startVariant,
seed: candidatePlan[0].seed,
maxQualityRetries: 0,
qualityTerrainAttempts: 1,
acceptBestAvailableQuality: false,
includeSeamVisualization: false,
},
search: {
searchId: "browser-e2e",
operationId: "browser-e2e",
committedRevision: 1,
workerEpoch: 1,
candidatePlan,
totalCandidateCount: candidateLimit,
},
});
const patchEndedAt = performance.now();
const afterPatchHeap = heapSnapshot("after-patch");
if (afterPatchHeap) heap.push(afterPatchHeap);
if (patch.result?.ok !== true) {
throw new Error(`No accepted preview was produced (${patch.result?.code || patch.result?.searchStatus || "unknown rejection"}).`);
}
const applyAck = await waitForApplyAck(patchWorker, patch);
const afterApplyHeap = heapSnapshot("after-apply-ack");
if (afterApplyHeap) heap.push(afterApplyHeap);
patchWorker.terminate();
const attempts = patch.result?.searchAttempts || [];
const boundedEvents = patch.progress.filter((entry) => entry.boundedWork === true);
const invalidBoundedEvents = boundedEvents.filter((entry) => !Number.isFinite(entry.completed)
|| !Number.isFinite(entry.total) || entry.completed < 0 || entry.total < 0 || entry.completed > entry.total);
const assertions = {
workerTransportSucceeded: patch.ok === true,
candidateAuditPresent: attempts.length > 0,
previewPublished: patch.result?.ok === true && patch.result?.searchStatus === "succeeded",
boundedAttempts: attempts.length <= candidateLimit,
fullPipelineTimingsPresent: attempts.every((attempt) => (attempt.patchTimings || []).some((entry) => entry.key === "candidate" || entry.key === "tiled-total" || entry.key === "tiled-regeneration-total")),
noBestAvailableAcceptance: attempts.every((attempt) => attempt.candidateQuality?.acceptedAsBestAvailable !== true),
boundedProgressValid: boundedEvents.length > 0 && invalidBoundedEvents.length === 0,
applyAckHashMatches: applyAck.mirrorHash === patch.result?.acceptedWorldHash,
patchBudgetMet: patch.wallMs < patchBudgetMs,
};
const report = {
status: Object.values(assertions).every(Boolean) ? "pass" : "fail",
environment: {
userAgent: navigator.userAgent,
hardwareConcurrency: navigator.hardwareConcurrency || null,
deviceMemoryGiB: navigator.deviceMemory || null,
crossOriginIsolated,
},
workload: {
seed, startVariant, candidateLimit, selection: rect, selectionWidth, selectionHeight,
selectionShape: rect.kind || "rect", terrainType, patchMode: params.get("mode") || "regeneration",
plannedTileUpperBound: Math.ceil(selectionWidth / Math.floor(258 / 1.72)) * Math.ceil(selectionHeight / Math.floor(183 / 1.72)),
},
timing: {
initialWallMs: initialEndedAt - initialStartedAt,
patchWallMs: patch.wallMs,
patchBudgetMs,
initialMaxProgressGapMs: maxProgressGap(initial.progress, initialStartedAt, initialEndedAt),
patchMaxProgressGapMs: maxProgressGap(patch.progress, patchStartedAt, patchEndedAt),
},
memory: heap.length ? {
snapshots: heap,
peakUsedJSHeapSize: Math.max(...heap.map((entry) => entry.usedJSHeapSize)),
} : null,
assertions,
result: {
ok: patch.result?.ok === true,
code: patch.result?.code || null,
searchStatus: patch.result?.searchStatus || null,
actualVariant: patch.result?.actualVariant ?? null,
nextVariant: patch.result?.nextVariant ?? null,
attempts,
acceptedWorldHash: patch.result?.acceptedWorldHash || null,
applyAck,
},
};
document.documentElement.dataset.status = report.status;
resultEl.textContent = JSON.stringify(report, null, 2);
}
try {
await main();
} catch (error) {
document.documentElement.dataset.status = "fail";
resultEl.textContent = JSON.stringify({ status: "fail", infrastructureError: error?.message || String(error), stack: error?.stack || null }, null, 2);
}

View file

@ -2,50 +2,30 @@ import { Worker } from "node:worker_threads";
import { performance } from "node:perf_hooks";
import { generateMap } from "../src/mapPipeline.js";
import { createWorldMap } from "../src/worldMap.js";
import { buildMaximumProductionLasso, derivePatchSeed } from "./production-fixtures.mjs";
const budgetMs = Math.max(1, Number(process.env.PATCH_MAX_WORKER_BUDGET_MS) || 60_000);
const timeoutMs = Math.max(90_000, budgetMs + 30_000);
const worldSeed = Number(process.env.PATCH_TEST_WORLD_SEED ?? 114514) >>> 0;
const candidateVariant = Number(process.env.PATCH_TEST_VARIANT ?? 0) >>> 0;
function deriveSeed(worldSeed, terrainType, variant) {
let h = (worldSeed >>> 0) ^ 0x9e3779b9;
h = Math.imul(h ^ (variant >>> 0), 668265263) >>> 0;
for (const ch of String(terrainType || "auto")) h = Math.imul(h ^ ch.charCodeAt(0), 16777619) >>> 0;
return h >>> 0;
}
const initialStartedAt = performance.now();
const initial = generateMap(worldSeed);
const initialGenerationMs = Math.round(performance.now() - initialStartedAt);
const world = createWorldMap(initial);
const width = 470;
const height = 333;
const x0 = Math.max(0, Math.min(world.width - width, world.originX + 238));
const y0 = Math.max(0, Math.min(world.height - height, world.originY));
const rect = { x0, y0, x1: x0 + width, y1: y0 + height, kind: "lasso" };
const insetX = Math.max(4, Math.floor(width * 0.16));
const insetY = Math.max(4, Math.floor(height * 0.16));
rect.polygon = [
{ x: rect.x0 + insetX, y: rect.y0 },
{ x: rect.x1 - 1, y: rect.y0 + insetY },
{ x: rect.x1 - insetX, y: rect.y1 - 1 },
{ x: rect.x0, y: rect.y1 - insetY },
];
const rect = buildMaximumProductionLasso(world);
const terrainType = initial.terrainTemplate?.terrainType || initial.terrainDebug?.terrainType || "auto";
const seed = deriveSeed(world.seed, terrainType, candidateVariant);
const seed = derivePatchSeed(world.seed, terrainType, candidateVariant);
const candidatePlan = [{ candidateId: `max-worker:${candidateVariant}`, candidateOrdinal: 1, variant: candidateVariant, seed }];
const worker = new Worker(new URL("./browser-worker-node-shim.mjs", import.meta.url), { type: "module" });
const startedAt = performance.now();
let progressEvents = 0;
let maxRssBytes = process.memoryUsage().rss;
let maxHeapBytes = process.memoryUsage().heapUsed;
let lastLabel = "";
const memoryTimer = setInterval(() => {
const memory = process.memoryUsage();
maxRssBytes = Math.max(maxRssBytes, memory.rss);
maxHeapBytes = Math.max(maxHeapBytes, memory.heapUsed);
}, 100);
async function finish(exitCode, payload) {
@ -68,7 +48,6 @@ async function finish(exitCode, payload) {
budgetMs,
withinBudget,
maxRssBytes,
maxHeapBytes,
progressEvents,
lastLabel,
searchStatus: payload?.searchStatus || null,

View file

@ -11,6 +11,7 @@ import {
normalizePatchPrefectureCapitals,
preparePatchTransactionFields,
reconcileGeneratedHumanPointsWithFinalTerrain,
regionalRecalculationProbability,
restorePatchTransactionSnapshot,
refreshPatchPrefectureMetadata,
synchronizePatchAdministrativeMetadata,
@ -37,6 +38,16 @@ function assert(condition, message) {
console.log(`OK: ${message}`);
}
const recalcAtSelection = regionalRecalculationProbability(0, 100);
const recalcNear = regionalRecalculationProbability(20, 100);
const recalcFar = regionalRecalculationProbability(70, 100);
const recalcOutside = regionalRecalculationProbability(100, 100);
assert(recalcAtSelection === 1
&& recalcNear > recalcFar
&& recalcFar > 0
&& recalcOutside === 0,
"regional recalculation probability rises monotonically toward the selected expansion area");
function testPointInPolygon(px, py, polygon) {
let inside = false;
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
@ -80,6 +91,66 @@ assert(search.result?.ok && search.result.actualVariant === 6, "content rejectio
assert(calls.length === 2 && calls.every((call) => call.baseline === 1), "candidate worlds start from one immutable baseline");
assert(progress.filter((event) => event.boundedWork).every((event) => event.completed <= event.total), "bounded progress remains inside its finite work total");
const bestOfThreeCalls = [];
const bestOfThreeScores = new Map([[12, 0.31], [13, 0.88], [14, 0.57]]);
const bestOfThree = runPatchCandidateSearch({
id: 1001,
world: base,
rect: { x0: 0, y0: 0, x1: 1, y1: 1 },
options: { acceptBestAvailableQuality: true },
search: {
searchId: "unit-best-of-three",
selectBestCandidate: true,
candidatePlan: [12, 13, 14].map((variant, index) => ({ candidateOrdinal: index + 1, candidateId: `best-${variant}`, variant, seed: 100 + variant })),
},
}, {
cloneWorld: structuredClone,
generateCandidate: (world, rect, options) => {
bestOfThreeCalls.push(options.variant);
world.fields.marker[0] = options.variant;
return {
ok: true,
variant: options.variant,
seed: options.seed,
candidateQuality: { hardPass: false, score: bestOfThreeScores.get(options.variant) },
seamDiagnostics: { hardPass: false, gateReasons: ["forced-diagnostic-only-seam"] },
rects: { writeRect: rect },
};
},
});
assert(bestOfThree.result?.ok
&& bestOfThree.result.bestOfCandidates === true
&& bestOfThree.result.actualVariant === 13
&& bestOfThree.result.candidateOrdinal === 2
&& bestOfThreeCalls.join(",") === "12,13,14"
&& bestOfThree.result.searchAttempts.map((attempt) => attempt.status).join(",") === "evaluated,success,evaluated",
"best-of-three mode evaluates every complete candidate and publishes the highest quality even when all gates remain diagnostic failures");
const asyncBestOfThree = await runPatchCandidateSearchAsync({
id: 1002,
world: base,
rect: { x0: 0, y0: 0, x1: 1, y1: 1 },
options: { acceptBestAvailableQuality: true },
search: {
searchId: "unit-best-of-three-async",
selectBestCandidate: true,
candidatePlan: [21, 22, 23].map((variant, index) => ({ candidateOrdinal: index + 1, candidateId: `async-best-${variant}`, variant, seed: 200 + variant })),
},
}, {
cloneWorld: structuredClone,
generateCandidate: async (world, rect, options) => ({
ok: true,
variant: options.variant,
seed: options.seed,
candidateQuality: { hardPass: options.variant === 22, score: options.variant === 23 ? 0.93 : options.variant === 22 ? 0.71 : 0.42 },
seamDiagnostics: { hardPass: options.variant === 22, gateReasons: options.variant === 22 ? [] : ["diagnostic-seam"] },
rects: { writeRect: rect },
}),
});
assert(asyncBestOfThree.result?.ok && asyncBestOfThree.result.actualVariant === 23
&& asyncBestOfThree.result.searchAttempts.length === 3,
"async best-of-three selection uses quality score as the primary ranking signal and evaluates all candidates");
const repeatedPipelineProgress = [];
const repeatedPipelineSearch = runPatchCandidateSearch({
id: 101,
@ -383,6 +454,18 @@ assert(!ArrayBuffer.isView(transportDebugViewport.transportDebug.layers.expressw
&& transportDebugViewport.transportDebug.layers.components.length === 1,
"viewport drops unused transport potential rasters while preserving vector diagnostics");
const edgeViewport = getViewportMap({
width: 3,
height: 2,
originX: 0,
originY: 0,
renderRevision: 1,
fields: { sea: new Uint8Array([1, 2, 3, 4, 5, 6]) },
sourceMap: {},
}, { x: -1, y: -1 }, 4, 4);
assert(Array.from(edgeViewport.sea).join(",") === "1,1,1,1,1,1,2,3,1,4,5,6,1,1,1,1",
"viewport row copies preserve exact clipping and fallback cells at negative camera edges");
const arrayDeltaBase = {
width: 1, height: 1,
fields: { marker: new Uint8Array([1]) }, generatedMask: new Uint8Array(1),
@ -540,6 +623,264 @@ assert(transactionalCalls.every(([sea, municipality, villages]) => sea === 0 &&
&& JSON.stringify(transactionalSearchBase) === JSON.stringify(transactionOriginal),
"rejected and accepted transactional candidates both start from and restore the same committed mirror");
const transactionalBestBase = structuredClone(transactionOriginal);
const transactionalBestScores = new Map([[31, 0.44], [32, 0.91], [33, 0.68]]);
const transactionalBestSearch = runPatchCandidateSearch({
id: 303,
world: transactionalBestBase,
rect: { x0: 2, y0: 1, x1: 6, y1: 5 },
options: { acceptBestAvailableQuality: true },
search: {
selectBestCandidate: true,
resolvedPatchMode: "regeneration",
candidatePlan: [31, 32, 33].map((variant, index) => ({ candidateOrdinal: index + 1, candidateId: `transactional-best-${variant}`, variant, seed: 300 + variant })),
},
}, {
transactional: true,
generateCandidate: (world, rect, options) => {
preparePatchTransactionFields(options._externalTransactionSnapshot, world, rect);
world.fields.sea[19] = options.variant;
world.fields.municipalityId[10] = options.variant;
world.sourceMap.villages.push({ x: options.variant, y: 2 });
world.patchGenerationSerial++;
return {
ok: true,
rects: { writeRect: rect },
candidateQuality: { hardPass: false, score: transactionalBestScores.get(options.variant) },
seamDiagnostics: { hardPass: false, gateReasons: ["diagnostic-only"] },
};
},
});
const transactionalBestApplied = applyCommittedMirrorDelta(structuredClone(transactionOriginal), transactionalBestSearch.transactionDelta);
assert(transactionalBestSearch.result?.actualVariant === 32
&& transactionalBestSearch.result?.candidateOrdinal === 2
&& JSON.stringify(transactionalBestBase) === JSON.stringify(transactionOriginal)
&& transactionalBestApplied.fields.sea[19] === 32
&& transactionalBestApplied.fields.municipalityId[10] === 32
&& transactionalBestApplied.sourceMap.villages.at(-1)?.x === 32,
"transactional best-of-three keeps only the highest-quality candidate delta and restores the committed mirror after every evaluation");
const draftSelectionBase = structuredClone(transactionOriginal);
const draftSelectionScores = new Map([[41, 0.41], [42, 0.94], [43, 0.63]]);
const draftSelectionDraftCalls = [];
const draftSelectionFullCalls = [];
let draftSelectionDeltaBuilds = 0;
let draftSelectionHashBuilds = 0;
const draftSelectionProgress = [];
const draftSelectionSearch = await runPatchCandidateSearchAsync({
id: 304,
world: draftSelectionBase,
rect: { x0: 2, y0: 1, x1: 6, y1: 5 },
options: { acceptBestAvailableQuality: true },
search: {
searchId: "unit-draft-ranked-production",
selectBestCandidate: true,
draftSelection: true,
resolvedPatchMode: "regeneration",
candidatePlan: [41, 42, 43].map((variant, index) => ({
candidateOrdinal: index + 1, candidateId: `draft-ranked-${variant}`, variant, seed: 400 + variant,
})),
},
}, {
transactional: true,
onProgress: (message) => draftSelectionProgress.push(message.progress),
prepareOperationContext: () => ({ ok: true, signature: "unit-shared-context" }),
evaluateDraftCandidate: async (world, rect, options) => {
draftSelectionDraftCalls.push(options.variant);
return {
ok: true,
score: draftSelectionScores.get(options.variant),
qualityUpperBound: draftSelectionScores.get(options.variant),
candidateQuality: { score: draftSelectionScores.get(options.variant), qualityUpperBound: draftSelectionScores.get(options.variant), terrain: { score: 0.8 }, human: { score: 0.7 } },
reusableForFull: true,
precomputedDraft: { terrain: { unitTerrainVariant: options.variant }, baseSeed: (400 + options.variant) >>> 0, effectiveSeed: (400 + options.variant) >>> 0, generationContext: { variant: options.variant }, generationTimings: [{ key: "terrain", ms: 1 }] },
};
},
generateCandidate: async (world, rect, options) => {
draftSelectionFullCalls.push({ variant: options.variant, reusedFullDraft: options._precomputedDraftCandidate != null, reusedTerrain: options._precomputedTerrainDraftCandidate?.generationContext?.variant ?? null });
preparePatchTransactionFields(options._externalTransactionSnapshot, world, rect);
world.fields.sea[19] = options.variant;
world.fields.municipalityId[10] = options.variant;
world.sourceMap.villages.push({ x: options.variant, y: 3 });
world.patchGenerationSerial++;
return {
ok: true,
rects: { writeRect: rect },
candidateQuality: { hardPass: true, score: 0.9 },
seamDiagnostics: { hardPass: true },
};
},
buildCommittedDeltaFromTransaction: (transaction, world) => {
draftSelectionDeltaBuilds++;
return buildCommittedMirrorDeltaFromTransaction(transaction, world);
},
hashCommittedWorld: (world) => {
draftSelectionHashBuilds++;
return hashCommittedWorld(world);
},
});
const draftSelectionApplied = applyCommittedMirrorDelta(structuredClone(transactionOriginal), draftSelectionSearch.transactionDelta);
assert(draftSelectionSearch.result?.ok
&& draftSelectionSearch.result.actualVariant === 42
&& draftSelectionSearch.result.draftSelection?.policy === "admissible-terrain-scout-branch-and-bound-two-lane-v2"
&& draftSelectionSearch.result.draftSelection?.fullCandidateCount === 1
&& draftSelectionSearch.result.draftSelection?.reusedWinningDraft === false
&& draftSelectionSearch.result.draftSelection?.reusedTerrainDraft === true
&& draftSelectionSearch.result.draftSelection?.fullProductionFromTerrainOnly === true
&& draftSelectionDraftCalls.join(",") === "41,42,43"
&& draftSelectionFullCalls.length === 1
&& draftSelectionFullCalls[0].variant === 42
&& draftSelectionFullCalls[0].reusedFullDraft === false
&& draftSelectionFullCalls[0].reusedTerrain === 42,
"terrain-scout production evaluates three cheap terrain candidates, fully finalizes only the viable winner, and never reuses simplified human/transport draft stages");
assert(draftSelectionDeltaBuilds === 1
&& draftSelectionHashBuilds === 1
&& draftSelectionApplied.fields.sea[19] === 42
&& draftSelectionApplied.fields.municipalityId[10] === 42
&& JSON.stringify(draftSelectionBase) === JSON.stringify(transactionOriginal),
"draft-ranked production builds the committed delta and whole-world hash exactly once for the accepted finalist and restores the committed mirror");
assert(draftSelectionProgress.filter((event) => event.boundedWork).every((event) => event.completed <= event.total)
&& draftSelectionProgress.filter((event) => event.workUnitId?.startsWith("finalist-") && event.key === "finalist-generation").every((event) => event.total === 1),
"draft-ranked production keeps finalist progress bounded per full candidate without leaving an incomplete multi-finalist work unit");
const nearTieBase = structuredClone(transactionOriginal);
const nearTieDraftScores = new Map([[51, 0.900], [52, 0.885], [53, 0.61]]);
const nearTieFullScores = new Map([[51, 0.78], [52, 0.93], [53, 0.64]]);
const nearTieFullCalls = [];
let nearTieDeltaBuilds = 0;
let nearTieHashBuilds = 0;
const nearTieSearch = await runPatchCandidateSearchAsync({
id: 305,
world: nearTieBase,
rect: { x0: 2, y0: 1, x1: 6, y1: 5 },
options: { acceptBestAvailableQuality: true },
search: {
searchId: "unit-draft-near-tie",
selectBestCandidate: true,
draftSelection: true,
resolvedPatchMode: "regeneration",
candidatePlan: [51, 52, 53].map((variant, index) => ({
candidateOrdinal: index + 1, candidateId: `draft-near-${variant}`, variant, seed: 500 + variant,
})),
},
}, {
transactional: true,
prepareOperationContext: () => ({ ok: true, signature: "unit-near-tie-context" }),
evaluateDraftCandidate: async (world, rect, options) => {
const upper = new Map([[51, 0.95], [52, 0.96], [53, 0.70]]).get(options.variant);
return {
ok: true,
score: nearTieDraftScores.get(options.variant),
qualityUpperBound: upper,
candidateQuality: { score: nearTieDraftScores.get(options.variant), qualityUpperBound: upper, hardPass: true, terrain: { score: 0.8 }, human: { score: 0.7 } },
reusableForFull: true,
precomputedDraft: { terrain: { unitTerrainVariant: options.variant }, baseSeed: (500 + options.variant) >>> 0, effectiveSeed: (500 + options.variant) >>> 0, generationContext: { variant: options.variant }, generationTimings: [{ key: "terrain", ms: 1 }] },
};
},
generateCandidate: async (world, rect, options) => {
nearTieFullCalls.push({ variant: options.variant, reusedFullDraft: options._precomputedDraftCandidate != null, reusedTerrain: options._precomputedTerrainDraftCandidate?.generationContext?.variant ?? null });
preparePatchTransactionFields(options._externalTransactionSnapshot, world, rect);
world.fields.sea[19] = options.variant;
world.fields.municipalityId[10] = options.variant;
world.sourceMap.villages.push({ x: options.variant, y: 7 });
world.patchGenerationSerial++;
return {
ok: true,
rects: { writeRect: rect },
candidateQuality: { hardPass: true, score: nearTieFullScores.get(options.variant) },
seamDiagnostics: { hardPass: true, gateReasons: [] },
};
},
buildCommittedDeltaFromTransaction: (transaction, world) => {
nearTieDeltaBuilds++;
return buildCommittedMirrorDeltaFromTransaction(transaction, world);
},
hashCommittedWorld: (world) => {
nearTieHashBuilds++;
return hashCommittedWorld(world);
},
});
const nearTieApplied = applyCommittedMirrorDelta(structuredClone(transactionOriginal), nearTieSearch.transactionDelta);
assert(nearTieSearch.result?.ok
&& nearTieSearch.result.actualVariant === 52
&& nearTieSearch.result.draftSelection?.policy === "admissible-terrain-scout-branch-and-bound-two-lane-v2"
&& nearTieSearch.result.draftSelection?.selectionReason === "admissible-bound-winner-live"
&& nearTieSearch.result.draftSelection?.fullCandidateCount === 2
&& nearTieSearch.result.draftSelection?.fullGenerationPassCount === 2
&& nearTieSearch.result.draftSelection?.branchBoundPrunedCandidateOrdinals.includes(3)
&& nearTieFullCalls.map((row) => row.variant).join(",") === "51,52",
"admissible Branch-and-Bound fully evaluates only candidates that can still beat the current best and removes the fixed near-tie heuristic");
assert(nearTieDeltaBuilds === 1
&& nearTieHashBuilds === 1
&& nearTieApplied.fields.sea[19] === 52
&& nearTieApplied.fields.municipalityId[10] === 52
&& JSON.stringify(nearTieBase) === JSON.stringify(transactionOriginal),
"near-tie comparison still constructs delta/hash only once for the final selected candidate and restores the committed mirror");
const draftFallbackBase = structuredClone(transactionOriginal);
const draftFallbackCalls = [];
let draftFallbackDeltaBuilds = 0;
let draftFallbackHashBuilds = 0;
const draftFallbackSearch = await runPatchCandidateSearchAsync({
id: 306,
world: draftFallbackBase,
rect: { x0: 2, y0: 1, x1: 6, y1: 5 },
options: { acceptBestAvailableQuality: true },
search: {
searchId: "unit-draft-content-fallback",
selectBestCandidate: true,
draftSelection: true,
resolvedPatchMode: "regeneration",
candidatePlan: [71, 72, 73].map((variant, index) => ({
candidateOrdinal: index + 1, candidateId: `draft-fallback-${variant}`, variant, seed: 700 + variant,
})),
},
}, {
transactional: true,
prepareOperationContext: () => ({ ok: true, signature: "unit-fallback-context" }),
evaluateDraftCandidate: async (world, rect, options) => ({
ok: true,
score: options.variant === 71 ? 0.91 : options.variant === 72 ? 0.72 : 0.51,
qualityUpperBound: options.variant === 71 ? 0.96 : options.variant === 72 ? 0.90 : 0.60,
candidateQuality: { score: options.variant === 71 ? 0.91 : options.variant === 72 ? 0.72 : 0.51, qualityUpperBound: options.variant === 71 ? 0.96 : options.variant === 72 ? 0.90 : 0.60, hardPass: true },
reusableForFull: true,
precomputedDraft: { terrain: { unitTerrainVariant: options.variant }, baseSeed: (700 + options.variant) >>> 0, effectiveSeed: (700 + options.variant) >>> 0, generationContext: { variant: options.variant }, generationTimings: [{ key: "terrain", ms: 1 }] },
}),
generateCandidate: async (world, rect, options) => {
draftFallbackCalls.push({ variant: options.variant, reusedFullDraft: options._precomputedDraftCandidate != null, reusedTerrain: options._precomputedTerrainDraftCandidate?.generationContext?.variant ?? null });
preparePatchTransactionFields(options._externalTransactionSnapshot, world, rect);
if (options.variant === 71) return { ok: false, code: "patch-quality-gate-failed", reason: "forced full-stage rejection" };
world.fields.sea[19] = options.variant;
world.fields.municipalityId[10] = options.variant;
world.patchGenerationSerial++;
return { ok: true, rects: { writeRect: rect }, candidateQuality: { hardPass: true, score: 0.82 }, seamDiagnostics: { hardPass: true } };
},
buildCommittedDeltaFromTransaction: (transaction, world) => {
draftFallbackDeltaBuilds++;
return buildCommittedMirrorDeltaFromTransaction(transaction, world);
},
hashCommittedWorld: (world) => {
draftFallbackHashBuilds++;
return hashCommittedWorld(world);
},
});
const draftFallbackApplied = applyCommittedMirrorDelta(structuredClone(transactionOriginal), draftFallbackSearch.transactionDelta);
assert(draftFallbackSearch.result?.ok
&& draftFallbackSearch.result.actualVariant === 72
&& draftFallbackSearch.result.draftSelection?.selectionReason?.startsWith("admissible-bound")
&& draftFallbackSearch.result.draftSelection?.fullCandidateCount === 2
&& draftFallbackCalls.map((row) => row.variant).join(",") === "71,72"
&& draftFallbackCalls[0].reusedFullDraft === false
&& draftFallbackCalls[0].reusedTerrain === 71
&& draftFallbackCalls[1].reusedFullDraft === false
&& draftFallbackCalls[1].reusedTerrain === null,
"clear terrain-scout winner reuses only exact terrain on the fast path and regenerates a discarded runner-up after content rejection");
assert(draftFallbackDeltaBuilds === 1
&& draftFallbackHashBuilds === 1
&& draftFallbackApplied.fields.sea[19] === 72
&& JSON.stringify(draftFallbackBase) === JSON.stringify(transactionOriginal),
"content fallback preserves the one-delta/one-hash publication invariant");
const municipalityWorld = {
width: 8, height: 6,
fields: {

View file

@ -1,4 +1,5 @@
import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
@ -232,7 +233,19 @@ class CdpBrowser {
}
}
export async function launchChromiumCdp({ executablePath = process.env.CHROMIUM_PATH || "/usr/bin/chromium" } = {}) {
function defaultChromiumExecutable() {
const candidates = process.platform === "win32"
? [
process.env.LOCALAPPDATA && join(process.env.LOCALAPPDATA, "Programs", "Microsoft Edge", "Application", "msedge.exe"),
process.env["PROGRAMFILES(X86)"] && join(process.env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe"),
process.env.PROGRAMFILES && join(process.env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe"),
process.env.PROGRAMFILES && join(process.env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe"),
]
: ["/usr/bin/chromium", "/usr/bin/chromium-browser", "/usr/bin/google-chrome"];
return candidates.filter(Boolean).find((candidate) => existsSync(candidate)) || candidates.filter(Boolean)[0];
}
export async function launchChromiumCdp({ executablePath = process.env.CHROMIUM_PATH || defaultChromiumExecutable() } = {}) {
const userDataDir = await mkdtemp(join(tmpdir(), "jmg-chromium-"));
const child = spawn(executablePath, [
"--headless=new",

36
tests/debug.log Normal file
View file

@ -0,0 +1,36 @@
[0811/195718.540:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/195719.021:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/195751.212:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/195858.053:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200025.218:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200112.414:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200159.694:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200206.915:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200237.062:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200303.325:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200325.372:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200341.314:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200355.039:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200414.422:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/200430.422:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/201027.558:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/201052.753:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/203141.432:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/204206.661:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/204207.227:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/204407.281:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/204529.582:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/204721.230:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/204808.726:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/204903.607:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/204911.221:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/204942.084:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/205011.188:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/205041.144:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/205101.640:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/205120.979:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/205145.386:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/205255.007:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/205255.511:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/205745.891:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)
[0811/210101.304:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: アクセスが拒否されました。 (0x5)

View file

@ -0,0 +1,15 @@
import { parentPort } from 'node:worker_threads';
if (!parentPort) throw new Error('generation worker node wrapper requires parentPort');
const pending = [];
globalThis.self = {
postMessage(data, transfer = []) {
parentPort.postMessage(data, transfer);
},
};
parentPort.on('message', (data) => {
if (typeof globalThis.self.onmessage === 'function') globalThis.self.onmessage({ data });
else pending.push(data);
});
await import('../src/generationWorker.js');
while (pending.length) globalThis.self.onmessage({ data: pending.shift() });

View file

@ -2,36 +2,18 @@ import { Worker } from "node:worker_threads";
import { performance } from "node:perf_hooks";
import { generateMap } from "../src/mapPipeline.js";
import { createWorldMap } from "../src/worldMap.js";
import { buildMaximumProductionLasso, derivePatchSeed } from "./production-fixtures.mjs";
function assert(condition, message) {
if (!condition) throw new Error(message);
console.log(`OK: ${message}`);
}
function deriveSeed(worldSeed, terrainType, variant) {
let hash = (worldSeed >>> 0) ^ 0x9e3779b9;
hash = Math.imul(hash ^ (variant >>> 0), 668265263) >>> 0;
for (const char of String(terrainType || "auto")) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619) >>> 0;
return hash >>> 0;
}
const initial = generateMap(114514);
const world = createWorldMap(initial);
const width = 470;
const height = 333;
const x0 = Math.max(0, Math.min(world.width - width, world.originX + 238));
const y0 = Math.max(0, Math.min(world.height - height, world.originY));
const rect = { x0, y0, x1: x0 + width, y1: y0 + height, kind: "lasso" };
const insetX = Math.max(4, Math.floor(width * 0.16));
const insetY = Math.max(4, Math.floor(height * 0.16));
rect.polygon = [
{ x: rect.x0 + insetX, y: rect.y0 },
{ x: rect.x1 - 1, y: rect.y0 + insetY },
{ x: rect.x1 - insetX, y: rect.y1 - 1 },
{ x: rect.x0, y: rect.y1 - insetY },
];
const rect = buildMaximumProductionLasso(world);
const terrainType = initial.terrainTemplate?.terrainType || initial.terrainDebug?.terrainType || "auto";
const seed = deriveSeed(world.seed, terrainType, 0);
const seed = derivePatchSeed(world.seed, terrainType, 0);
const worker = new Worker(new URL("./browser-worker-node-shim.mjs", import.meta.url), { type: "module" });
let terminalMessage = null;
let cancelStartedAt = 0;

View file

@ -130,7 +130,12 @@ async function main() {
x1: world.originX + 132,
y1: world.originY + 118,
};
const candidate = { candidateId: "node-sync:1", candidateOrdinal: 1, variant: 1, seed: 0x51a7c3d3 };
const candidates = [1, 2, 3].map((variant, index) => ({
candidateId: `node-sync:${variant}`,
candidateOrdinal: index + 1,
variant,
seed: (0x51a7c3d3 + Math.imul(index, 0x9e3779b9)) >>> 0,
}));
const resultPromise = waitFor(worker, (message) => message?.id === 92 && message?.type !== "progress", 180_000);
worker.postMessage({
id: 92,
@ -139,11 +144,11 @@ async function main() {
options: {
patchMode: "regeneration",
terrainType: "auto",
variant: candidate.variant,
seed: candidate.seed,
variant: candidates[0].variant,
seed: candidates[0].seed,
maxQualityRetries: 0,
qualityTerrainAttempts: 1,
acceptBestAvailableQuality: false,
acceptBestAvailableQuality: true,
includeSeamVisualization: false,
},
search: {
@ -152,14 +157,29 @@ async function main() {
committedRevision: 1,
workerEpoch: 1,
executionAttempt: 1,
totalCandidateCount: 1,
totalCandidateCount: candidates.length,
reuseCommittedMirror: true,
candidatePlan: [candidate],
resolvedPatchMode: "regeneration",
selectBestCandidate: true,
draftSelection: true,
candidatePlan: candidates,
},
});
const result = await resultPromise;
assert(result.ok === true && result.result?.ok === true, "a cold-synchronized mirror can run a complete production patch without retransmitting world");
assert(result.result?.acceptedWorldHash && result.result?.applyToken, "cold-synchronized candidate returns transactional hash and Apply token");
assert(result.ok === true && result.result?.ok === true, "a cold-synchronized mirror can run draft-ranked production without retransmitting world");
const draftDebug = result.result?.draftSelection;
const exactBoundBehavior = draftDebug?.fullCandidateCount < 3
? draftDebug?.branchBoundPrunedCount > 0
: draftDebug?.fullCandidateCount === 3 && draftDebug?.branchBoundPrunedCount === 0 && draftDebug?.fullComparedCandidateOrdinals?.length === 3;
assert(draftDebug?.enabled === true
&& draftDebug?.draftCount === 3
&& draftDebug?.fullCandidateCount >= 1
&& draftDebug?.fullCandidateCount <= 3
&& draftDebug?.reusedWinningDraft === false
&& draftDebug?.fullProductionFromTerrainOnly === true
&& exactBoundBehavior,
"real Worker production ranks three terrain scouts, prunes only mathematically dominated candidates, and fully compares all candidates when admissible bounds overlap");
assert(result.result?.acceptedWorldHash && result.result?.applyToken, "draft-ranked finalist returns transactional hash and Apply token");
const ackId = "node-sync-apply";
const applyPromise = waitFor(worker, (message) => message?.type === "patch-apply-ack-result" && message.ackId === ackId);

View file

@ -0,0 +1,21 @@
export function derivePatchSeed(worldSeed, terrainType, variant) {
let hash = (worldSeed >>> 0) ^ 0x9e3779b9;
hash = Math.imul(hash ^ (variant >>> 0), 668265263) >>> 0;
for (const char of String(terrainType || "auto")) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619) >>> 0;
return hash >>> 0;
}
export function buildMaximumProductionLasso(world, width = 470, height = 333) {
const x0 = Math.max(0, Math.min(world.width - width, world.originX + 238));
const y0 = Math.max(0, Math.min(world.height - height, world.originY));
const rect = { x0, y0, x1: x0 + width, y1: y0 + height, kind: "lasso" };
const insetX = Math.max(4, Math.floor(width * 0.16));
const insetY = Math.max(4, Math.floor(height * 0.16));
rect.polygon = [
{ x: rect.x0 + insetX, y: rect.y0 },
{ x: rect.x1 - 1, y: rect.y0 + insetY },
{ x: rect.x1 - insetX, y: rect.y1 - 1 },
{ x: rect.x0, y: rect.y1 - insetY },
];
return rect;
}

View file

@ -0,0 +1,87 @@
import { Worker } from "node:worker_threads";
import { performance } from "node:perf_hooks";
import { generateMap } from "../src/mapPipeline.js";
import { createWorldMap } from "../src/worldMap.js";
function assert(condition, message) {
if (!condition) throw new Error(message);
console.log(`OK: ${message}`);
}
const initial = generateMap(114514);
const baseline = createWorldMap(initial);
const rect = { x0: 90, y0: 55, x1: 165, y1: 120 };
const baseSeed = 88001;
const candidatePlan = [0, 1, 2].map((variant, index) => ({
candidateId: `r10-exact:${variant}`,
candidateOrdinal: index + 1,
variant,
seed: (baseSeed + Math.imul(variant, 2654435761)) >>> 0,
}));
async function runSearch(draftSelection) {
return await new Promise((resolve, reject) => {
const worker = new Worker(new URL("./browser-worker-node-shim.mjs", import.meta.url), { type: "module" });
const startedAt = performance.now();
worker.on("message", async (message) => {
if (message?.type === "progress") return;
await worker.terminate();
resolve({ message, elapsedMs: Math.round(performance.now() - startedAt) });
});
worker.on("error", reject);
worker.postMessage({
id: draftSelection ? 1001 : 1002,
world: structuredClone(baseline),
rect,
options: {
patchMode: "regeneration",
terrainType: "auto",
variant: 0,
seed: baseSeed,
maxQualityRetries: 0,
qualityTerrainAttempts: 1,
acceptBestAvailableQuality: false,
includeSeamVisualization: false,
},
search: {
searchId: draftSelection ? "r10-exact-fast" : "r10-exact-exhaustive",
operationId: "r10-exact-production",
committedRevision: 1,
workerEpoch: 1,
executionAttempt: 1,
totalCandidateCount: candidatePlan.length,
selectBestCandidate: true,
draftSelection,
resolvedPatchMode: "regeneration",
candidatePlan,
},
});
});
}
const fast = await runSearch(true);
const exhaustive = await runSearch(false);
assert(fast.message?.result?.ok === true && exhaustive.message?.result?.ok === true,
"both optimized and exhaustive searches produce publishable production candidates");
assert(fast.message.result.actualVariant === exhaustive.message.result.actualVariant,
"terrain-scout Branch-and-Bound selects the same variant as exhaustive full-production search");
assert(Math.abs(Number(fast.message.result.selectionScore) - Number(exhaustive.message.result.selectionScore)) <= 1e-12,
"optimized search returns the exact same final production quality score as exhaustive search");
assert(fast.message.result.draftSelection?.reusedWinningDraft === false
&& fast.message.result.draftSelection?.fullProductionFromTerrainOnly === true,
"optimized search never publishes or reuses simplified human/transport draft output");
assert(fast.message.result.draftSelection?.parallelDraftGeneration === true
&& fast.message.result.draftSelection?.parallelDraftLaneCount === 2,
"two resident terrain-scout lanes are active in the real Worker path");
assert(fast.message.result.draftSelection?.fullCandidateCount <= candidatePlan.length
&& Number.isFinite(fast.message.result.draftSelection?.branchBoundPrunedCount)
&& fast.message.result.draftSelection?.ranking?.every((row) => Number.isFinite(row.qualityUpperBound)),
"admissible bounds never evaluate more full candidates than exhaustive search and retain explicit upper-bound audit data");
console.log(JSON.stringify({
optimizedMs: fast.elapsedMs,
exhaustiveMs: exhaustive.elapsedMs,
optimizedFullCandidates: fast.message.result.draftSelection?.fullCandidateCount,
prunedCandidates: fast.message.result.draftSelection?.branchBoundPrunedCount,
variant: fast.message.result.actualVariant,
score: fast.message.result.selectionScore,
}, null, 2));

View file

@ -0,0 +1,102 @@
import assert from "node:assert/strict";
import { Worker } from "node:worker_threads";
import { generateMap } from "../src/mapPipeline.js";
import { createWorldMap } from "../src/worldMap.js";
import { buildMaximumProductionLasso, derivePatchSeed } from "./production-fixtures.mjs";
const worldSeed = 114514;
const candidateVariant = 0;
const initial = generateMap(worldSeed);
const world = createWorldMap(initial);
const rect = buildMaximumProductionLasso(world);
const terrainType = initial.terrainTemplate?.terrainType || initial.terrainDebug?.terrainType || "auto";
const seed = derivePatchSeed(world.seed, terrainType, candidateVariant);
const candidatePlan = [{ candidateId: `r11-selection-native:${candidateVariant}`, candidateOrdinal: 1, variant: candidateVariant, seed }];
const worker = new Worker(new URL("./browser-worker-node-shim.mjs", import.meta.url), { type: "module" });
const timeoutMs = 90_000;
const result = await new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error(`r11 selection-native worker timed out after ${timeoutMs} ms`)), timeoutMs);
worker.on("message", (message) => {
if (message.id !== 1 || message.type === "progress") return;
clearTimeout(timer);
if (!message.ok) reject(new Error(message.error || message.code || "r11 worker failed"));
else resolve(message.result);
});
worker.on("error", (error) => {
clearTimeout(timer);
reject(error);
});
worker.postMessage({
id: 1,
world,
rect,
options: {
patchMode: "expansion",
terrainType,
variant: candidateVariant,
seed,
maxQualityRetries: 0,
qualityTerrainAttempts: 1,
acceptBestAvailableQuality: false,
includeSeamVisualization: false,
},
search: {
searchId: "r11-selection-native-production",
operationId: "r11-selection-native-production",
committedRevision: 1,
workerEpoch: 1,
executionAttempt: 1,
totalCandidateCount: 1,
candidatePlan,
},
});
});
try {
assert.equal(result?.ok, true, "candidate must complete successfully");
assert.equal(result?.searchStatus, "succeeded", "candidate search must succeed");
assert.equal(result?.selectionNativeProduction, true, "large Expansion must report selection-native production");
assert.equal(result?.patchGenerationMode, "selection-native-chunked-production-v1", "large Expansion must expose r11 generation mode");
assert.equal(result?.candidateQuality?.hardPass, true, "aggregate production quality gate must pass");
assert.equal(result?.candidateQuality?.finalMerge?.hardPass, true, "final merged production quality must pass");
assert.equal(result?.candidateQuality?.finalMerge?.transportHierarchyPass, true, "transport hierarchy Oracle must pass");
assert.equal(result?.candidateQuality?.finalMerge?.transportTopology?.hardPass, true, "transport topology/service quality must pass");
assert.equal(result?.candidateQuality?.finalMerge?.prefectureCoherence?.hardPass, true, "prefecture regional coherence quality must pass");
assert.equal(result?.seamDiagnostics?.hardPass, true, "whole-selection seam audit must pass");
const nativeTransport = result?.seamDiagnostics?.aggregateTransportRepair?.selectionNativeTransport;
assert.equal(nativeTransport?.policy, "whole-selection-post-admin-transport-v2-coherent-graph", "r11 whole-selection transport finalizer must be authoritative");
assert.equal(nativeTransport?.chunkPostAdminTransportDeferred, true, "private chunks must defer duplicate post-admin transport finalization");
assert.equal(nativeTransport?.fullResolutionRouting, true, "published transport must use full-resolution routing");
assert.ok((nativeTransport?.roadGraphRepair?.roadGraphAfterComponents || 0) <= (nativeTransport?.roadGraphRepair?.roadGraphBeforeComponents || 0), "whole-selection road graph repair must not increase fragmentation");
assert.ok((nativeTransport?.railGraphRepair?.railGraphAfterComponents || 0) <= (nativeTransport?.railGraphRepair?.railGraphBeforeComponents || 0), "whole-selection rail graph repair must not increase fragmentation");
for (const classDebug of Object.values(nativeTransport?.regionalTrunkTransport?.byClass || {})) {
if (!classDebug?.demand) continue;
assert.equal(classDebug.mandatoryServiceConnected, classDebug.mandatoryServiceNodes, "every mandatory major-city/capital trunk service must be connected");
if (classDebug.classGraph?.roadGraphBeforeComponents != null) assert.ok(classDebug.classGraph.roadGraphAfterComponents <= classDebug.classGraph.roadGraphBeforeComponents, "road hierarchy graph must not become more fragmented");
if (classDebug.classGraph?.railGraphBeforeComponents != null) assert.ok(classDebug.classGraph.railGraphAfterComponents <= classDebug.classGraph.railGraphBeforeComponents, "rail hierarchy graph must not become more fragmented");
}
const classes = result?.candidateQuality?.finalMerge?.transportClasses || {};
const requirements = result?.candidateQuality?.finalMerge?.transportRequirements || {};
for (const key of ["national", "expressway", "railTrunk"]) {
if (!requirements[key]?.demand) continue;
assert.ok((classes[key]?.cells || 0) >= (requirements[key]?.minCells || 0), `${key} routed-coverage floor must be met`);
assert.ok((classes[key]?.paths || 0) > 0, `${key} must still contain at least one production corridor when demanded`);
}
console.log(JSON.stringify({
ok: true,
patchGenerationMode: result.patchGenerationMode,
selectionNativePolicy: result.selectionNativePolicy,
finalScore: result.candidateQuality.finalMerge.score,
transportClasses: result.candidateQuality.finalMerge.transportClasses,
transportRequirements: result.candidateQuality.finalMerge.transportRequirements,
selectionNativeTransport: nativeTransport,
}, null, 2));
} finally {
await worker.terminate();
}

View file

@ -0,0 +1,81 @@
import assert from 'node:assert/strict';
import { Worker } from 'node:worker_threads';
function pathLength(paths) {
return (paths || []).reduce((sum, path) => sum + Math.max(0, (path?.length || 0) - 1), 0);
}
function prefMunicipalityCounts(map) {
const groups = new Map();
for (let i = 0; i < map.adminId.length; i++) {
if (map.sea[i]) continue;
const a = Number(map.adminId[i]), p = Number(map.prefectureRegionId[i]);
if (a < 0 || p < 0) continue;
if (!groups.has(p)) groups.set(p, new Set());
groups.get(p).add(a);
}
return [...groups.values()].map((s) => s.size);
}
function edgeTouches(paths, width, height) {
let hits = 0;
for (const path of paths || []) {
if ((path || []).some(([x, y]) => x <= 0 || y <= 0 || x >= width - 1 || y >= height - 1)) hits++;
}
return hits;
}
function runWorker(seed) {
return new Promise((resolve, reject) => {
const worker = new Worker(new URL('./helpers-generation-worker-node-wrapper.mjs', import.meta.url), { type: 'module' });
const timer = setTimeout(() => { worker.terminate(); reject(new Error(`worker timeout for seed ${seed}`)); }, 90000);
worker.on('error', (error) => { clearTimeout(timer); reject(error); });
worker.on('message', (message) => {
if (message?.type !== 'result' || message?.id !== seed) return;
clearTimeout(timer);
worker.terminate();
if (!message.ok) reject(new Error(message.error || 'generation failed'));
else resolve(message.map);
});
worker.postMessage({ id: seed, seed, options: { terrainType: 'auto' } });
});
}
for (const seed of [114514, 999]) {
const map = await runWorker(seed);
assert.equal(map.width, 258, `seed ${seed}: published width`);
assert.equal(map.height, 183, `seed ${seed}: published height`);
const over = map.initialGenerationOverscan;
assert.equal(over?.version, 'literal-hidden-raster-center-crop-v1', `seed ${seed}: literal overscan marker`);
assert(over.fullWidth > map.width && over.fullHeight > map.height, `seed ${seed}: production was generated on a larger raster`);
assert(over.marginX >= 40 && over.marginY >= 40, `seed ${seed}: meaningful hidden halo exists`);
const visibleSize = map.width * map.height;
const hiddenSize = over.fullWidth * over.fullHeight;
for (const key of ['sea','elevation','slope','populationDensity','landuse','adminId','prefectureRegionId']) {
assert.equal(map[key]?.length, visibleSize, `seed ${seed}: authoritative raster ${key} is center-cropped`);
}
for (const [key, value] of Object.entries(map)) {
if (ArrayBuffer.isView(value)) assert.notEqual(value.length, hiddenSize, `seed ${seed}: hidden full raster ${key} is not leaked to published map`);
}
for (let id = 0; id < (map.adminCenters || []).length; id++) {
const c = map.adminCenters[id];
if (!c) continue;
assert(c.x >= 0 && c.y >= 0 && c.x < map.width && c.y < map.height, `seed ${seed}: visible municipality seat is in crop`);
assert.equal(map.adminId[Math.round(c.y) * map.width + Math.round(c.x)], id, `seed ${seed}: municipal seat belongs to municipality ${id}`);
}
const pops = (map.adminCenters || []).filter(Boolean).map((c) => Number(c.municipalityPopulation || 0));
assert(pops.length >= 30, `seed ${seed}: sufficient municipalities survive central crop`);
assert(Math.min(...pops) >= 1000, `seed ${seed}: municipal population floor`);
assert(pops.filter((p) => p > 10000).length / pops.length <= 0.30, `seed ${seed}: >10k municipalities are minority`);
const cityNames = (map.adminCenters || []).filter(Boolean).map((c) => String(c.name || ''));
assert(cityNames.filter((n) => n.endsWith('市')).length / cityNames.length <= 0.35, `seed ${seed}: 市 does not dominate`);
const prefCounts = prefMunicipalityCounts(map);
assert(prefCounts.length >= 2 && Math.min(...prefCounts) >= 10, `seed ${seed}: every visible prefecture has substantial municipal subdivision`);
const nationalLen = pathLength(map.nationalRoads);
const railLen = pathLength([...(map.railways || []), ...(map.branchRailways || [])]);
assert(nationalLen > 0 && railLen / nationalLen >= 0.70 && railLen / nationalLen <= 1.05, `seed ${seed}: published rail network is dense and close to national-road density`);
const service = map.transportDebug?.postAdminTransportFinalization?.visibleCropMajorCityService;
assert(service && service.checked >= 1, `seed ${seed}: published major-city service is audited`);
assert.equal(service.missing.length, 0, `seed ${seed}: no interior visible major city lacks national+rail+expressway service`);
const trunkBoundaryHits = edgeTouches([...(map.nationalRoads || []), ...(map.railways || []), ...(map.branchRailways || []), ...(map.expressways || [])], map.width, map.height);
assert(trunkBoundaryHits >= 2, `seed ${seed}: real hidden-context trunk corridors cross the published crop boundary`);
}
console.log('All r11.4 literal initial-overscan worker checks passed.');

View file

@ -0,0 +1,106 @@
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.');

View file

@ -0,0 +1,150 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { Worker } from 'node:worker_threads';
function runWorker(seed) {
return new Promise((resolve, reject) => {
const worker = new Worker(new URL('./helpers-generation-worker-node-wrapper.mjs', import.meta.url), { type: 'module' });
const timer = setTimeout(() => { worker.terminate(); reject(new Error(`worker timeout seed ${seed}`)); }, 120000);
worker.on('error', (error) => { clearTimeout(timer); reject(error); });
worker.on('message', async (message) => {
if (message?.type !== 'result' || message.id !== seed) return;
clearTimeout(timer);
worker.removeAllListeners();
await worker.terminate();
if (!message.ok) reject(new Error(message.error || 'generation failed'));
else resolve(message.map);
});
worker.postMessage({ id: seed, seed, options: { terrainType: 'auto' } });
});
}
function pathLength(paths) {
return (paths || []).reduce((sum, path) => {
let length = 0;
for (let i = 1; i < (path?.length || 0); i++) length += Math.hypot(path[i][0] - path[i - 1][0], path[i][1] - path[i - 1][1]);
return sum + length;
}, 0);
}
function prefCounts(map) {
const groups = new Map();
for (let i = 0; i < map.adminId.length; i++) {
if (map.sea[i]) continue;
const a = Number(map.adminId[i]), p = Number(map.prefectureRegionId[i]);
if (a < 0 || p < 0) continue;
if (!groups.has(p)) groups.set(p, new Set());
groups.get(p).add(a);
}
return [...groups.values()].map((s) => s.size);
}
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 parallelRun(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;
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;
}
function extremeRun(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;
run = angle >= 82 ? run + 1 : 0; best = Math.max(best, run);
}
return best;
}
function edgeHits(paths, width, height) {
return (paths || []).filter((path) => (path || []).some(([x, y]) => x <= 0 || y <= 0 || x >= width - 1 || y >= height - 1)).length;
}
function pointPathDistance(point, path) {
let best = Infinity;
for (const tuple of path || []) best = Math.min(best, Math.hypot(tuple[0] - point.x, tuple[1] - point.y));
return best;
}
function clearInteriorMinorOrphans(map) {
const minor = map.minorRoads || [];
const trunks = [...(map.nationalRoads || []), ...(map.externalRoads || []), ...(map.expressways || []), ...(map.railways || []), ...(map.branchRailways || [])];
const civic = [...(map.modernCities || []), ...(map.markets || []), ...(map.villages || []), ...(map.ports || []), ...(map.adminCenters || []).filter(Boolean)];
const connected = (tuple, self) => {
const pt = { x: tuple[0], y: tuple[1] };
if (trunks.some((path) => pointPathDistance(pt, path) <= 2.5)) return true;
return minor.some((path, index) => index !== self && pointPathDistance(pt, path) <= 2.2);
};
const out = [];
for (let index = 0; index < minor.length; index++) {
const path = minor[index];
if (!path?.length) continue;
let length = 0;
for (let k = 1; k < path.length; k++) length += Math.hypot(path[k][0] - path[k - 1][0], path[k][1] - path[k - 1][1]);
if (length >= 24) continue;
if (path.some(([x, y]) => x <= 0.5 || y <= 0.5 || x >= map.width - 1.5 || y >= map.height - 1.5)) continue;
if (civic.some((point) => pointPathDistance(point, path) <= 2.6)) continue;
const a = path[0], b = path[path.length - 1];
if (!connected(a, index) && !connected(b, index)) out.push({ index, length });
}
return out;
}
for (const seed of [1, 2, 5]) {
console.log('r11.5 audit seed', seed);
const map = await runWorker(seed);
console.log('r11.5 generated seed', seed);
const post = map.transportDebug?.postAdminTransportFinalization || {};
const visible = post.visibleCropMajorCityService;
const hidden = post.postDedupeMajorCityService;
const visibleFinalizer = post.initialVisibleCropFinalizer;
assert.equal(map.initialGenerationOverscan?.version, 'literal-hidden-raster-center-crop-v1', `seed ${seed}: literal production overscan`);
assert(visibleFinalizer?.productionOnly && visibleFinalizer?.simplifiedOutputForbidden, `seed ${seed}: visible finalizer uses full production output only`);
assert(post.visibleCropOrphanMinorRoadCleanup?.enabled === true, `seed ${seed}: crop-induced minor-road orphan cleanup ran on published raster`);
assert.equal(clearInteriorMinorOrphans(map).length, 0, `seed ${seed}: no short crop-created interior minor road is disconnected at both ends without serving a settlement`);
assert.equal(visible?.missing?.length || 0, 0, `seed ${seed}: every publishable major city gets all required trunk modes`);
assert.equal(visible?.edgeTruncated?.length || 0, 0, `seed ${seed}: crop-edge is no longer an excuse for missing major-city trunk service`);
for (const ex of visible?.geographicExceptions || []) {
assert(ex.visibleLandComponentArea < 96 && ex.national && ex.rail && !ex.expressway, `seed ${seed}: only tiny-islet motorway omission is allowed`);
}
assert.equal(hidden?.missing?.length || 0, 0, `seed ${seed}: hidden production audit has no unresolved major-city service failure`);
const counts = prefCounts(map);
assert(counts.length >= 2 && Math.min(...counts) >= 10, `seed ${seed}: every published prefecture has at least ten municipalities`);
assert(map.regionalDebug?.visibleCropPrefectureRepair?.minimumVisibleMunicipalities >= 10, `seed ${seed}: post-crop prefecture repair is active`);
const nationalLen = pathLength(map.nationalRoads);
const railLen = pathLength([...(map.railways || []), ...(map.branchRailways || [])]);
const ratio = nationalLen > 0 ? railLen / nationalLen : 0;
assert(ratio >= 0.78 && ratio <= 1.00, `seed ${seed}: railway density (${ratio.toFixed(3)}) is high but remains below national-road density`);
assert(parallelRun(map.nationalRoads || [], 3) <= 5, `seed ${seed}: no long ~1km-class national-road parallel corridor`);
assert(parallelRun(map.expressways || [], 4) <= 5, `seed ${seed}: no long ~1km-class expressway parallel corridor`);
assert(Math.max(0, ...(map.expressways || []).map(extremeRun)) <= 1, `seed ${seed}: expressway avoids repeated extreme bends`);
const trunkEdgeHits = edgeHits([...(map.nationalRoads || []), ...(map.expressways || []), ...(map.railways || []), ...(map.branchRailways || [])], map.width, map.height);
assert(trunkEdgeHits >= 2, `seed ${seed}: hidden OD context produces real trunk continuations across published boundary`);
}
const cropSource = fs.readFileSync(new URL('../src/initialGenerationCrop.js', import.meta.url), 'utf8');
const postSource = fs.readFileSync(new URL('../src/mapPostAdminTransport.js', import.meta.url), 'utf8');
const rendererSource = fs.readFileSync(new URL('../src/renderer.js', import.meta.url), 'utf8');
assert(cropSource.includes('rebalanceVisiblePrefectureMunicipalityFloor(out, 10)'), 'visible prefecture floor is applied after exact crop');
assert(cropSource.includes('pruneVisibleCropOrphanMinorRoads(out)'), 'minor-road orphan cleanup is re-run after exact crop');
assert(postSource.includes('ensureVisibleCropMajorCityInternalService') && postSource.includes('densifyNationalToRailRatio'), 'visible-core transport quality finalizers exist');
assert(rendererSource.includes('land fill and coastline share the exact same binary sea mask') && rendererSource.includes('color = centerWater ? waterColor : landColor'), 'coastline and land/water fill use the same binary sea mask');
console.log('All r11.5 visible-quality finalizer regression checks passed.');

View file

@ -0,0 +1,94 @@
import assert from 'node:assert/strict';
import { Worker } from 'node:worker_threads';
import { generateMap } from '../src/mapPipeline.js';
import { createWorldMap } from '../src/worldMap.js';
import { buildMaximumProductionLasso, derivePatchSeed } from './production-fixtures.mjs';
const initial = generateMap(114514);
const world = createWorldMap(initial);
const rect = buildMaximumProductionLasso(world);
const terrainType = initial.terrainTemplate?.terrainType || initial.terrainDebug?.terrainType || 'auto';
const candidatePlan = [0, 1, 2].map((variant, index) => ({
candidateId: `r11.6-large:${variant}`,
candidateOrdinal: index + 1,
variant,
seed: derivePatchSeed(world.seed, terrainType, variant),
}));
const worker = new Worker(new URL('./browser-worker-node-shim.mjs', import.meta.url), { type: 'module' });
const startedAt = performance.now();
const result = await new Promise((resolve, reject) => {
const timeoutMs = 540_000;
const timer = setTimeout(() => {
reject(new Error(`r11.6 max-range best-of timed out after ${timeoutMs / 1000} seconds`));
void worker.terminate().catch(() => {});
}, timeoutMs);
worker.on('message', (message) => {
if (message.id !== 1 || message.type === 'progress') return;
clearTimeout(timer);
if (!message.ok) reject(new Error(message.error || message.code || 'worker failed'));
else resolve(message.result);
});
worker.on('error', (error) => { clearTimeout(timer); reject(error); });
worker.postMessage({
id: 1,
world,
rect,
options: {
patchMode: 'expansion', terrainType, variant: 0, seed: candidatePlan[0].seed,
maxQualityRetries: 0, qualityTerrainAttempts: 1,
acceptBestAvailableQuality: false, includeSeamVisualization: false,
},
search: {
searchId: 'r11.6-large-bestof-quality', operationId: 'r11.6-large-bestof-quality',
committedRevision: 1, workerEpoch: 1, executionAttempt: 1,
totalCandidateCount: 3, selectBestCandidate: true, draftSelection: true,
parallelDrafts: false, candidatePlan,
},
});
});
const elapsedMs = performance.now() - startedAt;
assert.equal(result?.ok, true, 'max-range best-of must produce a publishable full-production candidate');
assert.equal(result?.bestOfCandidates, true, 'multiple-candidate highest-quality selection remains enabled');
assert.equal(result?.candidateQuality?.hardPass, true, 'selected candidate must pass full quality');
assert.equal(result?.candidateQuality?.finalMerge?.hardPass, true, 'selected whole-selection merge must pass');
assert.equal(result?.seamDiagnostics?.hardPass, true, 'selected candidate must pass seam audit');
const attempts = result.searchAttempts || [];
const fullAttempts = attempts.filter((a) => ['evaluated', 'success'].includes(a.status));
assert.equal(fullAttempts.length, 3, 'overlapping admissible bounds compare all three complete candidates');
assert.equal(result?.draftSelection?.fullGenerationPassCount, 3, 'winner publication must not perform a fourth rematerialization generation');
const maxScore = Math.max(...fullAttempts.map((a) => Number(a.candidateQuality?.finalMerge?.score ?? -Infinity)));
assert(Math.abs(Number(result.candidateQuality.finalMerge.score) - maxScore) < 1e-12, 'selected candidate must be the highest final-production quality among full candidates');
assert(['admissible-bound-cached-winner', 'admissible-bound-winner-live'].includes(result?.draftSelection?.selectionReason), 'winner must publish either directly from the live exact Production state or from retained exact replay state, never by a fourth full regeneration');
const req = result.candidateQuality.finalMerge.transportRequirements || {};
const cls = result.candidateQuality.finalMerge.transportClasses || {};
for (const key of ['national', 'expressway', 'railTrunk']) {
if (!req[key]?.demand) continue;
assert((cls[key]?.cells || 0) >= (req[key]?.minCells || 0), `${key}: routed coverage floor must be met`);
assert((cls[key]?.paths || 0) > 0, `${key}: demanded class must exist`);
}
assert(Object.entries(req).some(([key, value]) => value?.demand && (cls[key]?.paths || 0) < (value?.minPaths || 0) && (cls[key]?.cells || 0) >= (value?.minCells || 0)),
'fixture must exercise representation-independent quality: fewer merged paths than diagnostic minPaths while routed coverage still passes');
const compactReq = fullAttempts.find((a) => a.candidateQuality?.finalMerge?.transportRequirements)?.candidateQuality?.finalMerge?.transportRequirements || {};
for (const value of Object.values(compactReq)) {
assert.equal(value.required, value.demand, 'compact Worker diagnostics must mirror the real demand field');
}
console.log(JSON.stringify({
ok: true,
elapsedMs: Math.round(elapsedMs),
selectedVariant: result.actualVariant,
selectedScore: result.candidateQuality.finalMerge.score,
scores: fullAttempts.map((a) => ({ variant: a.variant, score: a.candidateQuality?.finalMerge?.score })),
fullGenerationPassCount: result.draftSelection.fullGenerationPassCount,
selectionReason: result.draftSelection.selectionReason,
transportClasses: cls,
transportRequirements: req,
}, null, 2));
await worker.terminate();
process.exit(0);

View file

@ -0,0 +1,89 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { Worker } from 'node:worker_threads';
function runWorker(seed) {
return new Promise((resolve, reject) => {
const worker = new Worker(new URL('./helpers-generation-worker-node-wrapper.mjs', import.meta.url), { type: 'module' });
const timer = setTimeout(() => { worker.terminate(); reject(new Error(`worker timeout seed ${seed}`)); }, 120000);
worker.on('error', (error) => { clearTimeout(timer); reject(error); });
worker.on('message', async (message) => {
if (message?.type !== 'result' || message.id !== seed) return;
clearTimeout(timer); worker.removeAllListeners(); await worker.terminate();
if (!message.ok) reject(new Error(message.error || 'generation failed')); else resolve(message.map);
});
worker.postMessage({ id: seed, seed, options: { terrainType: 'auto' } });
});
}
function pathLength(paths) {
let sum = 0;
for (const path of paths || []) for (let i = 1; i < (path?.length || 0); i++) sum += Math.hypot(path[i][0] - path[i - 1][0], path[i][1] - path[i - 1][1]);
return sum;
}
function maxGap(paths) {
let max = 0;
for (const path of paths || []) for (let i = 1; i < (path?.length || 0); i++) max = Math.max(max, Math.hypot(path[i][0] - path[i - 1][0], path[i][1] - path[i - 1][1]));
return max;
}
function pointPathDistance(point, paths) {
let best = Infinity;
for (const path of paths || []) for (const q of path || []) best = Math.min(best, Math.hypot(q[0] - point.x, q[1] - point.y));
return best;
}
function endpointContinuityAudit(map) {
const bad = [];
const exp = map.expressways || [];
const ordinary = [...(map.nationalRoads || []), ...(map.minorRoads || []), ...(map.externalRoads || [])];
const cities = (map.modernCities || []).filter((c) => (c.population || 0) >= 50000);
for (let i = 0; i < exp.length; i++) {
const path = exp[i]; if (!path?.length) continue;
const others = exp.filter((_, j) => j !== i);
for (const tuple of [path[0], path[path.length - 1]]) {
const pt = { x: tuple[0], y: tuple[1] };
const edge = pt.x < 2 || pt.y < 2 || pt.x > map.width - 3 || pt.y > map.height - 3;
const interchange = (map.interchanges || []).some((ic) => Math.hypot(ic.x - pt.x, ic.y - pt.y) <= 6.5);
const city = cities.some((c) => Math.hypot(c.x - pt.x, c.y - pt.y) <= 28);
const connected = edge || pointPathDistance(pt, others) <= 3.5 || pointPathDistance(pt, ordinary) <= 4.5 || interchange || city;
if (!connected) bad.push({ path: i, x: pt.x, y: pt.y });
}
}
return bad;
}
const source = fs.readFileSync(new URL('../src/mapPostAdminTransport.js', import.meta.url), 'utf8');
assert(!source.includes('function directPath('), 'post-admin transport has no directPath straight-line fallback');
assert(!source.includes('function directLandConnector('), 'transport has no directLandConnector straight-line fallback');
assert(source.includes('if (gap > maxJoinGap) return []'), 'failed chain legs reject the whole trunk instead of drawing a straight chord');
assert(source.includes('removeDiscontinuousTransportPaths(2.25)'), 'final production invariant removes sparse-jump transport paths');
const map = await runWorker(1);
const trunk = {
national: map.nationalRoads || [],
expressway: map.expressways || [],
rail: [...(map.railways || []), ...(map.branchRailways || [])],
};
for (const [name, paths] of Object.entries(trunk)) {
assert(maxGap(paths) <= Math.SQRT2 + 1e-6, `${name}: every emitted segment is raster-contiguous; no renderer straight chord remains`);
}
const nationalLength = pathLength(trunk.national);
const railLength = pathLength(trunk.rail);
assert(nationalLength > 0 && railLength / nationalLength >= 0.80 && railLength / nationalLength <= 1.01,
`rail density remains high (${(railLength / nationalLength).toFixed(3)}) and approximately national-road scale`);
const expressLength = pathLength(trunk.expressway);
assert((map.interchanges || []).length >= Math.max(2, Math.floor(expressLength / 18)),
`IC density is sufficient for ${expressLength.toFixed(1)} expressway cells`);
assert.equal(endpointContinuityAudit(map).length, 0, 'expressways have no unjustified interior dead-end endpoints');
const ordinary = [...(map.minorRoads || []), ...(map.nationalRoads || []), ...(map.externalRoads || [])];
const villages = map.villages || [];
const ruralServed = villages.filter((v) => pointPathDistance(v, ordinary) <= 4).length;
assert(!villages.length || ruralServed / villages.length >= 0.80, `rural village road coverage is ${(ruralServed / Math.max(1, villages.length)).toFixed(3)}`);
assert((map.minorRoads || []).length >= 60, 'published countryside retains a substantial local-road network');
const post = map.transportDebug?.postAdminTransportFinalization || {};
assert((post.finalInterchangeRebuild?.added || 0) >= (post.finalInterchangeRebuild?.target || 0), 'final IC rebuild meets its production target');
assert(post.finalDiscontinuousTransportCleanup, 'final discontinuity cleanup ran');
console.log('All r11.7 terrain-routed transport density regression checks passed.');

View file

@ -0,0 +1,195 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { Worker } from 'node:worker_threads';
const HERE = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(HERE, '..');
function runWorker(seed) {
return new Promise((resolve, reject) => {
const worker = new Worker(new URL('./helpers-generation-worker-node-wrapper.mjs', import.meta.url), { type: 'module' });
const timer = setTimeout(() => { worker.terminate(); reject(new Error(`worker timeout seed ${seed}`)); }, 180000);
worker.on('error', (error) => { clearTimeout(timer); reject(error); });
worker.on('message', async (message) => {
if (message?.type !== 'result' || message.id !== seed) return;
clearTimeout(timer);
worker.removeAllListeners();
await worker.terminate();
if (!message.ok) reject(new Error(message.error || 'generation failed'));
else resolve(message.map);
});
worker.postMessage({ id: seed, seed, options: { terrainType: 'auto' } });
});
}
function pathLength(paths) {
let total = 0;
for (const p of paths || []) for (let k = 1; k < (p?.length || 0); k++) total += Math.hypot(p[k][0] - p[k - 1][0], p[k][1] - p[k - 1][1]);
return total;
}
function maxVertexGap(paths) {
let max = 0;
for (const p of paths || []) for (let k = 1; k < (p?.length || 0); k++) max = Math.max(max, Math.hypot(p[k][0] - p[k - 1][0], p[k][1] - p[k - 1][1]));
return max;
}
function pointPathDistance(point, paths) {
let best = Infinity;
for (const p of paths || []) for (const q of p || []) best = Math.min(best, Math.hypot(point.x - q[0], point.y - q[1]));
return best;
}
function tangent(p, k) {
const a = p[Math.max(0, k - 2)], b = p[Math.min(p.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 longestParallelRun(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 = tangent(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;
if (d2 < 0.75 || d2 > radius * radius) continue;
const u = tangent(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;
}
function nearMissEndpointCount(map, source, target, maxGap) {
let nearMiss = 0, checked = 0;
for (const p of source || []) {
if (!p?.length) continue;
for (const raw of [p[0], p[p.length - 1]]) {
const point = { x: raw[0], y: raw[1] };
if (point.x <= 1 || point.y <= 1 || point.x >= map.width - 2 || point.y >= map.height - 2) continue;
checked++;
let best = Infinity;
for (const q of target || []) {
if (!q || q === p) continue;
best = Math.min(best, pointPathDistance(point, [q]));
}
if (best > 0.75 && best <= maxGap) nearMiss++;
}
}
return { nearMiss, checked, rate: nearMiss / Math.max(1, checked) };
}
function transportTerrainAudit(map, paths) {
let seaCells = 0, highElevationCells = 0, extremeSlopeCells = 0, mountainRidgeCells = 0;
for (const p of paths || []) for (const raw of p || []) {
const x = Math.round(raw[0]), y = Math.round(raw[1]);
if (x < 0 || y < 0 || x >= map.width || y >= map.height) { seaCells++; continue; }
const i = y * map.width + x;
if (map.sea?.[i]) seaCells++;
const e = map.elevation?.[i] || 0, s = map.slope?.[i] || 0, r = map.ridgeField?.[i] || 0;
if (e >= 0.695) highElevationCells++;
if (s >= 0.60) extremeSlopeCells++;
if (e >= 0.58 && r >= 0.72) mountainRidgeCells++;
}
return { seaCells, highElevationCells, extremeSlopeCells, mountainRidgeCells };
}
function expresswayDeadEnds(map) {
const main = map.expressways || [], external = map.externalExpressways || [];
const bad = [];
for (let i = 0; i < main.length; i++) {
const path = main[i]; if (!path?.length) continue;
for (const raw of [path[0], path[path.length - 1]]) {
const p = { x: raw[0], y: raw[1] };
if (p.x <= 2 || p.y <= 2 || p.x >= map.width - 3 || p.y >= map.height - 3) continue;
const joined = pointPathDistance(p, [...main.filter((_, j) => j !== i), ...external]) <= 3.5;
const ic = (map.interchanges || []).some((q) => Math.hypot(q.x - p.x, q.y - p.y) <= 9.0);
const city = (map.modernCities || []).some((q) => (q.population || 0) >= 50000 && Math.hypot(q.x - p.x, q.y - p.y) <= 28);
const port = (map.ports || []).some((q) => Math.hypot(q.x - p.x, q.y - p.y) <= 18);
if (!(joined || (ic && (city || port)))) bad.push({ x: p.x, y: p.y, joined, ic, city, port });
}
}
return bad;
}
for (const seed of [1, 2]) {
const map = await runWorker(seed);
const national = map.nationalRoads || [];
const expressway = map.expressways || [];
const rail = [...(map.railways || []), ...(map.branchRailways || [])];
const local = map.minorRoads || [];
const trunk = [...national, ...expressway, ...rail];
assert(maxVertexGap(trunk) <= Math.SQRT2 + 1e-6, `seed ${seed}: all emitted trunk geometry is raster-contiguous`);
const terrain = transportTerrainAudit(map, trunk);
assert.equal(terrain.seaCells, 0, `seed ${seed}: no published trunk cell crosses sea`);
assert.equal(terrain.highElevationCells, 0, `seed ${seed}: no published trunk cell crosses the hard high-elevation ceiling`);
assert.equal(terrain.extremeSlopeCells, 0, `seed ${seed}: no published trunk cell crosses an extreme slope`);
assert.equal(terrain.mountainRidgeCells, 0, `seed ${seed}: no published trunk cell traverses a high mountain ridge`);
assert(longestParallelRun(national, 3) <= 5, `seed ${seed}: national-road kilometre-scale parallelism is bounded`);
assert(longestParallelRun(expressway, 4) <= 3, `seed ${seed}: expressway parallel corridors are collapsed`);
const nationalMiss = nearMissEndpointCount(map, national, [...national, ...(map.externalRoads || [])], 4.6);
const expressMiss = nearMissEndpointCount(map, expressway, [...expressway, ...(map.externalExpressways || [])], 5.0);
const railMiss = nearMissEndpointCount(map, rail, [...rail, ...(map.externalRailways || [])], 4.5);
const localMiss = nearMissEndpointCount(map, local, [...local, ...national, ...(map.externalRoads || [])], 3.6);
assert(nationalMiss.nearMiss <= 3, `seed ${seed}: national near-miss endpoints are rare (${nationalMiss.nearMiss})`);
assert.equal(expressMiss.nearMiss, 0, `seed ${seed}: expressways have no close-but-unjoined endpoints`);
assert.equal(railMiss.nearMiss, 0, `seed ${seed}: railways have no close-but-unjoined endpoints`);
assert(localMiss.rate <= 0.015, `seed ${seed}: local close-but-unjoined endpoint rate is ${(localMiss.rate * 100).toFixed(2)}%`);
const nationalLen = pathLength(national), expressLen = pathLength(expressway), railLen = pathLength(rail);
assert(nationalLen > 0 && railLen / nationalLen >= 0.82 && railLen / nationalLen <= 1.02,
`seed ${seed}: rail density remains high and slightly below national-road scale (${(railLen / nationalLen).toFixed(3)})`);
const icCount = (map.interchanges || []).length;
assert(icCount <= Math.ceil(expressLen / 12) + 2, `seed ${seed}: IC count is not over-dense (${icCount} for ${expressLen.toFixed(1)} cells)`);
assert(icCount >= Math.max(1, Math.floor(expressLen / 30)), `seed ${seed}: retained expressway still has usable IC coverage`);
assert.equal(expresswayDeadEnds(map).length, 0, `seed ${seed}: internal expressway endpoints are connected or terminate at a city/port IC`);
const ordinary = [...local, ...national, ...(map.externalRoads || [])];
const villages = map.villages || [];
const ruralServed = villages.filter((v) => pointPathDistance(v, ordinary) <= 4).length;
assert(!villages.length || ruralServed / villages.length >= 0.80, `seed ${seed}: rural-road village coverage is ${(ruralServed / Math.max(1, villages.length)).toFixed(3)}`);
assert(local.length >= 70, `seed ${seed}: countryside retains a substantial organic local-road network (${local.length})`);
const post = map.transportDebug?.postAdminTransportFinalization || {};
assert.equal(post.visibleCropMajorCityService?.missing?.length || 0, 0, `seed ${seed}: visible major-city trunk service has no unresolved city`);
assert.equal(post.postDedupeMajorCityService?.missing?.length || 0, 0, `seed ${seed}: final major-city national/rail/expressway contract passes`);
assert(post.absoluteFinalTerrainInvariant, `seed ${seed}: absolute final terrain invariant executed`);
const interchangeAudit = post.absoluteFinalInterchangeRebuild;
assert(interchangeAudit, `seed ${seed}: final IC rebuild is audited`);
assert(interchangeAudit.added >= icCount, `seed ${seed}: visible ICs are retained from the rebuilt pre-crop IC set`);
assert.equal(interchangeAudit.legacyUniformTarget, Math.max(0, Math.round(interchangeAudit.expresswayLength / 19.5)), `seed ${seed}: IC density reference is derived from the audited expressway length`);
assert(interchangeAudit.target >= interchangeAudit.added, `seed ${seed}: IC target accounts for every published IC`);
}
const featureSource = fs.readFileSync(path.join(ROOT, 'src/mapFeatures.js'), 'utf8');
const postSource = fs.readFileSync(path.join(ROOT, 'src/mapPostAdminTransport.js'), 'utf8');
const cropSource = fs.readFileSync(path.join(ROOT, 'src/initialGenerationCrop.js'), 'utf8');
const rendererSource = fs.readFileSync(path.join(ROOT, 'src/renderer.js'), 'utf8');
const appSource = fs.readFileSync(path.join(ROOT, 'src/app.js'), 'utf8');
const cssSource = fs.readFileSync(path.join(ROOT, 'styles/styles.css'), 'utf8');
assert(!postSource.includes('function directPath(') && !postSource.includes('function directLandConnector('), 'no direct trunk fallback exists');
assert(featureSource.includes('const trunkMode = mode === "expressway" || mode === "national" || mode === "rail"'), 'all production trunk route call-sites share one terrain-first policy clamp');
assert(featureSource.includes('heuristicWeight: trunkMode') && featureSource.includes('Math.min(options.heuristicWeight ?? 0.08'), 'late trunk callers cannot restore a dominant Euclidean heuristic');
assert(featureSource.includes('literal endpoint chord') && postSource.includes('geometric chord'), 'subtle near-straight terrain-blind corridors are explicitly audited, not just coordinate jumps');
assert(postSource.includes('if (direct >= 18 && chordHardShare >= 0.08 && straightness > 0.90) return false'), 'final production terrain validator rejects near-chord trunks when the chord crosses hostile terrain');
assert(postSource.includes('runs.maxSeaRun > 0'), 'shared trunk alignment cannot reintroduce a sea crossing');
assert(cropSource.includes('let alreadyConnected = false') && cropSource.includes('passes: 2'), 'visible-crop road welding distinguishes exact junctions from near misses and performs a second bounded pass');
assert(postSource.includes('if (!hit || hit.connected) continue'), 'post-admin topology repair does not keep extending already-connected endpoints');
assert(postSource.includes('if (total < 8)') && postSource.includes('final-terminal-ic'), 'retained short expressway spurs still receive a real terminal IC rather than ending mid-road');
const municipalDraw = rendererSource.indexOf('drawLabels(ctx, municipalFallbackLabels, Infinity, occupiedLabels)');
const prefectureDraw = rendererSource.indexOf('drawLabels(ctx, prefectureLabels, Infinity, occupiedLabels)', municipalDraw + 1);
assert(municipalDraw >= 0 && prefectureDraw > municipalDraw, 'prefecture labels render above municipality labels in the general map pass');
assert(rendererSource.includes('drawLabels(ctx, prefectureLabels, Infinity, adminOccupied)'), 'prefecture labels also render last in administrative mode');
assert(rendererSource.includes('land fill and coastline share the exact same binary sea mask'), 'coastline and land fill retain their shared binary sea-mask contract');
assert(cssSource.includes('background:rgba(17,22,31,.84)'), 'tooltip is slightly translucent');
assert(appSource.includes('cursorY + 10') && appSource.includes('maxTop'), 'tooltip follows the cursor to the bottom before clamping');
assert(appSource.includes('const exclusion = 12') && appSource.includes('leftAlt') && appSource.includes('rightAlt'), 'tooltip reserves a cursor exclusion zone and flips horizontally at edges');
console.log('All r11.8 terrain/topology/tooltip regression checks passed.');

View file

@ -209,7 +209,7 @@ if (launchBrowser) {
const server = createServer(async (request, response) => {
try {
const url = new URL(request.url || "/", "http://127.0.0.1");
const relative = decodeURIComponent(url.pathname === "/" ? "/tests/additional-generation-e2e.html" : url.pathname);
const relative = decodeURIComponent(url.pathname === "/" ? "/index.html" : url.pathname);
const file = resolve(workspace, `.${relative}`);
if (file !== workspace && !file.startsWith(`${workspace}${sep}`)) throw new Error("Path outside workspace");
const bytes = await readFile(file);

View file

@ -2,54 +2,70 @@ import { performance } from "node:perf_hooks";
import { spawn } from "node:child_process";
import { fileURLToPath } from "node:url";
const defaultSuites = [
"additional-generation-unit",
"additional-generation-coverage-worker",
"core",
"terrain",
"terrain-name",
"admin",
"patch",
"patch-large",
"determinism-114514",
"determinism-12345",
"determinism-54321",
"determinism-777",
"determinism-999",
const cwd = fileURLToPath(new URL(".", import.meta.url));
const testFile = fileURLToPath(new URL("./test.js", import.meta.url));
const suiteDefinitions = [
{ name: "additional-generation-unit", file: "./additional-generation-unit.mjs", timeoutMs: 180_000, canonical: true },
{ name: "additional-generation-coverage-worker", file: "./additional-generation-coverage-worker.mjs", timeoutMs: 240_000, canonical: true },
{ name: "r10-exact-production-worker", file: "./r10-exact-production-worker.mjs", timeoutMs: 180_000, canonical: true },
{ name: "r11-selection-native-production", file: "./r11-selection-native-production.mjs", timeoutMs: 180_000, canonical: true },
{ name: "additional-generation-max-worker", file: "./additional-generation-max-worker.mjs", timeoutMs: 240_000, release: true },
{ name: "patch-worker-cancel", file: "./patch-worker-cancel.mjs", timeoutMs: 180_000, release: true },
{ name: "patch-worker-mirror-sync", file: "./patch-worker-mirror-sync.mjs", timeoutMs: 180_000, release: true },
{ name: "r11.4-literal-initial-overscan-worker", file: "./r11.4-literal-initial-overscan-worker.mjs", timeoutMs: 300_000, release: true },
{ name: "r11.4-transport-demography-overscan", file: "./r11.4-transport-demography-overscan.mjs", timeoutMs: 300_000, release: true },
{ name: "r11.5-visible-quality-finalizer", file: "./r11.5-visible-quality-finalizer.mjs", timeoutMs: 600_000, release: true },
{ name: "r11.6-large-bestof-quality", file: "./r11.6-large-bestof-quality.mjs", timeoutMs: 600_000, release: true },
{ name: "r11.7-terrain-routed-transport-density", file: "./r11.7-terrain-routed-transport-density.mjs", timeoutMs: 300_000, release: true },
{ name: "r11.8-terrain-topology-tooltip", file: "./r11.8-terrain-topology-tooltip.mjs", timeoutMs: 600_000, release: true },
{ name: "additional-generation-browser", file: "./run-additional-generation-browser.mjs", timeoutMs: 600_000, browser: true },
{ name: "core", timeoutMs: 360_000, canonical: true },
{ name: "terrain", timeoutMs: 600_000, canonical: true },
{ name: "terrain-name", timeoutMs: 240_000, canonical: true },
{ name: "admin", timeoutMs: 300_000, canonical: true },
{ name: "patch", timeoutMs: 300_000, canonical: true },
{ name: "patch-large", timeoutMs: 600_000, canonical: true },
{ name: "determinism-114514", timeoutMs: 240_000, canonical: true },
{ name: "determinism-12345", timeoutMs: 240_000, canonical: true },
{ name: "determinism-54321", timeoutMs: 240_000, canonical: true },
{ name: "determinism-777", timeoutMs: 240_000, canonical: true },
{ name: "determinism", timeoutMs: 240_000 },
{ name: "all", timeoutMs: 900_000 },
];
const suiteRegistry = new Map(suiteDefinitions.map((definition) => [definition.name, definition]));
const suiteGroups = new Map([
["default", suiteDefinitions.filter((definition) => definition.canonical).map((definition) => definition.name)],
["release", suiteDefinitions.filter((definition) => definition.canonical || definition.release).map((definition) => definition.name)],
["browser", suiteDefinitions.filter((definition) => definition.browser).map((definition) => definition.name)],
]);
const defaultTimeoutMs = 240_000;
function resolveSuite(name) {
const registered = suiteRegistry.get(name);
if (registered) return registered;
if (/^determinism-(?:0|[1-9]\d*)$/.test(name)) return { name, timeoutMs: defaultTimeoutMs };
return null;
}
const requestedSuites = String(process.env.TEST_SUITES || "").split(",").map((value) => value.trim()).filter(Boolean);
const suites = requestedSuites.length ? requestedSuites : defaultSuites;
const requestedGroup = String(process.env.TEST_GROUP || "default").trim();
if (!suiteGroups.has(requestedGroup)) throw new Error(`Unknown test group: ${requestedGroup}`);
const suites = requestedSuites.length ? requestedSuites : suiteGroups.get(requestedGroup);
const unknownSuites = suites.filter((suite) => !resolveSuite(suite));
if (unknownSuites.length) throw new Error(`Unknown test suite${unknownSuites.length === 1 ? "" : "s"}: ${unknownSuites.join(", ")}`);
const concurrency = Math.max(1, Math.min(2, Number(process.env.TEST_CONCURRENCY) || 1));
// Full-map shards can briefly peak at several hundred MB. CI may opt into a
// handoff delay when its runtime needs extra time to reclaim a completed child.
const suiteCooldownMs = Math.max(0, Number(process.env.TEST_SUITE_COOLDOWN_MS ?? 0));
const suiteTimeoutMs = {
"additional-generation-unit": 180_000,
"additional-generation-coverage-worker": 120_000,
core: 360_000,
terrain: 600_000,
"terrain-name": 240_000,
admin: 300_000,
patch: 300_000,
"patch-large": 600_000,
};
const defaultTimeoutMs = 240_000;
const maxOutputBytes = 32 * 1024 * 1024;
const cwd = fileURLToPath(new URL(".", import.meta.url));
const testFile = fileURLToPath(new URL("./test.js", import.meta.url));
const additionalUnitFile = fileURLToPath(new URL("./additional-generation-unit.mjs", import.meta.url));
const additionalCoverageWorkerFile = fileURLToPath(new URL("./additional-generation-coverage-worker.mjs", import.meta.url));
function runSuite(suite) {
return new Promise((resolve) => {
const timeoutMs = suiteTimeoutMs[suite] || defaultTimeoutMs;
const descriptor = resolveSuite(suite);
const timeoutMs = descriptor.timeoutMs || defaultTimeoutMs;
const started = performance.now();
console.error(`[test-all] start ${suite}`);
const standaloneFile = suite === "additional-generation-unit"
? additionalUnitFile
: suite === "additional-generation-coverage-worker"
? additionalCoverageWorkerFile
: null;
const standaloneFile = descriptor.file ? fileURLToPath(new URL(descriptor.file, import.meta.url)) : null;
const commandFile = standaloneFile || testFile;
const commandArgs = standaloneFile ? [commandFile] : [commandFile, `--suite=${suite}`];
const child = spawn(process.execPath, commandArgs, {
@ -57,25 +73,33 @@ function runSuite(suite) {
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
const stdoutChunks = [];
const stderrChunks = [];
let outputBytes = 0;
let outputOverflow = false;
let timedOut = false;
let forceKillTimer = null;
let resolved = false;
const append = (current, chunk) => {
if (outputOverflow) return current;
const next = current + chunk.toString("utf8");
if (Buffer.byteLength(next, "utf8") > maxOutputBytes) {
const finish = (payload) => {
if (resolved) return;
resolved = true;
resolve(payload);
};
const append = (target, chunk) => {
if (outputOverflow) return;
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
if (outputBytes + buffer.byteLength > maxOutputBytes) {
outputOverflow = true;
child.kill("SIGTERM");
return current;
return;
}
return next;
outputBytes += buffer.byteLength;
target.push(buffer);
};
child.stdout.on("data", (chunk) => { stdout = append(stdout, chunk); });
child.stderr.on("data", (chunk) => { stderr = append(stderr, chunk); });
child.stdout.on("data", (chunk) => append(stdoutChunks, chunk));
child.stderr.on("data", (chunk) => append(stderrChunks, chunk));
const timeout = setTimeout(() => {
timedOut = true;
@ -90,7 +114,7 @@ function runSuite(suite) {
if (forceKillTimer) clearTimeout(forceKillTimer);
const seconds = Math.round((performance.now() - started) / 10) / 100;
console.error(`[test-all] error ${suite}: ${error.message}`);
resolve({
finish({
suite,
seconds,
fullMapGenerations: null,
@ -107,22 +131,31 @@ function runSuite(suite) {
clearTimeout(timeout);
if (forceKillTimer) clearTimeout(forceKillTimer);
const seconds = Math.round((performance.now() - started) / 10) / 100;
const stdout = Buffer.concat(stdoutChunks).toString("utf8");
const stderr = Buffer.concat(stderrChunks).toString("utf8");
const output = `${stdout}\n${stderr}`;
const ng = (output.match(/^NG:/gm) || []).length;
const info = output.match(/INFO: suite=([^;]+); fullMapGenerations=(\d+); elapsedMs=(\d+)/);
const infrastructureError = outputOverflow
? `Output exceeded ${maxOutputBytes} bytes`
: null;
const testFailure = status !== 0 && !signal && !timedOut && !outputOverflow && !infrastructureError;
const failedAssertions = ng > 0 ? ng : testFailure ? 1 : 0;
console.error(
`[test-all] end ${suite}: status=${status} signal=${signal || "none"} ` +
`failures=${ng} seconds=${seconds}`,
`failures=${failedAssertions} seconds=${seconds}`,
);
resolve({
if (testFailure || signal || timedOut || outputOverflow || infrastructureError) {
const tail = output.trim().slice(-16_000);
if (tail) console.error(`[test-all] output ${suite}:\n${tail}`);
}
finish({
suite,
seconds,
fullMapGenerations: info ? Number(info[2]) : null,
elapsedMsReported: info ? Number(info[3]) : null,
failedAssertions: ng,
failedAssertions,
testFailure,
status,
signal: signal || null,
timedOut,
@ -157,7 +190,7 @@ const failures = completed.reduce(
0,
);
const infrastructureFailure = completed.some(
(row) => row.status !== 0 || row.signal || row.timedOut || row.outputOverflow || row.infrastructureError,
(row) => row.signal || row.timedOut || row.outputOverflow || row.infrastructureError,
);
const withinSuiteBudgets = completed.every((row) => !row.timedOut);

View file

@ -1,10 +0,0 @@
import { generateMap } from "../src/mapPipeline.js";
const seed = Number(process.argv[2]);
if (!Number.isFinite(seed)) throw new Error("seed is required");
const a = generateMap(seed, { onProgress() {} });
const b = generateMap(seed, { onProgress() {} });
console.log(JSON.stringify({
seed,
adminIdEqual: Buffer.from(a.adminId.buffer, a.adminId.byteOffset, a.adminId.byteLength).equals(Buffer.from(b.adminId.buffer, b.adminId.byteOffset, b.adminId.byteLength)),
adminDebugEqual: JSON.stringify(a.adminDebug) === JSON.stringify(b.adminDebug),
}));

View file

@ -22,6 +22,10 @@ const TEST_SUITE = (() => {
const arg = process.argv.find((value) => value.startsWith("--suite="));
return arg ? arg.slice("--suite=".length) : "core";
})();
const STATIC_TEST_SUITES = new Set(["all", "core", "terrain", "terrain-name", "admin", "patch", "patch-large", "determinism"]);
if (!STATIC_TEST_SUITES.has(TEST_SUITE) && !/^determinism-(?:0|[1-9]\d*)$/.test(TEST_SUITE)) {
throw new Error(`Unknown test suite: ${TEST_SUITE}`);
}
const TEST_STARTED_AT = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
const DETERMINISM_SEED = (() => {
if (IS_BROWSER) return Number(new URLSearchParams(location.search).get("seed")) || 114514;
@ -43,22 +47,36 @@ async function readLocalText(path) {
return readFile(new URL(path, import.meta.url), "utf8");
}
const [namesSource, mapGeneratorSource, mapOutputSource, mapTerrainSource, rendererSource, appSource, mapPipelineSource, mapAdminStageSource, mapPatchSource, mapPatchWorkerSource, committedWorldDeltaSource, worldMapSource, municipalSource, testSource] = await Promise.all([
readLocalText("../src/names.js"),
readLocalText("../src/mapPipeline.js"),
readLocalText("../src/mapOutput.js"),
readLocalText("../src/mapTerrain.js"),
readLocalText("../src/renderer.js"),
readLocalText("../src/app.js"),
readLocalText("../src/mapPipeline.js"),
readLocalText("../src/mapAdminStage.js"),
readLocalText("../src/mapPatch.js"),
readLocalText("../src/mapPatchWorker.js"),
readLocalText("../src/committedWorldDelta.js"),
readLocalText("../src/worldMap.js"),
readLocalText("../src/mapMunicipalCoherence.js"),
readLocalText("./test.js"),
]);
let namesSource = "";
let mapGeneratorSource = "";
let mapOutputSource = "";
let mapTerrainSource = "";
let rendererSource = "";
let appSource = "";
let mapPipelineSource = "";
let mapAdminStageSource = "";
let mapPatchSource = "";
let mapPatchWorkerSource = "";
let committedWorldDeltaSource = "";
let worldMapSource = "";
let municipalSource = "";
if (suiteEnabled("core")) {
[namesSource, mapOutputSource, mapTerrainSource, rendererSource, appSource, mapPipelineSource, mapAdminStageSource, mapPatchSource, mapPatchWorkerSource, committedWorldDeltaSource, worldMapSource, municipalSource] = await Promise.all([
readLocalText("../src/names.js"),
readLocalText("../src/mapOutput.js"),
readLocalText("../src/mapTerrain.js"),
readLocalText("../src/renderer.js"),
readLocalText("../src/app.js"),
readLocalText("../src/mapPipeline.js"),
readLocalText("../src/mapAdminStage.js"),
readLocalText("../src/mapPatch.js"),
readLocalText("../src/mapPatchWorker.js"),
readLocalText("../src/committedWorldDelta.js"),
readLocalText("../src/worldMap.js"),
readLocalText("../src/mapMunicipalCoherence.js"),
]);
mapGeneratorSource = mapPipelineSource;
}
const derivePatchSeedStart = appSource.indexOf("function derivePatchSeed");
const derivePatchSeedEnd = derivePatchSeedStart >= 0 ? appSource.indexOf("\n}", derivePatchSeedStart) : -1;
const derivePatchSeedSource = derivePatchSeedStart >= 0 && derivePatchSeedEnd > derivePatchSeedStart
@ -707,7 +725,6 @@ try {
let terrainSeedSummaries = [];
if (suiteEnabled("core")) {
const map = generateTestMap(12345);
const other = generateTestMap(54321);
const urbanCellCount = [...map.landuse].filter((value) => value >= 2 && value <= 8).length;
const cityPopulations = map.modernCities.map((city) => city.population || 0);
const maxPopulation = Math.max(...cityPopulations);
@ -827,7 +844,7 @@ try {
assert(NAME_TEMPLATE_WEIGHTS && NAME_TEMPLATE_WEIGHTS.generic?.modifierTerrain > 0, "NAME_TEMPLATE_WEIGHTS exists");
assert(NAME_PROBABILITIES && NAME_PROBABILITIES.contextCategoryWeights?.generic, "NAME_PROBABILITIES exists");
const removedContextModule = "placeName" + "Context.js";
assert(!namesSource.includes(removedContextModule) && !mapGeneratorSource.includes(removedContextModule) && !testSource.includes(removedContextModule), "removed name-context import is absent");
assert(!namesSource.includes(removedContextModule) && !mapGeneratorSource.includes(removedContextModule), "removed name-context import is absent from production modules");
assert(Object.keys(NAME_KANJI_POOLS).every((key) => Array.isArray(NAME_KANJI_POOLS[key])), "name category pools are centralized arrays");
assert(Object.values(NAME_KANJI_POOLS).every((pool) => pool.every((part) => typeof part === "string" && !part.includes("\uFFFD"))), "configured name category pools contain valid strings");
const removedContextSuffixConst = "CONTEXT" + "_SUFFIXES";
@ -837,6 +854,18 @@ try {
assert(mapPatchSource.includes("splitWorldPathByPatch") && mapPatchSource.includes("patchAffected"), "patch path merging is alpha-aware for lasso selections");
assert(!mapPatchSource.includes("patchAlpha(x, y, rects, 0)"), "patch admin repair uses the active patch seed");
assert(mapPatchSource.includes("refreshPatchInfluenceFields") && mapPatchSource.includes("roadCellsPainted"), "patch generation refreshes derived transport influence fields after path merges");
assert(mapPatchSource.includes("createTransportPathSpatialIndex") && mapPatchSource.includes("TRANSPORT_SPATIAL_BUCKET_SIZE"), "patch transport queries use a bucketed spatial index instead of repeatedly scanning every path");
assert(mapPatchSource.includes("pathfindScratch") && mapPatchSource.includes("Float32Array") && mapPatchSource.includes("buildHierarchicalPathCorridor"), "patch connector pathfinding reuses typed scratch storage and has a coarse-to-fine hierarchy");
assert(mapPatchSource.includes("TransportUnionFind") && mapPatchSource.includes("GraphIncrementalUnions") && mapPatchSource.includes("GraphFullRebuilds"), "transport graph repair tracks connector merges incrementally and reserves full rebuilds for audit");
assert(mapPatchSource.includes("getRadialInfluenceKernel") && mapPatchSource.includes("paintInfluenceDisk"), "regional influence refresh reuses exact radial kernels instead of recalculating distance powers per painted cell");
assert(mapPatchSource.includes("regionalUrbanScratch") && mapPatchSource.includes("population.subarray"), "regional urban recalculation reuses compact scratch buffers and row copies");
assert(mapPatchSource.includes("ensurePatchSelectionDistanceCache") && mapPatchSource.includes("_selectionDistanceCache") && mapPatchSource.includes("selectionDistanceCacheReused"), "lasso regional transport and urban recalculation share one lazy selection-distance raster");
assert(mapPatchWorkerSource.includes("admissible-terrain-scout-branch-and-bound-two-lane-v2") && mapPatchWorkerSource.includes("draftCandidateUpperBound") && mapPatchWorkerSource.includes("branchBoundPrunedCount") && mapPatchWorkerSource.includes("precomputeRawTerrainScoutBatch") && mapPatchWorkerSource.includes("fullProductionFromTerrainOnly") && !mapPatchWorkerSource.includes("draftNearTieFullGap"), "candidate ranking uses admissible Branch-and-Bound with resident two-lane terrain scouts; simplified human/transport drafts are never reused for publication");
assert(worldMapSource.includes("INITIAL_QUALITY_TRANSPORT_CLASSES") && worldMapSource.includes("initial-production-quality-v2") && mapPatchSource.includes("transportHierarchyPass"), "initial quality oracle records national, expressway, and rail-trunk production density and enforces hierarchy-aware final quality");
assert(mapPatchSource.includes("finalizeRegionalTrunkTransport") && mapPatchSource.includes("patchRegionalTrunkFinalizer") && mapPatchSource.includes("productionTransportParity"), "full additional-generation finalists restore production trunk transport and fill missing national, expressway, and trunk-rail demand");
assert(mapPatchSource.includes("connectorLayerForEndpoints") && mapPatchSource.includes("transportHierarchyAtPoint"), "transport seam repair preserves expressway, national-road, and trunk-rail hierarchy instead of demoting every connector");
assert(appSource.includes("PATCH_SEARCH_BATCH_SIZE = 3") && appSource.includes("PATCH_SEARCH_DEFAULT_LIMIT = 12") && appSource.includes("contentBatchExhausted") && appSource.includes("allCandidatePlan"), "quality-rejected candidates advance automatically in three-candidate batches up to twelve without publishing drafts");
assert(!appSource.includes("BEST AVAILABLE") && appSource.includes("acceptBestAvailableQuality: false"), "top-level preview publication has no best-available quality bypass");
assert(mapPatchSource.includes("normalizeGeneratedPointIds"), "patch generation normalizes generated point admin and prefecture ids");
assert(mapPatchSource.includes("generateUnifiedWorldNativePatchCandidate") && mapPatchSource.includes("generateMap(seed") && mapPatchSource.includes("unified-world-native-patch"), "patch modes execute the complete production generation pipeline");
assert(!mapPatchSource.includes("generateVariablePatchCandidate") && !mapPatchSource.includes("PATCH_VARIABLE_CANDIDATE_ENABLED"), "retired variable rectangle candidate implementation is removed");
@ -848,7 +877,7 @@ try {
assert(derivePatchSeedSource.includes("function derivePatchSeed(world, terrainType, variant") && !derivePatchSeedSource.includes("rect.x") && !derivePatchSeedSource.includes("rect.y"), "UI patch seed is independent of selection bounds and backing-world padding");
assert(!appSource.includes("qualityWorkerRetries: 1") && !appSource.includes("attemptVariant = (attemptVariant + 3)"), "UI does not run the obsolete hidden whole-patch retry wrapper");
assert(mapPatchSource.includes("generateTiledRegenerationPatch") && mapPatchSource.includes("patch-candidate-coverage-incomplete"), "large Regeneration is tiled and rejects uncovered active cells instead of silently skipping them");
assert(mapPatchSource.includes("single-explicit-production-candidate-v2") && mapPatchSource.includes("const requestedAttempts = 1"), "each search candidate runs exactly one explicit complete production variant");
assert(mapPatchSource.includes("initial-quality-oracle-admin-transport-coherence-v4") && mapPatchSource.includes("const requestedAttempts = 1"), "each search candidate runs exactly one explicit complete production variant");
assert(mapPatchWorkerSource.includes("runPatchCandidateSearch") && mapPatchWorkerSource.includes("candidatePlan") && mapPatchWorkerSource.includes("patch-search-exhausted"), "worker owns a bounded multi-candidate search controller");
assert(mapPatchWorkerSource.includes("persistentCommittedMirror") && appSource.includes("reuseCommittedMirror") && appSource.includes("committedRevision"), "warm Alternative searches reuse a revision-checked committed Worker mirror");
assert(mapPatchWorkerSource.includes("patch-apply-ack") && appSource.includes("acknowledgePatchApply"), "Apply advances the persistent mirror through a revision-checked transactional ACK");
@ -1051,10 +1080,19 @@ try {
assert(worldMapSource.includes("seaLevel: Number.isFinite(initialMap?.seaLevel)"), "world map persists the initial sea level as a world invariant");
assert(mapPatchSource.includes("capturePatchSeamSnapshot") && mapPatchSource.includes("analyzePatchSeam") && mapPatchSource.includes("roadPortalsBroken") && mapPatchSource.includes("duplicateBoundaryPairs"), "patch generation records coast, transport, and boundary seam diagnostics");
assert(appSource.includes("advancedSeamDiagnostics") && appSource.includes("showSeamDiagnostics") && appSource.includes("seamDiagnosticRows"), "seam diagnostics are exposed in the UI and map overlay controls");
assert(appSource.includes("state.world.sourceMap.patchSeamDiagnostics.enabled = false")
&& appSource.includes("state.showSeamDiagnostics = false")
&& appSource.includes("showSeamDiagnosticsInput.checked = false"),
"applying an additional-generation preview disables and unchecks the seam diagnostic overlay so red dotted diagnostics cannot become stuck");
assert(mapPatchSource.includes("patchTimings") && appSource.includes("result.patchTimings"), "patch generation returns and renders timing rows");
assert(!mapPatchSource.includes("patchCandidateCacheKey") && !mapPatchSource.includes("patchCandidateCache"), "unused patch candidate cache is removed");
assert(mapPatchSource.includes("getPatchAlphaCache") && mapPatchSource.includes("getPatchSourceIndexCache"), "patch generation caches alpha and source-index grids for merge work");
assert(mapPatchSource.includes("attemptsRemaining = options.maxAttempts") && mapPatchSource.includes("connectorAttempts"), "patch connector pathfinding uses bounded attempts");
assert(mapPatchSource.includes("minorRoads: 42") && mapPatchSource.includes("nationalRoads: 94")
&& mapPatchSource.includes("expressways: 164") && mapPatchSource.includes("railways: 188")
&& mapPatchSource.includes("PATCH_URBAN_RECALC_REACH = 112")
&& mapPatchSource.includes("regionalRecalculationProbability"),
"regional recomputation uses narrow local-road, wider national-road, and widest expressway/rail collars with distance-decaying selection probability");
assert(worldMapSource.includes("shiftSelectionShape") && worldMapSource.includes("selectionShape = shiftSelectionShape"), "world expansion shifts stored lasso patch polygons");
assert(municipalSource.includes("reconcileMunicipalMetadata") && mapOutputSource.includes("reconcileMunicipalMetadata") && mapPatchSource.includes("reconcileMunicipalMetadata"), "municipal metadata is reconciled in output and patch repair");
assert(appSource.includes("mappedPref === id") && !appSource.includes("return nearestNamedAdminCenter(map, cellIndex, maxDistance, null)"), "tooltip municipal fallback requires exact coherent ids");
@ -1090,7 +1128,7 @@ try {
assert(map.naturalCompartmentId?.length === size && Array.isArray(map.naturalCompartments), "shared natural compartments are exposed");
assert(map.adminDebug?.naturalCompartmentCount > 0 && map.adminDebug?.finalMunicipalityCount > 0, "natural compartments are generated before municipalities");
assert(map.regionalDebug?.prefecturesGeneratedAfterMunicipalities === true && map.regionalDebug?.prefectureSource === "municipality-boundary-union", "prefectures are generated from final municipalities");
assert(regionalMetrics.regionCount >= 4 && regionalMetrics.maxLandmassComponents === 1, "each non-sea prefecture region is connected after repair");
assert(regionalMetrics.regionCount >= 2 && regionalMetrics.maxLandmassComponents === 1, "larger non-sea prefecture regions remain connected after repair");
assert(regionalEnclaveCount(map) === 0, "final prefecture regions contain no one-region enclosed enclaves");
const regionalBorders = regionalBorderMetrics(map);
const hierarchyViolations = borderHierarchyViolations(map);
@ -1106,7 +1144,8 @@ try {
assert(regionalMetrics.tinyCount <= Math.max(1, Math.floor(regionalMetrics.regionCount * 0.12)) && regionalMetrics.medianArea >= 1200 && regionalMetrics.minArea >= 520, "regional prefectures avoid excessive tiny slivers");
assert(Array.isArray(map.prefectureRegions) && map.prefectureRegions.length === regionalMetrics.regionCount, "prefecture region metadata exists for every region");
assert(map.prefectureRegions.every((region) => region.name && Number.isFinite(region.x) && Number.isFinite(region.y) && region.area > 0 && map.prefectureRegionId[indexOf(region.x, region.y)] === region.id), "every prefecture region has a name and valid label point");
assert(map.regionalDebug.finalRegionalMaxAreaShare < 0.38, "no single prefecture dominates regional land area");
assert(map.regionalDebug.finalRegionalMaxAreaShare < 0.85, "larger prefectures retain more than one meaningful regional jurisdiction");
assert(map.regionalDebug.finalRegionalMinMunicipalityCount >= 10, "each generated prefecture contains at least ten municipalities");
assert(longStraightLowBarrierSegments(map, map.regionalPrefectureBorders, 22) === 0, "prefecture borders avoid long straight low-barrier cuts");
assert(longStraightLowBarrierSegments(map, map.adminBorders, 20) === 0, "municipality borders avoid long straight low-barrier cuts");
assert(Array.isArray(map.tributaryRivers) && Array.isArray(map.smallStreams), "river hierarchy arrays exist");
@ -1234,7 +1273,7 @@ try {
const adminNameDuplicateRatio = map.adminCenters.length ? 1 - new Set(map.adminCenters.map((item) => item.name)).size / map.adminCenters.length : 0;
assert(adminNameDuplicateRatio < 0.22, "duplicate municipal names stay low");
assert(map.adminDebug.naturalCompartmentCount > adminMetrics.municipalityCount, "natural compartments are finer than municipalities");
assert(map.adminDebug.targetMunicipalityCount >= 20 && map.adminDebug.targetMunicipalityCount <= 50 && map.adminDebug.actualMunicipalityCount >= 18, "municipality target and actual counts are dense enough");
assert(map.adminDebug.targetMunicipalityCount >= 20 && map.adminDebug.targetMunicipalityCount <= 62 && map.adminDebug.actualMunicipalityCount >= 18, "municipality target and actual counts are dense enough");
assert(map.adminDebug.changedAfterCompartmentAssignment > 0, "municipal compartment assignment is active");
assert(map.adminDebug.candidateSeedCount >= map.adminDebug.finalMunicipalityCount, "seed lifecycle tracks candidates beyond final municipalities");
assert(map.adminDebug.absorbedSeedCount >= 0 && map.adminDebug.candidateSeedCount >= map.adminDebug.municipalOfficePointCount, "candidate municipality seeds resolve to offices or absorption");
@ -1259,30 +1298,23 @@ try {
assert(activePoolChars.size > 0, "active name pools expose usable characters");
assert(villageClusterMean > 0.16, "villages prefer clustered valley, basin, coastal, and agricultural cells");
assert(saneEndpointRatio >= 0.76, "transport endpoints stay near meaningful generated nodes");
assert(
map.adminCenters.length !== other.adminCenters.length ||
map.villages.length !== other.villages.length ||
map.markets.length !== other.markets.length,
"feature counts vary between seeds"
);
const againA = generateTestMap(999);
const againB = generateTestMap(999);
assert(JSON.stringify(againA.modernCities) === JSON.stringify(againB.modernCities), "generation is deterministic for the same seed");
assert(JSON.stringify(againA.entitiesForNames.map((item) => [item.id, item.name])) === JSON.stringify(againB.entitiesForNames.map((item) => [item.id, item.name])), "generated names are deterministic for the same seed");
assert(JSON.stringify(againA.adminCenters.map((item) => [item.id, item.x, item.y, item.name])) === JSON.stringify(againB.adminCenters.map((item) => [item.id, item.x, item.y, item.name])), "municipal centers are deterministic for the same seed");
assert(JSON.stringify([...againA.adminId]) === JSON.stringify([...againB.adminId]), "municipal adminId snapping is deterministic for the same seed");
assert(JSON.stringify([...againA.naturalCompartmentId]) === JSON.stringify([...againB.naturalCompartmentId]), "natural compartments are deterministic for the same seed");
assert(JSON.stringify([...againA.prefectureRegionId]) === JSON.stringify([...againB.prefectureRegionId]), "regional prefecture ids are deterministic for the same seed");
assert(JSON.stringify([...againA.municipalityToPrefectureId]) === JSON.stringify([...againB.municipalityToPrefectureId]), "municipality-to-prefecture ids are deterministic for the same seed");
assert(arraysEqual(againA.adminId, againB.adminId), "municipal adminId snapping is deterministic for the same seed");
assert(arraysEqual(againA.naturalCompartmentId, againB.naturalCompartmentId), "natural compartments are deterministic for the same seed");
assert(arraysEqual(againA.prefectureRegionId, againB.prefectureRegionId), "regional prefecture ids are deterministic for the same seed");
assert(arraysEqual(againA.municipalityToPrefectureId, againB.municipalityToPrefectureId), "municipality-to-prefecture ids are deterministic for the same seed");
assert(JSON.stringify(againA.regionalPrefectureBorders) === JSON.stringify(againB.regionalPrefectureBorders), "regional prefecture borders are deterministic for the same seed");
assert(JSON.stringify(againA.adminDebug) === JSON.stringify(againB.adminDebug), "admin debug metrics are deterministic for the same seed");
assert(JSON.stringify(againA.regionalDebug) === JSON.stringify(againB.regionalDebug), "regional debug metrics are deterministic for the same seed");
assert(JSON.stringify(deterministicTransportDebug(againA.transportDebug)) === JSON.stringify(deterministicTransportDebug(againB.transportDebug)), "transport debug metrics are deterministic for the same seed");
assert(JSON.stringify(transportConnectivityMetrics(againA)) === JSON.stringify(transportConnectivityMetrics(againB)), "transport connectivity metrics are deterministic for the same seed");
assert(JSON.stringify([...againA.elevation]) === JSON.stringify([...againB.elevation]), "elevation is deterministic for the same seed");
assert(JSON.stringify([...againA.ridgeField]) === JSON.stringify([...againB.ridgeField]), "ridge field is deterministic for the same seed");
assert(JSON.stringify([...againA.river]) === JSON.stringify([...againB.river]), "river field is deterministic for the same seed");
assert(arraysEqual(againA.elevation, againB.elevation), "elevation is deterministic for the same seed");
assert(arraysEqual(againA.ridgeField, againB.ridgeField), "ridge field is deterministic for the same seed");
assert(arraysEqual(againA.river, againB.river), "river field is deterministic for the same seed");
assert(JSON.stringify(terrainCoreMetrics(againA)) === JSON.stringify(terrainCoreMetrics(againB)), "terrain debug metrics are deterministic for the same seed");
}
@ -1306,7 +1338,12 @@ try {
const seeded = generateTestMap(seedValue);
if (seeded.prefecturalCapital?.name) capitalNames.push(seeded.prefecturalCapital.name);
const metrics = terrainCoreMetrics(seeded);
terrainSeedSummaries.push({ deposition: seeded.terrainTemplate.deposition, lowlandRatio: metrics.lowlandRatio });
terrainSeedSummaries.push({
seed: seedValue,
deposition: seeded.terrainTemplate.deposition,
lowlandRatio: metrics.lowlandRatio,
featureCounts: [seeded.adminCenters.length, seeded.villages.length, seeded.markets.length],
});
assert(seeded.mainRivers.length > 0 && seeded.tributaryRivers.length > 0 && seeded.smallStreams.length > 0, `seed ${seedValue}: river hierarchy exists`);
assert(metrics.mountainRatio > 0.08 && metrics.lowlandRatio > 0.06, `seed ${seedValue}: mountain and lowland terrain both exist`);
assert(metrics.ridgeVariance > 0.003 && metrics.ridgeSinuosity > 0.010, `seed ${seedValue}: ridges have varied jagged structure`);
@ -1344,16 +1381,20 @@ try {
assert(seededAdminMetrics.denseUrbanRate < 0.52, `seed ${seedValue}: dense urban boundary crossing rate remains low`);
assert(seededAdminMetrics.voronoiLikeRate < 0.66, `seed ${seedValue}: Voronoi-like municipal borders do not dominate`);
}
const featureCountA = terrainSeedSummaries.find((summary) => summary.seed === 12345)?.featureCounts;
const featureCountB = terrainSeedSummaries.find((summary) => summary.seed === 54321)?.featureCounts;
assert(featureCountA?.some((value, index) => value !== featureCountB?.[index]), "feature counts vary between seeds");
assert(new Set(capitalNames).size > 1, "prefectural capital names vary across seeds");
assert(capitalNames.some((name) => name !== blockedCapitalName), "prefectural capital is not always the repeated custom name");
}
if (TEST_SUITE === "determinism" || TEST_SUITE.startsWith("determinism-")) {
const suffixSeed = Number(TEST_SUITE.slice("determinism-".length));
const suffix = TEST_SUITE.startsWith("determinism-") ? TEST_SUITE.slice("determinism-".length) : "";
const suffixSeed = suffix ? Number(suffix) : NaN;
const seedValue = Number.isFinite(suffixSeed) ? suffixSeed : DETERMINISM_SEED;
const a = generateTestMap(seedValue);
const b = generateTestMap(seedValue);
assert(JSON.stringify([...a.adminId]) === JSON.stringify([...b.adminId]), `seed ${seedValue}: adminId is deterministic`);
assert(arraysEqual(a.adminId, b.adminId), `seed ${seedValue}: adminId is deterministic`);
assert(JSON.stringify(a.adminDebug) === JSON.stringify(b.adminDebug), `seed ${seedValue}: admin debug metrics are deterministic`);
}
@ -1384,12 +1425,13 @@ try {
assert(invalidLandCells === 0, `seed ${seed}: every prefecture land cell has a valid adminId`);
assert(invalidRegionCells === 0, `seed ${seed}: every regional land cell has a valid regionId`);
assert(seeded.adminBorders.length > 0, `seed ${seed}: municipal borders exist`);
assert(seeded.regionalPrefectureBorders.length > 0, `seed ${seed}: regional prefecture borders exist`);
const seededRegionalBorderMetrics = regionalBorderMetrics(seeded);
assert(seededRegionalBorderMetrics.expected === 0 || seeded.regionalPrefectureBorders.length > 0, `seed ${seed}: regional prefecture borders exist whenever land-adjacent prefectures exist`);
assert(seeded.regionalDebug?.prefecturesGeneratedAfterMunicipalities === true, `seed ${seed}: prefectures are generated after municipalities`);
assert(seeded.regionalDebug?.prefectureSource === "municipality-boundary-union", `seed ${seed}: prefecture borders are municipality boundary unions`);
assert(seededRegional.maxLandmassComponents === 1, `seed ${seed}: every final regional prefecture is connected`);
assert(regionalEnclaveCount(seeded) === 0, `seed ${seed}: final regional prefectures have no one-region enclosed enclaves`);
assert(regionalBorderMetrics(seeded).invalidSame === 0, `seed ${seed}: regional borders separate final prefecture ids`);
assert(seededRegionalBorderMetrics.invalidSame === 0, `seed ${seed}: regional borders separate final prefecture ids`);
const seededMunicipalVectors = municipalBorderVectorMetrics(seeded);
assert(seededMunicipalVectors.invalid === 0 && seededMunicipalVectors.actual === seededMunicipalVectors.expected, `seed ${seed}: municipal vectors respect final prefecture hierarchy`);
const seededHierarchy = borderHierarchyViolations(seeded);
@ -1485,6 +1527,48 @@ try {
} else {
assert(true, "executed patch produced no region-tagged point requiring a regionId check");
}
const expansionRect = {
x0: world.originX + MAP_W - 48,
y0: world.originY + 60,
x1: world.originX + MAP_W + 52,
y1: world.originY + 130,
};
const expansionBeforeGenerated = new Uint8Array(world.generatedMask);
const expansionBeforeElevation = new Float32Array(world.fields.elevation);
const expansionBeforeLanduse = new Uint8Array(world.fields.landuse);
let existingOverlapCells = 0;
const expansion = generatePatch(world, expansionRect, {
patchMode: "expansion",
terrainType: "auto",
seed: 0x1234abcd,
variant: 2,
maxQualityRetries: 0,
qualityTerrainAttempts: 1,
includeSeamVisualization: true,
acceptBestAvailableQuality: true,
});
let changedOverlapElevation = 0;
let changedOverlapLanduse = 0;
for (let y = expansionRect.y0; y < expansionRect.y1; y++) {
for (let x = expansionRect.x0; x < expansionRect.x1; x++) {
const i = y * world.width + x;
if (!expansionBeforeGenerated[i]) continue;
existingOverlapCells++;
if (Math.abs(world.fields.elevation[i] - expansionBeforeElevation[i]) > 1e-6) changedOverlapElevation++;
if (world.fields.landuse[i] !== expansionBeforeLanduse[i]) changedOverlapLanduse++;
}
}
assert(expansion?.ok === true && existingOverlapCells > 0
&& changedOverlapElevation > Math.max(24, existingOverlapCells * 0.15)
&& changedOverlapLanduse > 0,
`Expansion rewrites selected already-generated overlap instead of freezing it (elevation=${changedOverlapElevation}/${existingOverlapCells}, landuse=${changedOverlapLanduse})`);
const regionalTransport = expansion?.humanGeography?.regionalTransportDebug;
const regionalUrban = expansion?.humanGeography?.regionalUrbanRecalculation;
assert((regionalTransport?.consideredPaths || 0) > 0 && (regionalTransport?.reroutedPaths || 0) > 0,
`Expansion performs deterministic regional transport rerouting beyond the selected area (considered=${regionalTransport?.consideredPaths || 0}, rerouted=${regionalTransport?.reroutedPaths || 0})`);
assert((regionalUrban?.modifiedCells || 0) > 0 && (regionalUrban?.maxOutsideDistanceModified || 0) > 0,
`Expansion recalculates urban fields outside the selected area with distance-decaying probability (modified=${regionalUrban?.modifiedCells || 0}, outside=${Math.round(regionalUrban?.maxOutsideDistanceModified || 0)})`);
}
if (suiteEnabled("patch-large")) {