20 lines
4.4 KiB
JavaScript
20 lines
4.4 KiB
JavaScript
const fs=require('node:fs'),vm=require('node:vm'),assert=require('node:assert/strict'),{createHash}=require('node:crypto'),decomp=require('poly-decomp');
|
|
const index=JSON.parse(fs.readFileSync('research/county-map-index.json','utf8')),context={window:{}};vm.runInNewContext(fs.readFileSync('dist/county-index.js','utf8'),context);const catalog=context.window.COUNTY_INDEX;
|
|
assert.equal(index.levels.length,329);assert.equal(catalog.levels.length,329);assert.equal(index.unavailable.length,4);assert.deepEqual(Array.from(catalog.unavailable,l=>l.id).sort(),['441900','442000','460400','620200']);
|
|
const parents=JSON.parse(fs.readFileSync('research/prefecture-map-index.json','utf8')).levels.flatMap(p=>p.regions.filter(r=>r.unitType==='prefecture').map(r=>({...r,province:p.id})));
|
|
assert.deepEqual(index.levels.concat(index.unavailable).map(l=>l.id).sort(),parents.map(l=>l.id).sort());for(const entry of index.levels.concat(index.unavailable)){const parent=parents.find(p=>p.id===entry.id);assert.equal(entry.name,parent.name);assert.equal(entry.province,parent.province);}
|
|
const packages=new Map();for(const meta of catalog.levels){assert.ok(/^\.\/county\/\d{6}\.js$/.test(meta.path));if(!packages.has(meta.path))packages.set(meta.path,[]);packages.get(meta.path).push(meta);}
|
|
assert.deepEqual(fs.readdirSync('dist/county').sort(),Array.from(packages.keys(),p=>p.split('/').pop()).sort());const packedLevels=new Map();for(const [path,metas] of packages){assert.ok(metas.length>=1&&metas.length<=2);assert.equal(new Set(metas.map(m=>m.province)).size,1);metas.sort((a,b)=>a.id.localeCompare(b.id));assert.equal(path,'./county/'+metas[0].id+'.js');const text=fs.readFileSync('dist/'+path.slice(2),'utf8');assert.equal(text,metas.map(m=>fs.readFileSync('src/county/'+m.id+'.js','utf8')).join('\n'));const local=[];vm.runInNewContext(text,{window:{PuzzleAtlas:{register:value=>{assert.ok(!packedLevels.has(value.id));packedLevels.set(value.id,value);local.push(value.id);}}}});assert.deepEqual(local,metas.map(m=>m.id));}
|
|
assert.equal(packedLevels.size,329);let pieces=0,rings=0,holes=0,repairs=0;
|
|
for(const entry of index.levels){
|
|
const text=fs.readFileSync('src/county/'+entry.id+'.js','utf8');assert.equal(createHash('sha256').update(text).digest('hex'),entry.assetSha256);
|
|
const level=packedLevels.get(entry.id),meta=catalog.levels.find(l=>l.id===entry.id);assert.ok(meta);assert.equal(meta.count,entry.regions.length);assert.equal(level.province,entry.province);assert.equal(level.kind,'county');assert.equal(level.regions.length,entry.regions.length);assert.equal(level.id,entry.id);
|
|
const seen=new Set();for(const r of level.regions){const source=entry.regions.find(v=>v.id===r.id);assert.ok(source);assert.ok(!seen.has(r.id));seen.add(r.id);assert.equal(r.name,source.name);assert.equal(r.rings.length,source.rings);assert.equal((r.holes||[]).length,source.holes);repairs+=source.repairedSourcePolygons;
|
|
for(const ring of r.rings.concat(r.holes||[])){assert.ok(ring.length>=3);assert.ok(ring.every(p=>p.length===2&&p.every(Number.isFinite)));assert.ok(decomp.isSimple(ring),r.name+' must have a simple boundary');let area=0;for(let i=0;i<ring.length;i++)area+=ring[i][0]*ring[(i+1)%ring.length][1]-ring[(i+1)%ring.length][0]*ring[i][1];assert.ok(Math.abs(area)>1e-10);}
|
|
if((r.holes&&r.holes.length)||(entry.collisionFallbacks||[]).includes(r.id))assert.ok(r.physics&&r.physics.length);pieces++;rings+=r.rings.length;holes+=(r.holes||[]).length;
|
|
}
|
|
// Every county participates in a connected puzzle, including marked island routes.
|
|
const connected=new Set([0]);for(let i=0;i<level.regions.length;i++)for(const [a,b,type] of level.edges){assert.ok(a>=0&&b>=0&&a<level.regions.length&&b<level.regions.length);assert.ok(type>=0&&type<=2);if(connected.has(a)||connected.has(b)){connected.add(a);connected.add(b);}}assert.equal(connected.size,level.regions.length,level.name+' puzzle must be connected');
|
|
}
|
|
assert.equal(pieces,2728);const report={passed:true,stages:329,countyFiles:packages.size,pieces,rings,holes,repairedSourcePolygons:repairs,exceptions:index.unavailable,scope:'Frozen provider coverage, source names/IDs, topology repairs, static collision holes and all local assets; not current authoritative administrative validation.'};fs.writeFileSync('artifacts/county-map-audit.json',JSON.stringify(report,null,2));console.log('县区关卡核对通过:329关、2728块、'+packages.size+'个县区文件,本地文件/来源索引/父级/边界拓扑/内环碰撞/关卡连通性完整。');
|