Files
deepgrain-foundry/test/t_corner_seal.js
T
44r0n7andClaude Opus 5.5 80668a4baf Lakes fill only to their lower rim and keep clear of spawn and the dock (t96)
The lake lip was Math.min(surfY), the highest ground (y grows downward), so water was poured
above the lower rim and, once levelled, ran up to the trade dock on 4 of 12 seeds. The lip is
now the lower rim edge minus one, and each lake stays 80 grains from spawn. Seed hashes and
the shallow-ore hash re-baselined (changes only under lake basins); golden fixtures also
freeze surfY/dirtY/AMB from the previous generator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:45:51 -04:00

77 lines
4.4 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();
// t96 re-baseline: only lake generation (rim height, spawn clearance, levelling) changes these material bytes.
for (const [seed, expected] of [
[777, '7d60a0f1a92b42438471c8c15c5cc92576a75a4aa54b8c519c42904acb7088a6'],
[77, '724eb1730ca5fead6aeaa13994beb2e066bb9d65c7db7e0b790857e2d72b7c2a'],
[4242, '9ffdabf1ffe8ab6cd5855ccfe05c31221d1a0517d960bf2e2273018e9dbf03f9'],
]) {
run(`generateWorld(${seed}, 512, 384)`);
check(`seed ${seed} restores corner sealing`,run('CORNER_SEAL')===true);
check(`seed ${seed} matches its thread-independent 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_SCREW,${direction},20,20,true);
var dx=SCREW_DX[SCREW_PARTS[${direction}]],dy=SCREW_DY[SCREW_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);
screwIntake(y*W+x,part[y*W+x],dx,dy);`);
check(`diagonal screw ${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];})()`);
// t88 chunk streams re-baseline this seeded pile to 46 wide and 19 high.
check('the seeded sand pile keeps its re-baselined shape and all grains', pile[0]>=44 && pile[0]<=48 && pile[1]>=18 && pile[1]<=20 && 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();