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

70 lines
4.7 KiB
JavaScript

import assert from 'node:assert/strict';
import { generateMap } from './mapGenerator.js';
import { createWorldMap } from './worldMap.js';
import { generatePatch } from './mapPatch.js';
import { MAP_W, MAP_H, worldIndexOf } from './mapUtils.js';
const baseSeeds = (process.env.BASE_SEEDS || '24681357,13579246').split(',').map(Number).filter(Number.isFinite);
const requestedDirections = new Set((process.env.DIRECTIONS || 'right,left,down,up').split(',').map(s => s.trim()));
const results = [];
const quiet = process.env.QUIET === '1';
function probeAxis(world, direction, baseOriginX, baseOriginY) {
const edgeX = baseOriginX + MAP_W - 1;
const edgeY = baseOriginY + MAP_H - 1;
if (direction === 'right') return { horizontal: false, fixed: edgeX, start: baseOriginY + Math.floor(MAP_H*.22), end: baseOriginY + Math.floor(MAP_H*.78), oldOffset: 0, newOffset: 1 };
if (direction === 'left') return { horizontal: false, fixed: baseOriginX, start: baseOriginY + Math.floor(MAP_H*.22), end: baseOriginY + Math.floor(MAP_H*.78), oldOffset: 0, newOffset: -1 };
if (direction === 'down') return { horizontal: true, fixed: edgeY, start: baseOriginX + Math.floor(MAP_W*.22), end: baseOriginX + Math.floor(MAP_W*.78), oldOffset: 0, newOffset: 1 };
return { horizontal: true, fixed: baseOriginY, start: baseOriginX + Math.floor(MAP_W*.22), end: baseOriginX + Math.floor(MAP_W*.78), oldOffset: 0, newOffset: -1 };
}
function makeRect(direction, ox, oy) {
const edgeX = ox + MAP_W - 1, edgeY = oy + MAP_H - 1;
const x0 = ox + Math.floor(MAP_W*.22), x1 = ox + Math.floor(MAP_W*.78);
const y0 = oy + Math.floor(MAP_H*.22), y1 = oy + Math.floor(MAP_H*.78);
if (direction === 'right') return { x0: edgeX - 33, y0, x1: edgeX + 93, y1 };
if (direction === 'left') return { x0: ox - 93, y0, x1: ox + 33, y1 };
if (direction === 'down') return { x0, y0: edgeY - 33, x1, y1: edgeY + 93 };
return { x0, y0: oy - 93, x1, y1: oy + 33 };
}
function measureFrontier(world, probe) {
let pairs = 0, maxJump = 0, sum = 0;
for (let t = probe.start; t < probe.end; t++) {
let ax, ay, bx, by;
if (!probe.horizontal) {
ax = probe.fixed + probe.oldOffset; ay = t;
bx = probe.fixed + probe.newOffset; by = t;
} else {
ax = t; ay = probe.fixed + probe.oldOffset;
bx = t; by = probe.fixed + probe.newOffset;
}
const a = worldIndexOf(world,ax,ay), b = worldIndexOf(world,bx,by);
if (a < 0 || b < 0 || world.fields.sea[a] || world.fields.sea[b]) continue;
const jump = Math.abs(world.fields.elevation[a] - world.fields.elevation[b]);
pairs++; sum += jump; maxJump = Math.max(maxJump,jump);
}
return { pairs, maxJump, meanJump: pairs ? sum/pairs : 0 };
}
for (const baseSeed of baseSeeds) {
const initial = generateMap(baseSeed, { terrainType:'auto', onProgress(){} });
for (const direction of ['right','left','down','up']) {
if (!requestedDirections.has(direction)) continue;
const world = createWorldMap(structuredClone(initial));
const ox = world.originX, oy = world.originY;
const rect = makeRect(direction,ox,oy);
const patchSeed = (0x13579bdf ^ baseSeed ^ ({right:0x11,left:0x22,down:0x33,up:0x44}[direction])) >>> 0;
const result = generatePatch(world,rect,{patchMode:'expansion',terrainType:'auto',seed:patchSeed,variant:1,maxQualityRetries:0,qualityTerrainAttempts:1,acceptBestAvailableQuality:true});
assert.equal(result.ok,true,`${baseSeed}/${direction}: ${result.reason || result.code}`);
const probe = measureFrontier(world,probeAxis(world,direction,ox,oy));
assert.ok(probe.pairs >= 12,`${baseSeed}/${direction}: insufficient land frontier pairs (${probe.pairs})`);
assert.ok(probe.maxJump <= 0.075,`${baseSeed}/${direction}: frontier max jump ${probe.maxJump}`);
assert.equal(result.seamDiagnostics?.hardPass,true,`${baseSeed}/${direction}: seam gate ${JSON.stringify(result.seamDiagnostics?.gateReasons || [])}`);
assert.ok((result.seamDiagnostics?.maxEstablishedFrontierElevationJump || 0) <= 0.075,`${baseSeed}/${direction}: diagnostic frontier jump ${result.seamDiagnostics?.maxEstablishedFrontierElevationJump}`);
const row={baseSeed,direction,patchSeed,pairs:probe.pairs,maxJump:+probe.maxJump.toFixed(4),meanJump:+probe.meanJump.toFixed(4),diagnosticMax:+(result.seamDiagnostics?.maxEstablishedFrontierElevationJump||0).toFixed(4),seamStatus:result.seamDiagnostics?.status,seamReasons:result.seamDiagnostics?.gateReasons||[],adjusted:result.humanGeography?.establishedFrontierElevationCellsAdjusted||0,gradientAdjusted:result.humanGeography?.establishedFrontierGradientCellsAdjusted||0};
results.push(row);
if (!quiet) console.log(JSON.stringify(row));
}
}
console.log(JSON.stringify({ok:true,cases:results.length,maxObserved:Math.max(...results.map(r=>r.maxJump)),results},null,2));