Files
deepgrain-foundry/test/t_corner_seal.js
T
44r0n7andClaude Opus 5.5 9bc5a6c3af Square wall corners hold grains, liquids and gas (t39)
Powder, liquid and gas no longer step diagonally between two solid
cells; flyers and diagonal tube intakes obey the same corner. World
generation settles with the rule off, so seeds make the same worlds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 18:27:10 -04:00

76 lines
4.3 KiB
JavaScript

// Corner-touching square walls seal loose grains without changing free piles or worldgen.
const { load, suite, flatWorld, rect, count, ticks } = require('./harness');
const { createHash } = require('node:crypto');
const { run } = load();
const { check, done } = suite();
for (const [seed, expected] of [
[777, '040644fb50c891911383ef0206ea988677d7ec447a3e6637cefa99364e765861'],
[77, '2e6af816af54c8de4fe9c7188607f7af503ef844b2087245779bcf39e8330bdb'],
[4242, 'f09270f4ac5da9347bc277c9959e31ee797313b4a994598b4647dcf852532782'],
]) {
run(`generateWorld(${seed}, 512, 384)`);
check(`seed ${seed} restores corner sealing`,run('CORNER_SEAL')===true);
check(`seed ${seed} keeps its pre-seal material hash`, createHash('sha256').update(run('mat')).digest('hex') === expected);
}
for (const material of ['SAND', 'WATER', 'STEAM']) for (const mirror of [false, true]) {
flatWorld(run, 160, 256, 224);
// Span the whole world so escaping an end cannot be confused with crossing a corner.
run(`RS=12345; AMB.fill(20); temp.fill(20); weather.skyY=-1000;
for(let bx=0;bx<40;bx++) placeBlock(BP_WALL,RSTONE,bx,${mirror ? '47-bx' : '8+bx'},true);`);
const gas = material === 'STEAM';
rect(run, 70, gas ? 166 : 20, 89, gas ? 170 : 24, run(material));
if (gas) {
// Isolate gas movement from cooling/condensation, including chunk sleeping.
run(`temp.fill(120); var gasLeaks=0; for(let k=0;k<500;k++){tick++;tickStamp=tickStamp%255+1;simMove();
for(let y=0;y<H;y++)for(let x=0;x<W;x++)if(mat[y*W+x]===STEAM && y<(${mirror ? '47-(x>>2)' : '8+(x>>2)'})*4) gasLeaks++;}`);
} else ticks(run, 500);
const result = run(`(()=>{
let leaked=0,held=0,x0=W,x1=0,y0=H,y1=0;
for(let y=0;y<H;y++)for(let x=0;x<W;x++)if(mat[y*W+x]===${material}) {
const top=(${mirror ? '47-(x>>2)' : '8+(x>>2)'})*4;
if(${gas ? 'y<top' : 'y>=top+4'}) leaked++; else held++;
x0=Math.min(x0,x);x1=Math.max(x1,x);y0=Math.min(y0,y);y1=Math.max(y1,y);
}
return {leaked,held,width:x1-x0+1,height:y1-y0+1};
})()`);
check(`${material} stays ${gas ? 'under' : 'above'} ${mirror ? 'rising' : 'descending'} square stairs`, result.leaked === 0 && result.held === 100 && (!gas || run('gasLeaks')===0), JSON.stringify(result)+(gas ? ` crossings ${run('gasLeaks')}` : ''));
if (!gas) check(`${material} piles and slides along the steps`, result.height > 1 && count(run,run(material),70,0,89,223)<100, JSON.stringify(result));
}
// A screw mouth must not draw sand through two external wall cells either.
for (const direction of [4,5,6,7]) {
flatWorld(run,160,160,140);
run(`placeBlock(BP_TUBE,${direction},20,20,true);
var dx=TDX[TUBE_PARTS[${direction}]],dy=TDY[TUBE_PARTS[${direction}]];
var x=dx>0?80:83,y=dy>0?80:83;
setCell((y-dy)*W+x-dx,SAND,20,0);
setCell((y-dy)*W+x,STONE,20,0);setCell(y*W+x-dx,STONE,20,0);
tubeIntake(y*W+x,part[y*W+x],dx,dy);`);
check(`diagonal tube ${direction} cannot pull through a solid corner`,run('mat[(y-dy)*W+x-dx]===SAND'));
}
// A free flyer can take the same diagonal shortcut between wall cells.
flatWorld(run, 160, 160, 140);
run(`setCell(79*W+80,STONE,20,0);setCell(80*W+79,STONE,20,0);
flyers=[{x:79.5,y:79.5,vx:1,vy:1,age:0,m:SAND,T:20,free:true,run:0}];flyTick();`);
check('a flying grain cannot cross a sealed corner', run('flyers.length===0 && mat[79*W+79]===SAND'));
flatWorld(run, 240, 160, 120);
run('RS=12345;');
rect(run, 110, 40, 129, 59, run('SAND'));
ticks(run, 600);
const pile = run(`(()=>{let x0=W,x1=0,y0=H,y1=0;
for(let y=0;y<H;y++)for(let x=0;x<W;x++)if(mat[y*W+x]===SAND){x0=Math.min(x0,x);x1=Math.max(x1,x);y0=Math.min(y0,y);y1=Math.max(y1,y);}
return [x1-x0+1,y1-y0+1];})()`);
// Before the fix: 57 wide, 17 high. Small bounds allow random-state differences from prior scenes.
check('open-ground sand still forms the same size pile', pile[0]>=55 && pile[0]<=59 && pile[1]>=16 && pile[1]<=18 && count(run,run('SAND'))===400, pile);
const sealedPile = createHash('sha256').update(run('mat')).digest('hex'), sealedRS=run('RS');
flatWorld(run,240,160,120);
run('RS=12345; CORNER_SEAL=false;');
rect(run,110,40,129,59,run('SAND'));ticks(run,600);
check('open pile and random sequence exactly match unsealed movement', createHash('sha256').update(run('mat')).digest('hex')===sealedPile && run('RS')===sealedRS);
run('CORNER_SEAL=true;');
done();