Screw tubes carry bars, one whole bar at a time

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 00:09:33 -04:00
co-authored by Claude Opus 5.5
parent 9340bc132b
commit 9df161046d
10 changed files with 201 additions and 33 deletions
+56
View File
@@ -250,4 +250,60 @@ for (const lift of [true, false]) {
}
run('carryColumn = __carry;');
// --- whole bars in screw tubes; position an existing bar inside for mid-ride cases
function putBar(x, y) { run(`{ spawnBar(BARCU, 20, 40, 0, 0); const e=ents[0]; entMark(e,0); e.x=e.ix=${x}; e.y=e.iy=${y}; entMark(e,1); }`); }
function barTube(corner = false) {
fresh();
run(`for (let by = 20; by < 24; by++) placeBlock(BP_TUBE, 0, 25, by, true);
${corner ? 'for (let bx = 25; bx < 29; bx++) placeBlock(BP_TUBE, 1, bx, 19, true);' : ''}`);
putBar(100,94);
}
function watchBar(n, condition) { return run(`(() => { for (let k=0;k<${n};k++) { simTick(); if (${condition}) return true; } return false; })()`); }
fresh();
run('for (let bx=20;bx<25;bx++) placeBlock(BP_BELT,1,bx,25,true); for(let by=21;by<25;by++) placeBlock(BP_TUBE,0,25,by,true); spawnBar(BARCU,84,98,0,0);');
check('a belt feeds a whole copper bar into a lift and out its top', watchBar(200, 'ents.length===1 && ents[0].iy+BAR_H<=84'), run('JSON.stringify(ents)'));
fresh();
run('for(let bx=20;bx<24;bx++) placeBlock(BP_TUBE,1,bx,20,true);'); putBar(80,82);
check('a whole bar leaves a right tube', watchBar(100,'ents.length===1 && ents[0].ix>=96'));
// steam and smoke drift through tubes near furnaces: a bar passes them as it does in the open
barTube();
check('steam and smoke in a tube do not stop a bar', run(`(() => { for (let k = 0; k < 120; k++) {
for (let y = 80; y < 94; y++) for (let x = 100; x < 104; x++) if (mat[y*W+x] === EMPTY && !occ[y*W+x]) setCell(y*W+x, (x + y) & 1 ? STEAM : SMOKE, 120, 0);
simTick(); if (ents.length === 1 && ents[0].iy + BAR_H <= 80) return true; } return false; })()`), run('JSON.stringify(ents)'));
barTube(true);
check('a bar turns from an up tube into a right tube and leaves', watchBar(200,'ents.length===1 && ents[0].ix>=116'));
fresh();
run('for(let bx=20;bx<24;bx++) placeBlock(BP_TUBE,1,bx,20,true);'); putBar(80,82);
rect(run,96,80,99,83,run('STONE'));
ticks(run,100);
check('a blocked mouth holds the whole bar inside without wall overlap',run('ents.length===1 && ents[0].ix===92 && !mat.some((m,i)=>m===STONE && occ[i])'),run('JSON.stringify(ents)'));
rect(run,96,80,99,83,run('EMPTY'));
check('the bar leaves when the mouth wall is removed',watchBar(80,'ents[0].ix>=96'));
barTube();
rect(run,100,88,103,89,run('SAND'));
const shared = run(`(() => { let overlap=false, rose=false; const n=mat.reduce((n,m)=>n+(m===SAND),0); for(let k=0;k<160;k++) { simTick(); if(mat.some((m,i)=>m===SAND && occ[i])) overlap=true; if(ents[0].iy<90) rose=true; } return { overlap, rose, n, after:mat.reduce((n,m)=>n+(m===SAND),0)+flyers.filter(f=>f.m===SAND).length }; })()`);
check('grains and a moving bar share a lift without overlap or lost sand',!shared.overlap && shared.rose && shared.n===shared.after,JSON.stringify(shared));
barTube(); ticks(run,8);
run('globalThis.__barSave=serializeWorld(); deserializeWorld(__barSave.meta,__barSave.buffers);');
check('a bar saved mid-tube continues out of its mouth',watchBar(100,'ents.length===1 && ents[0].iy+BAR_H<=80'));
fresh();
run('for(let bx=20;bx<24;bx++) placeBlock(BP_BELT,1,bx,25,true); placeBlock(BP_WALL,RSTONE,24,25,true); placeBlock(BP_WALL,RSTONE,24,24,true); spawnBar(BARCU,80,98,0,0);');
ticks(run,100);
check('a normal belt still carries a bar and stops at its blocked end',run('ents.length===1 && ents[0].ix===92 && ents[0].iy===98'));
fresh(); run('placeBlock(BP_TUBE,1,25,25,true); spawnBar(BARCU,100,98,0,0);'); ticks(run,100);
check('a bar resting on a horizontal tube stays on top',run('ents[0].ix===100 && ents[0].iy===98'));
for (const dir of [0, 2]) {
fresh(); run(`placeBlock(BP_TUBE,${dir},25,20,true); spawnBar(BARCU,100,${dir ? 78 : 84},0,${dir ? 0 : -0.4});`);
check(dir ? 'a falling bar enters a down tube from above' : 'an upward moving bar enters the bottom of an up tube',watchBar(20,'entTubeAt(ents[0].ix,ents[0].iy)!==0'));
}
fresh(); run('for(let bx=20;bx<24;bx++) placeBlock(BP_TUBE,1,bx,20,true); for(let by=21;by<25;by++) placeBlock(BP_TUBE,2,23,by,true);'); putBar(80,82);
check('a bar turns from a right tube down into a vertical lane',watchBar(160,'ents[0].iy>=100'));
barTube(true); rect(run,100,76,103,79,run('STONE')); ticks(run,100);
check('a blocked corner waits without overlapping stone',run('ents[0].iy===80 && !mat.some((m,i)=>m===STONE && occ[i])'));
rect(run,100,76,103,79,run('EMPTY'));
check('a blocked corner resumes when cleared',watchBar(120,'ents[0].ix>=116'));
barTube(); run('ents[0].T=1200; for(let y=94;y<96;y++) for(let x=100;x<104;x++) temp[y*W+x]=1200; entTick();');
check('a bar melting inside a tube becomes eight molten grains',run('ents.length===0') && count(run,run('MCU'))===8);
done();