Drill: visible dust; pulses push loose material back and open a channel (t92)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 18:32:31 -04:00
co-authored by Claude Opus 5.5
parent 729378770a
commit dadf62a28d
8 changed files with 151 additions and 22 deletions
+41
View File
@@ -206,6 +206,35 @@ check('115 grains of rubble prevent basalt breaking during sustained drilling',
const through118 = rubbleShield(118);
check('118 grains extinguish the ray before it can even shake basalt', through118 === 0 && !run('digp.some(d => d > 0)'), `${through118} basalt opened`);
// A 35-grain-deep pile against basalt: compare with gravity alone, tracking only rubble.
function channelPile(fire) {
flatWorld(run, 240, 160, 110);
return run(`(() => {
RS=12345; tick=0; lastPulseTick=-99; drillPulses=[]; player.bitTier=3;
bot.x=45; bot.y=88; EV.length=0;
for(let y=85;y<110;y++) {
for(let x=55;x<90;x++) setCell(y*W+x,RSTONE,20,0);
for(let x=90;x<150;x++) setCell(y*W+x,BASALT,20,F_NAT);
}
const measure=()=>{let n=0,sx=0,empty=0;for(let i=0;i<N;i++) if(mat[i]===RSTONE){n++;sx+=i%W;}
for(let x=55;x<90;x++) if(mat[90*W+x]===EMPTY) empty++;
return {n,cx:sx/n,empty};};
const before=measure(); let dust=0;
for(let t=0;t<480;t++) {
${fire ? 'toolPulse(130,90,1,0);' : ''} simTick();
dust+=EV.filter(e=>e.t==='debris' && e.dust).length; EV.length=0;
}
return {before,after:measure(),dust};
})()`);
}
const channelRest=channelPile(false), channel=channelPile(true);
check('medium pulses open most of the centre ray through a 35-grain pile within 480 ticks',
channel.after.empty>=28 && channel.after.empty>channelRest.after.empty+15,JSON.stringify(channel));
check('the pile streams back toward the bot rather than just bouncing on its surface',
channel.after.cx<channel.before.cx-5 && channel.after.cx<channelRest.after.cx-5,JSON.stringify(channelRest));
check('channel drilling retains every rubble grain',channel.after.n===channel.before.n,`${channel.before.n}->${channel.after.n}`);
check('shaking loose grains emits dust puffs',channel.dust>0,`${channel.dust} puffs`);
// Absorption over a fixed distance, isolated from gravity, kicks and cell breakage.
function transmission(material, distance, step, tune = 0) {
flatWorld(run, 240, 160, 150);
@@ -246,6 +275,18 @@ check('pulses fluidise a settled rubble pile so its height drops',
check('shaking conserves the pile material',fluidPile.grains===fluidPile.left,`${fluidPile.grains}->${fluidPile.left}`);
check('repeating a seeded pile gives the same result',JSON.stringify(shakenPile(true))===JSON.stringify(fluidPile));
// A wave must still shake loose grains far behind its advancing front, throughout the path.
flatWorld(run,240,160,150);
const travelled=run(`(() => {
RS=12345; bot.x=0;bot.y=0;tickStamp=1;EV.length=0;
for(const x of [25,45,65]) setCell(80*W+x,RSTONE,20,0);
const p={ox:19.5,oy:80.5,ang:0,w:1,jit:0.5,n:1,damp:PULSE_W[1].damp,
en:new Float32Array([1.4]),s:new Float32Array([60]),r:60,range:100,tune:0};
drillPulses=[p];pulseTick();
return [25,45,65].filter(x=>mat[80*W+x-1]===RSTONE).length;
})()`);
check('the travelled ray path pushes interior grains one cell toward the bot',travelled>=2,`${travelled}/3 botward hops (probabilistic kicks)`);
// One artificially long ray crosses many exposed grains in a single tick, exercising the caps.
flatWorld(run,240,160,150);
const kicks = run(`(() => {
+13
View File
@@ -22,6 +22,19 @@ with launch(viewport={'width':960,'height':600}) as (_,pg,url):
}""")
assert startup['warm']>=0
print('PASS both looks submit behind loading screen; first visible frame ms',startup)
dust=pg.evaluate("""()=>{
const particles=SET.particles,fogOn=SET.fog;SET.particles=true;SET.fog=false;
R.particles=[];EV.length=0;emit({t:'debris',x:bot.x,y:bot.y,m:RSTONE,dust:true});drainEvents();
const spawned=R.particles.length===1 && R.particles[0].dust===true;
R.ctx.clearRect(0,0,R.canvas.width,R.canvas.height);drawParticles();
const data=R.ctx.getImageData(0,0,R.canvas.width,R.canvas.height).data;
let visible=0,soft=0;for(let i=3;i<data.length;i+=4)if(data[i]){visible++;if(data[i]<100)soft++;}
for(let n=0;n<36;n++)drawParticles();const expired=R.particles.length===0;
SET.particles=false;spawnDebris(bot.x,bot.y,RSTONE,true);const disabled=R.particles.length===0;
SET.particles=particles;SET.fog=fogOn;return {spawned,visible,soft,expired,disabled};
}""")
assert dust['spawned'] and dust['visible']>4 and dust['soft']==dust['visible'] and dust['expired'] and dust['disabled'],dust
print('PASS pulse dust events draw a visible soft puff, expire and respect particles setting',dust)
pg.add_script_tag(path=str(root/'tools/perf-scenes.js'))
pg.evaluate("async()=>{perfScene('factory',1024,640);await startThreads();simPool.force=true;SET.look='pixel';SET.zoom=2;resizeRender();snapCamera();drawWorld();}")
result=pg.evaluate('''async () => {