Lakes no longer pour water to a guessed lip and repair it afterwards. Each lake lowers the land in the nearest valley with a smoothstep bowl, and water fills it to its spill level (the lower of the highest ground on each side, Priority-Flood in one dimension), so it is flat and contained by construction. Clay beds, no caves or loose pockets close under lakes, trees kept off the shore. Removes the levelling pass, the lower-rim lip and the bank-slope cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cj2RPXdPykKMEz5BXXczsj
77 lines
4.4 KiB
JavaScript
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 (bowl, spill level, clay bed, no caves under lakes) changes these bytes.
|
|
for (const [seed, expected] of [
|
|
[777, '6c75cd15278a0d8c952bfb0b0f7f6185c597bd8f607d112d5970f8bdff4b1f2f'],
|
|
[77, 'c1f3b0ac9b6d0a566e0051fa3f52388fa626ee714c3e18feed235721569ba056'],
|
|
[4242, '63f6d90358f3e53be577a724cc5265a6e54380c431196cd3ed24cfd064285924'],
|
|
]) {
|
|
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();
|