map/archive/generated-history/VALIDATE_LARGE_SELECTION_STABILITY.mjs
2026-08-08 17:41:30 +09:00

115 lines
4.8 KiB
JavaScript

import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { Worker } from 'node:worker_threads';
import { generateMap } from './mapGenerator.js';
import { createWorldMap } from './worldMap.js';
import { generatePatch } from './mapPatch.js';
import { collectTransferableBuffers } from './transferUtils.js';
const BASE_SEED = 24681357;
const PATCH_SEED = 0x4a35b921;
function centeredRect(world, width, height) {
return {
x0: Math.floor((world.width - width) / 2),
y0: Math.floor((world.height - height) / 2),
x1: Math.floor((world.width - width) / 2) + width,
y1: Math.floor((world.height - height) / 2) + height,
};
}
function workerAdapter(url) {
const code = `import { parentPort } from 'node:worker_threads';\n`
+ `globalThis.self = { onmessage: null, postMessage(message, transfer) { parentPort.postMessage(message, transfer); } };\n`
+ `await import(${JSON.stringify(url.href)});\n`
+ `parentPort.on('message', data => self.onmessage?.({ data }));\n`;
return new Worker(new URL(`data:text/javascript,${encodeURIComponent(code)}`), { type: 'module', execArgv: [] });
}
async function runDirect(width, height) {
const initial = generateMap(BASE_SEED, { terrainType: 'auto', onProgress() {} });
const world = createWorldMap(initial);
const rect = centeredRect(world, width, height);
const startedAt = Date.now();
const result = generatePatch(world, rect, {
patchMode: 'expansion', terrainType: 'auto', seed: PATCH_SEED, variant: 1,
maxQualityRetries: 0, qualityTerrainAttempts: 1, acceptBestAvailableQuality: true,
onProgress() {},
});
const row = {
mode: 'direct', size: `${width}x${height}`, ms: Date.now() - startedAt,
ok: result?.ok === true, code: result?.code || null,
tileCount: result?.tileCount || 1,
seam: result?.seamDiagnostics?.status || null,
hardPass: result?.seamDiagnostics?.hardPass ?? null,
};
assert.equal(row.ok, true, `${row.size}: ${result?.reason || result?.code}`);
assert.equal(row.hardPass, true, `${row.size}: seam hard gate failed`);
return row;
}
async function runWorker(width, height) {
const initial = generateMap(BASE_SEED, { terrainType: 'auto', onProgress() {} });
const world = createWorldMap(initial);
const rect = centeredRect(world, width, height);
const preview = structuredClone(world);
const transfer = Array.from(collectTransferableBuffers(preview));
const worker = workerAdapter(new URL('./mapPatchWorker.js', import.meta.url));
const startedAt = Date.now();
const message = await new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error(`${width}x${height}: worker timeout`)), 120000);
worker.on('message', message => {
if (message?.type === 'progress') return;
clearTimeout(timer);
resolve(message);
});
worker.on('error', error => { clearTimeout(timer); reject(error); });
worker.postMessage({
id: 1, world: preview, rect,
options: {
patchMode: 'expansion', terrainType: 'auto', seed: PATCH_SEED, variant: 1,
maxQualityRetries: 0, qualityTerrainAttempts: 1, acceptBestAvailableQuality: true,
},
}, transfer);
});
const row = {
mode: 'worker', size: `${width}x${height}`, ms: Date.now() - startedAt,
outerOk: message?.ok === true, ok: message?.result?.ok === true,
code: message?.result?.code || null, tileCount: message?.result?.tileCount || 1,
seam: message?.result?.seamDiagnostics?.status || null,
hardPass: message?.result?.seamDiagnostics?.hardPass ?? null,
};
worker.unref();
void worker.terminate();
assert.equal(row.outerOk, true, `${row.size}: worker outer failure ${message?.error || ''}`);
assert.equal(row.ok, true, `${row.size}: ${message?.result?.reason || message?.result?.code}`);
assert.equal(row.hardPass, true, `${row.size}: seam hard gate failed`);
return row;
}
const args = process.argv.slice(2);
if (args[0] === '--direct') {
console.log(JSON.stringify(await runDirect(Number(args[1]), Number(args[2]))));
process.exit(0);
}
if (args[0] === '--worker') {
console.log(JSON.stringify(await runWorker(Number(args[1]), Number(args[2]))));
process.exit(0);
}
const specs = [
['--direct', 300, 339],
['--direct', 500, 350],
['--direct', 600, 400],
['--worker', 600, 400],
];
const results = [];
for (const spec of specs) {
const child = spawnSync(process.execPath, [new URL(import.meta.url).pathname, ...spec.map(String)], {
cwd: process.cwd(), encoding: 'utf8', timeout: 130000, maxBuffer: 4 * 1024 * 1024,
});
assert.equal(child.status, 0, `${spec.slice(1).join('x')} ${spec[0]} failed:\n${child.stderr || child.stdout}`);
const lines = child.stdout.trim().split(/\r?\n/).filter(Boolean);
results.push(JSON.parse(lines.at(-1)));
}
console.log(JSON.stringify({ ok: true, results }, null, 2));