// 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>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>2)' : '8+(x>>2)'})*4; if(${gas ? '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=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();