Files
shanhe-puzzle/tests/growth.test.cjs
T
codex-bot 4845826deb
Shanhe source and offline package checks / verify (pull_request) Failing after 5s
feat: sync Shanhe 2.2 source and add remote package verification
2026-11-01 17:13:46 +08:00

30 lines
4.9 KiB
JavaScript

const test=require('node:test'),assert=require('node:assert/strict'),vm=require('node:vm'),fs=require('node:fs'),M=require('matter-js');
const {Game,GROWTH_MS}=require('../src/game.js');const box={window:{}};vm.runInNewContext(fs.readFileSync('src/maps.js','utf8'),box);const national=box.window.MAP_DATA.levels[0];
const index=code=>national.regions.findIndex(r=>r.id===code),bounds=g=>M.Bounds.create(g.body.vertices),width=g=>bounds(g).max.x-bounds(g).min.x;
function game(level=national){const g=new Game(level,420,620,4,()=>{},()=>.5,1);g.status='playing';g.queue=[];return g;}
function close(a,b){assert.ok(Math.abs(a-b)<1e-7,`${a} != ${b}`);}
test('四个大省单块再小8%,普通省区及细分关卡不受影响,描画和碰撞同尺寸',()=>{
const g=game();for(const code of ['650000','150000','540000','630000']){const id=index(code),small=g.createGroup([id],{tx:20,ty:150,angle:.4}),base=g.createGroup([id],{tx:20,ty:150,angle:.4},1);close(small.factor,.92);close(width(small)/width(base),.92);const points=national.regions[id].rings.flat().map(p=>g.worldPoint(small,p)),visual=M.Bounds.create(points),physical=bounds(small);const baseVisual=M.Bounds.create(national.regions[id].rings.flat().map(p=>g.worldPoint(base,p)));close((visual.max.x-visual.min.x)/(baseVisual.max.x-baseVisual.min.x),.92);assert.ok(Math.abs(visual.min.x-physical.min.x)<2&&Math.abs(visual.max.y-physical.max.y)<2,'原有凸分解近似误差应小于2px');}
close(g.singleFactor(index('360000')),1);const local=game(box.window.MAP_DATA.levels.find(l=>l.id==='650000'));assert.ok(local.level.regions.every((_,i)=>local.singleFactor(i)===1));local.destroy();g.destroy();
});
test('独立缩放不改变拼合位置和当前角度,普通省与大省仍按原位置正确合并',()=>{
const g=game(),pose={tx:10,ty:150,angle:.35},a=g.createGroup([index('650000')],pose),b=g.createGroup([index('620000')],pose);close(g.pose(a).tx,pose.tx);close(g.pose(a).ty,pose.ty);close(g.pose(b).tx,pose.tx);assert.ok(g.tryMerges());assert.equal(g.groups.length,1);close(g.groups[0].body.angle,.35);g.destroy();
});
test('两块大省合并后320ms逐渐恢复统一比例并略长大,几何与物理同时增长',()=>{
const g=game(),a=g.createGroup([index('650000')],{tx:20,ty:150,angle:0}),b=g.createGroup([index('630000')],{tx:20,ty:150,angle:0});assert.ok(g.tryMerges());const merged=g.groups[0],start=width(merged),centre={...merged.body.position};close(merged.factor,.92);assert.ok(merged.growth);let previous=merged.factor;
for(let i=0;i<4;i++){g.clock+=GROWTH_MS/4;g.stepGrowth();assert.ok(merged.factor>=previous);close(merged.body.position.x,centre.x);close(merged.body.position.y,centre.y);previous=merged.factor;}
close(merged.factor,1.02);close(width(merged)/start,1.02/.92);assert.equal(merged.growth,null);g.destroy();
});
test('增长按组合块数计算而不是反复倍乘,最大仅放大6%',()=>{
const g=game();close(g.groupFactor([0,1]),1.02);close(g.groupFactor([0,1,2,3]),1.04);close(g.groupFactor(Array.from({length:8},(_,i)=>i)),1.06);close(g.groupFactor(national.regions.map((_,i)=>i)),1.06);g.destroy();
});
test('暂停冻结长大过程,窗口与全局缩放不把中间状态跳到最终尺寸',()=>{
const g=game();g.createGroup([index('650000')],{tx:20,ty:150,angle:.2});g.createGroup([index('630000')],{tx:20,ty:150,angle:.2});g.tryMerges();let p=g.groups[0];g.clock+=80;g.stepGrowth();const partial=p.factor;assert.ok(partial>.92&&partial<1.02);g.status='paused';const clock=g.clock;g.tick(100);close(g.clock,clock);close(p.factor,partial);g.resize(360,580);p=g.groups[0];close(p.factor,partial);assert.ok(p.growth);close(p.body.angle,.2);g.rescale(g.scale*.9);p=g.groups[0];close(p.factor,partial);assert.ok(p.growth);g.clock+=GROWTH_MS;g.stepGrowth();close(p.factor,1.02);g.destroy();
});
test('预备块缩小但不进入物理世界,松手后保持准确落点且不穿侧墙',()=>{
const g=game();g.status='ready';const id=index('650000');g.queue=[id];g.start();const pending=g.pendingView();const full=g.createGroup([id],{tx:0,ty:0,angle:0},1);M.Composite.remove(g.engine.world,full.body);g.groups=[];g.movePending(-999,g.pending.serial);const x=g.pending.x;g.holdPending(g.pending.serial);g.releasePending(g.pending.serial);const body=g.groups[0];assert.ok(Math.abs(g.worldPoint(body,national.regions[id].center).x-x)<.2,'侧墙修正只容许凸分解的亚像素误差');assert.ok(bounds(body).min.x>=7.99);close(bounds(body).min.y,8);assert.ok(pending.scale>0);g.destroy();
});
test('通关立即保留最终成长尺寸,成绩截图不会停在缩小的中间帧',()=>{
const square=(id,x)=>({id:String(id),name:'块'+id,center:[x+50,50],rings:[[[x,0],[x+100,0],[x+100,100],[x,100]]]});const g=game({id:'100000',name:'两块',width:1000,height:1000,regions:[square(0,0),square(1,100)],edges:[[0,1,0]]});g.createGroup([0],{tx:50,ty:200,angle:.3});g.createGroup([1],{tx:50,ty:200,angle:.3});g.tryMerges();assert.equal(g.status,'won');close(g.groups[0].factor,1.02);assert.equal(g.groups[0].growth,null);g.destroy();
});