This commit is contained in:
33333-33333 2026-08-10 13:59:33 +09:00
commit 810ad6f5cb
33 changed files with 10710 additions and 1087 deletions

View file

@ -8,4 +8,11 @@ function adapter(url){const code=`import {parentPort} from 'node:worker_threads'
const initial=generateMap(24681357,{terrainType:'auto',onProgress(){}}); const world=createWorldMap(initial); const edge=world.originX+MAP_W-1,cy=world.originY+Math.floor(MAP_H/2); const rect={x0:edge-40,y0:cy-100,x1:edge-40+280,y1:cy+100};
const worker=adapter(new URL('./mapPatchWorker.js',import.meta.url)); const preview=structuredClone(world); const transfer=Array.from(collectTransferableBuffers(preview)); const t=Date.now(); let last=0;
const message=await new Promise((resolve,reject)=>{const timer=setTimeout(()=>reject(new Error('timeout')),80000);worker.on('message',m=>{if(m?.type==='progress'){if(Date.now()-last>4000){last=Date.now(); console.error(Date.now()-t,m.progress?.label||m.progress?.key);}return;}clearTimeout(timer);resolve(m)});worker.on('error',e=>{clearTimeout(timer);reject(e)});worker.postMessage({id:1,world:preview,rect,options:{patchMode:'expansion',terrainType:'auto',seed:0x4a35b921,variant:1,maxQualityRetries:0,qualityTerrainAttempts:1,acceptBestAvailableQuality:true}},transfer);});
console.log(JSON.stringify({ms:Date.now()-t,outer:message.ok,inner:message.result?.ok,tiled:message.result?.tiledExpansion,tileCount:message.result?.tileCount,seam:message.result?.seamDiagnostics?.status,reason:message.result?.reason||message.error||null},null,2)); await worker.terminate(); process.exit(0);
const summary={ms:Date.now()-t,outer:message.ok,inner:message.result?.ok,tiled:message.result?.tiledExpansion,tileCount:message.result?.tileCount,seam:message.result?.seamDiagnostics?.status,reason:message.result?.reason||message.error||null};
console.log(JSON.stringify(summary,null,2));
assert.equal(summary.outer,true,'worker transport must succeed');
assert.equal(summary.inner,true,`large patch must succeed: ${summary.reason||'unknown failure'}`);
assert.equal(summary.tiled,true,'large selection must use tiled production generation');
assert.ok(summary.tileCount>=2,'large selection must execute multiple production tiles');
assert.ok(summary.ms<60000,`large worker patch exceeded 60 s budget: ${summary.ms} ms`);
await worker.terminate();

View file

@ -1,3 +1,4 @@
import assert from 'node:assert/strict';
import { generateMap } from './mapGenerator.js';
import { createWorldMap } from './worldMap.js';
import { generatePatch } from './mapPatch.js';
@ -8,10 +9,15 @@ const wa=createWorldMap(structuredClone(init)), wb=createWorldMap(structuredClon
const a=rectFor(wa,258,183), b=rectFor(wb,259,184);
const opts={patchMode:'expansion',terrainType:'auto',seed:0x4a35b921,variant:1,maxQualityRetries:0,qualityTerrainAttempts:1,acceptBestAvailableQuality:true};
const ra=generatePatch(wa,a,opts); const rb=generatePatch(wb,b,opts);
if(!ra.ok||!rb.ok){console.log(JSON.stringify({ra:{ok:ra.ok,code:ra.code,reason:ra.reason},rb:{ok:rb.ok,code:rb.code,reason:rb.reason}},null,2));process.exit(2)}
assert.equal(ra.ok,true,`258x183 patch failed: ${ra.reason||ra.code||'unknown'}`);
assert.equal(rb.ok,true,`259x184 patch failed: ${rb.reason||rb.code||'unknown'}`);
const fields=['elevation','sea','plain','agriculture','populationDensity','prefectureRegionId','adminId'];
const out={a:{tiled:!!ra.tiledExpansion,tileCount:ra.tileCount||1},b:{tiled:!!rb.tiledExpansion,tileCount:rb.tileCount||1},common:{}};
for(const name of fields){const A=wa.fields[name],B=wb.fields[name];let n=0,diff=0,sum=0,max=0;for(let y=a.y0;y<a.y1;y++)for(let x=a.x0;x<a.x1;x++){const i=y*wa.width+x;const av=A[i],bv=B[i];const d=Math.abs(Number(av)-Number(bv));n++;if(d>1e-9)diff++;sum+=d;max=Math.max(max,d)}out.common[name]={n,diff,rate:diff/n,meanAbs:sum/n,maxAbs:max};}
function boundarySet(world,field,rect){const f=world.fields[field],s=new Set();for(let y=rect.y0;y<rect.y1-1;y++)for(let x=rect.x0;x<rect.x1-1;x++){const i=y*world.width+x;if(f[i]!==f[i+1]||f[i]!==f[i+world.width])s.add(`${x},${y}`)}return s}
for(const field of ['prefectureRegionId','adminId']){const A=boundarySet(wa,field,a),B=boundarySet(wb,field,a);let xor=0;for(const k of A)if(!B.has(k))xor++;for(const k of B)if(!A.has(k))xor++;out.common[field+'Boundary']={a:A.size,b:B.size,xor,normalized:xor/Math.max(1,A.size+B.size)};}
console.log(JSON.stringify(out,null,2));
assert.ok(out.common.elevation.rate<=0.08,`common elevation changed too much: ${out.common.elevation.rate}`);
assert.ok(out.common.sea.rate<=0.05,`common sea mask changed too much: ${out.common.sea.rate}`);
assert.ok(out.common.prefectureRegionIdBoundary.normalized<=0.15,`prefecture boundary threshold instability: ${out.common.prefectureRegionIdBoundary.normalized}`);
assert.ok(out.common.adminIdBoundary.normalized<=0.15,`municipal boundary threshold instability: ${out.common.adminIdBoundary.normalized}`);