Rename screw tubes to screw conveyors (t44)
Player text says Screw conveyor; code uses screw (BP_SCREW, piece id screw, screwIntake...). No compatibility code for old saves or blueprints. Suite outputs match before and after apart from names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+8
-8
@@ -698,21 +698,21 @@ with sync_playwright() as p:
|
||||
check('Wall diagonal defaults back to solid below', ev(f'Array.from({{length:4}}, (_,k)=>blockP[({bby}-12+k)*BW+{bbx}-5+k]).every(c=>c===BP_SLOPE_BL)'))
|
||||
pg.keyboard.press('Control+z'); settle(60)
|
||||
|
||||
# Diagonal tubes use real mouse placement, preview arrows, R and picking.
|
||||
check('select screw tube', select('3', "piece().id === 'tube'"))
|
||||
ev('GAME.pieceDir.tube = 0')
|
||||
# Diagonal screws use real mouse placement, preview arrows, R and picking.
|
||||
check('select screw conveyor', select('3', "piece().id === 'screw'"))
|
||||
ev('GAME.pieceDir.screw = 0')
|
||||
directions = []
|
||||
for _ in range(8):
|
||||
directions.append(ev('GAME.pieceDir.tube'))
|
||||
directions.append(ev('GAME.pieceDir.screw'))
|
||||
pg.keyboard.press('r'); settle(40)
|
||||
check('R cycles eight tube directions', directions == list(range(8)) and ev('GAME.pieceDir.tube') == 0)
|
||||
check('R cycles eight screw directions', directions == list(range(8)) and ev('GAME.pieceDir.screw') == 0)
|
||||
drag(lambda: blk(bbx - 5, bby - 12), lambda: blk(bbx - 2, bby - 9))
|
||||
check('diagonal tube drag places four down-right blocks', ev(f'Array.from({{length:4}}, (_,k)=>[blockP[({bby}-12+k)*BW+{bbx}-5+k],blockV[({bby}-12+k)*BW+{bbx}-5+k]]).every(([c,v])=>c===BP_TUBE && v===5)'))
|
||||
check('diagonal screw drag places four down-right blocks', ev(f'Array.from({{length:4}}, (_,k)=>[blockP[({bby}-12+k)*BW+{bbx}-5+k],blockV[({bby}-12+k)*BW+{bbx}-5+k]]).every(([c,v])=>c===BP_SCREW && v===5)'))
|
||||
pg.mouse.move(*blk(bbx - 4, bby - 11)); settle(60)
|
||||
check('diagonal hover names its direction', ev(f"hoverInfo(({bbx}-4)*4+1,({bby}-11)*4+1).t1.includes('down-right')"))
|
||||
pg.mouse.click(*blk(bbx - 4, bby - 11), button='middle'); settle(60)
|
||||
check('picking a diagonal tube keeps its direction', ev("piece().id === 'tube' && GAME.pieceDir.tube === 5"))
|
||||
pg.screenshot(path=str(out / '04-diagonal-tubes.png'))
|
||||
check('picking a diagonal screw keeps its direction', ev("piece().id === 'screw' && GAME.pieceDir.screw === 5"))
|
||||
pg.screenshot(path=str(out / '04-diagonal-screws.png'))
|
||||
pg.keyboard.press('Control+z'); settle(60)
|
||||
|
||||
# 6. ingot mold: click to place, pour, bars drop below, vacuum them
|
||||
|
||||
+48
-48
@@ -1,9 +1,9 @@
|
||||
// Screw tubes that never carry liquid (it flows through), tube and belt throughput, filters.
|
||||
// Screw conveyors that never carry liquid (it flows through), screw and belt throughput, filters.
|
||||
const { load, suite, flatWorld, rect, ticks, count } = require('./harness');
|
||||
const { run } = load();
|
||||
const { check, done } = suite();
|
||||
function fresh() { flatWorld(run, 320, 200, 120); run('bot.x = 20; bot.y = 40; money = 1e6; globalThis.__got = 0;'); }
|
||||
const tubeLiquid = () => run('(() => { let n = 0; for (let i = 0; i < N; i++) if (part[i] >= P_TU && part[i] <= P_TR && PH[mat[i]] === PH_LIQUID) n++; return n; })()');
|
||||
const screwLiquid = () => run('(() => { let n = 0; for (let i = 0; i < N; i++) if (part[i] >= P_SCREW_U && part[i] <= P_SCREW_R && PH[mat[i]] === PH_LIQUID) n++; return n; })()');
|
||||
// grains that land in the sink region are counted and taken away, so outlets never back up
|
||||
function rate(x0, x1, y0, y1, warm, meas) {
|
||||
const sink = `for (let y=${y0};y<=${y1};y++) for (let x=${x0};x<=${x1};x++) { const i=y*W+x; if (mat[i]===SAND) { mat[i]=EMPTY; __got++; } }`;
|
||||
@@ -13,42 +13,42 @@ function rate(x0, x1, y0, y1, warm, meas) {
|
||||
return run('__got') / (meas / 60);
|
||||
}
|
||||
|
||||
// --- tubes are sealed: no water in, no water carried
|
||||
// --- screws are sealed: no water in, no water carried
|
||||
fresh();
|
||||
run('for (let y = 120; y < 170; y++) for (let x = 100; x < 124; x++) setCell(y*W+x, EMPTY, AMB[y], 0)');
|
||||
rect(run, 100, 150, 123, 169, run('SAND'));
|
||||
rect(run, 100, 130, 123, 149, run('WATER'));
|
||||
run('for (let by = 22; by <= 41; by++) placeBlock(BP_TUBE, 0, 27, by, true); for (let bx = 27; bx <= 34; bx++) placeBlock(BP_TUBE, 1, bx, 21, true);');
|
||||
run('for (let by = 22; by <= 41; by++) placeBlock(BP_SCREW, 0, 27, by, true); for (let bx = 27; bx <= 34; bx++) placeBlock(BP_SCREW, 1, bx, 21, true);');
|
||||
const water0 = count(run, run('WATER'));
|
||||
ticks(run, 900);
|
||||
const wOut = count(run, run('WATER'), 124, 0, 319, 119), sOut = count(run, run('SAND'), 124, 0, 319, 119);
|
||||
check('a lift standing in a flooded pit brings up the sand but not the water', sOut > 150 && wOut === 0, `sand out ${sOut}, water out ${wOut}, water standing in the tubes ${tubeLiquid()}`);
|
||||
check('a lift standing in a flooded pit brings up the sand but not the water', sOut > 150 && wOut === 0, `sand out ${sOut}, water out ${wOut}, water standing in the screws ${screwLiquid()}`);
|
||||
const pitClouds = run('weather.moist.reduce((a, v) => a + v, 0)');
|
||||
const pitSteam = count(run, run('STEAM')); // the pit's floor is hot: some is steam on its way up at any moment
|
||||
check('the pit keeps its water (bar what steams off into the clouds)', water0 - count(run, run('WATER')) - pitSteam - pitClouds <= 6, `${water0} -> ${count(run, run('WATER'))}, ${pitSteam} steam, ${pitClouds.toFixed(0)} in the clouds`);
|
||||
fresh();
|
||||
run('for (let bx = 20; bx < 26; bx++) placeBlock(BP_TUBE, 1, bx, 25, true);');
|
||||
run('for (let bx = 20; bx < 26; bx++) placeBlock(BP_SCREW, 1, bx, 25, true);');
|
||||
rect(run, 84, 60, 95, 79, run('WATER'));
|
||||
ticks(run, 300);
|
||||
check('water poured on a tube runs straight through it', count(run, run('WATER'), 0, 0, 319, 99) === 0 && count(run, run('WATER'), 0, 104, 319, 119) > 200 && tubeLiquid() < 10, `${count(run, run('WATER'), 0, 104, 319, 119)} below, ${tubeLiquid()} still inside`);
|
||||
check('water poured on a screw runs straight through it', count(run, run('WATER'), 0, 0, 319, 99) === 0 && count(run, run('WATER'), 0, 104, 319, 119) > 200 && screwLiquid() < 10, `${count(run, run('WATER'), 0, 104, 319, 119)} below, ${screwLiquid()} still inside`);
|
||||
fresh();
|
||||
// a vertical tube pointing up, its top open to a pool: the water drains down through it, against the screw
|
||||
run('for (let by = 20; by <= 26; by++) placeBlock(BP_TUBE, 0, 25, by, true); for (let y = 60; y < 80; y++) { setCell(y*W+99, STONE, AMB[y], 0); setCell(y*W+104, STONE, AMB[y], 0); }');
|
||||
// a vertical screw pointing up, its top open to a pool: the water drains down through it, against the screw
|
||||
run('for (let by = 20; by <= 26; by++) placeBlock(BP_SCREW, 0, 25, by, true); for (let y = 60; y < 80; y++) { setCell(y*W+99, STONE, AMB[y], 0); setCell(y*W+104, STONE, AMB[y], 0); }');
|
||||
rect(run, 100, 60, 103, 79, run('WATER'));
|
||||
ticks(run, 400);
|
||||
check('water runs down through an upward screw, which never pushes it back', count(run, run('WATER'), 0, 0, 319, 79) === 0, count(run, run('WATER'), 0, 0, 319, 79));
|
||||
|
||||
// Intake itself fills the far lanes first, for either tube axis.
|
||||
// Intake itself fills the far lanes first, for either screw axis.
|
||||
for (const horizontal of [false, true]) {
|
||||
fresh();
|
||||
run(`placeBlock(BP_TUBE, ${horizontal ? 1 : 0}, 25, 20, true);`);
|
||||
run(`placeBlock(BP_SCREW, ${horizontal ? 1 : 0}, 25, 20, true);`);
|
||||
const filled = run(`(() => {
|
||||
const lanes = [];
|
||||
for (let k = 0; k < BS; k++) {
|
||||
tickStamp++;
|
||||
const x = ${horizontal ? 100 : 99}, y = ${horizontal ? 79 : 80};
|
||||
setCell(y*W+x, SAND, AMB[y], 0);
|
||||
tubeIntake(80*W+100, ${horizontal ? 'P_TR, 1, 0' : 'P_TU, 0, -1'});
|
||||
screwIntake(80*W+100, ${horizontal ? 'P_SCREW_R, 1, 0' : 'P_SCREW_U, 0, -1'});
|
||||
for (let lane = 0; lane < BS; lane++) if (mat[(80+${horizontal ? 'lane' : '0'})*W+100+${horizontal ? '0' : 'lane'}] === SAND && !lanes.includes(lane)) lanes.push(lane);
|
||||
}
|
||||
return lanes;
|
||||
@@ -59,7 +59,7 @@ for (const horizontal of [false, true]) {
|
||||
// A finite heap rides into the side of a six-block lift; remove each mouth flyer once.
|
||||
const sideSeed = run('RS');
|
||||
fresh();
|
||||
run('RS = 7; for (let bx = 16; bx < 32; bx++) placeBlock(BP_BELT, 1, bx, 25, true); for (let by = 19; by <= 24; by++) placeBlock(BP_TUBE, 0, 32, by, true);');
|
||||
run('RS = 7; for (let bx = 16; bx < 32; bx++) placeBlock(BP_BELT, 1, bx, 25, true); for (let by = 19; by <= 24; by++) placeBlock(BP_SCREW, 0, 32, by, true);');
|
||||
rect(run, 64, 40, 119, 99, run('SAND'));
|
||||
const sideLift = run(`(() => {
|
||||
let out = 0; const lanes = [0, 0, 0, 0];
|
||||
@@ -79,7 +79,7 @@ check('the side-fed lift conserves its sand, including mouth flyers', sideLift.b
|
||||
// The same belt and heap deliver 2243 into an open drop, the most the lift can be given.
|
||||
check('a side-fed lift carries at least 90% of what its belt brings (it was 65%)', sideLift.out >= Math.floor(0.9 * 2243), sideLift.out);
|
||||
|
||||
// Same belt and finite heap, with a non-blocking sink at the tube side.
|
||||
// Same belt and finite heap, with a non-blocking sink at the screw side.
|
||||
run(`RS = ${sideSeed}`);
|
||||
fresh();
|
||||
run('RS = 7; for (let bx = 16; bx < 32; bx++) placeBlock(BP_BELT, 1, bx, 25, true);');
|
||||
@@ -100,24 +100,24 @@ console.log('Belt delivery to a non-blocking sink, 3000 ticks: ' + beltDelivery)
|
||||
|
||||
// --- throughput
|
||||
fresh();
|
||||
run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_TUBE, 1, bx, 20, true);');
|
||||
run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_SCREW, 1, bx, 20, true);');
|
||||
run('for (let y = 0; y < 80; y++) { setCell(y*W+79, STONE, AMB[y], 0); setCell(y*W+96, STONE, AMB[y], 0); }');
|
||||
rect(run, 80, 0, 95, 79, run('SAND'));
|
||||
ticks(run, 60);
|
||||
const lanes = run('(() => { const s = new Set(); for (let y = 80; y < 84; y++) for (let x = 100; x < 120; x++) if (mat[y*W+x] === SAND) s.add(y); return s.size; })()');
|
||||
check('a sideways tube fed from above (its side) fills more than one row', lanes > 1, lanes);
|
||||
check('a sideways tube fed from a hopper runs full, all four grains wide', lanes === 4, lanes);
|
||||
check('a sideways screw fed from above (its side) fills more than one row', lanes > 1, lanes);
|
||||
check('a sideways screw fed from a hopper runs full, all four grains wide', lanes === 4, lanes);
|
||||
fresh();
|
||||
run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_TUBE, 1, bx, 20, true);');
|
||||
run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_SCREW, 1, bx, 20, true);');
|
||||
run('for (let y = 0; y < 80; y++) { setCell(y*W+79, STONE, AMB[y], 0); setCell(y*W+96, STONE, AMB[y], 0); }');
|
||||
rect(run, 80, 0, 95, 79, run('SAND'));
|
||||
const tr = rate(120, 319, 84, 119, 90, 300);
|
||||
// tubes run at belt pace (every other tick), so a belt under a tube's mouth keeps up with it
|
||||
check('a sideways tube moves around a hundred grains a second', tr > 80 && tr < 150, `${tr.toFixed(0)}/s`);
|
||||
// screws run at belt pace (every other tick), so a belt under a screw's mouth keeps up with it
|
||||
check('a sideways screw moves around a hundred grains a second', tr > 80 && tr < 150, `${tr.toFixed(0)}/s`);
|
||||
fresh();
|
||||
run('for (let y = 120; y < 196; y++) for (let x = 90; x < 130; x++) setCell(y*W+x, EMPTY, AMB[y], 0)');
|
||||
rect(run, 90, 130, 129, 195, run('SAND'));
|
||||
run('for (let by = 20; by <= 37; by++) placeBlock(BP_TUBE, 0, 26, by, true); for (let bx = 26; bx <= 40; bx++) placeBlock(BP_TUBE, 1, bx, 19, true);');
|
||||
run('for (let by = 20; by <= 37; by++) placeBlock(BP_SCREW, 0, 26, by, true); for (let bx = 26; bx <= 40; bx++) placeBlock(BP_SCREW, 1, bx, 19, true);');
|
||||
const lr = rate(164, 319, 80, 119, 400, 300);
|
||||
const liftLanes = run('(() => { const s = new Set(); for (let y = 76; y < 80; y++) for (let x = 120; x < 160; x++) if (mat[y*W+x] === SAND) s.add(y); return s.size; })()');
|
||||
check('a lift turns its corner without squeezing down to one lane', liftLanes === 4 && lr > 30, `${lr.toFixed(0)}/s, ${liftLanes} lanes at the top`);
|
||||
@@ -151,7 +151,7 @@ check('the load rides a belt up to a block deep', depth >= 3, depth);
|
||||
// --- filters
|
||||
fresh();
|
||||
// a filter for copper ore under a mixed pile; everything else rides right and off the end
|
||||
// fed from a walled hopper over its left end, the way a tube or belt would drop a mixed load on it
|
||||
// fed from a walled hopper over its left end, the way a screw or belt would drop a mixed load on it
|
||||
run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_FILTER, 128, bx, 25, true, { m: [CUORE] });');
|
||||
run('for (let y = 30; y < 120; y++) setCell(y*W+79, STONE, AMB[y], 0); for (let y = 30; y < 96; y++) setCell(y*W+88, STONE, AMB[y], 0);');
|
||||
run('for (let y = 40; y < 96; y++) for (let x = 80; x < 88; x++) setCell(y*W+x, (x + y) % 3 ? SAND : CUORE, AMB[y], 0)');
|
||||
@@ -236,7 +236,7 @@ run(`globalThis.__carry = carryColumn;
|
||||
for (const lift of [true, false]) {
|
||||
fresh();
|
||||
run('RS = 7; __shear = 0; for (let bx = 20; bx < 32; bx++) placeBlock(BP_BELT, 1, bx, 25, true);');
|
||||
if (lift) run('for (let by = 15; by <= 24; by++) placeBlock(BP_TUBE, 0, 32, by, true);');
|
||||
if (lift) run('for (let by = 15; by <= 24; by++) placeBlock(BP_SCREW, 0, 32, by, true);');
|
||||
else run('for (let by = 10; by <= 25; by++) placeBlock(BP_WALL, RSTONE, 32, by, true);');
|
||||
rect(run, 88, 94, 119, 99, run('SAND'));
|
||||
rect(run, 88, 70, 119, 93, run('CUORE'));
|
||||
@@ -251,60 +251,60 @@ for (const lift of [true, false]) {
|
||||
}
|
||||
run('carryColumn = __carry;');
|
||||
|
||||
// --- whole bars in screw tubes; position an existing bar inside for mid-ride cases
|
||||
// --- whole bars in screw conveyors; 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) {
|
||||
function barScrew(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);' : ''}`);
|
||||
run(`for (let by = 20; by < 24; by++) placeBlock(BP_SCREW, 0, 25, by, true);
|
||||
${corner ? 'for (let bx = 25; bx < 29; bx++) placeBlock(BP_SCREW, 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);');
|
||||
run('for (let bx=20;bx<25;bx++) placeBlock(BP_BELT,1,bx,25,true); for(let by=21;by<25;by++) placeBlock(BP_SCREW,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++) {
|
||||
run('for(let bx=20;bx<24;bx++) placeBlock(BP_SCREW,1,bx,20,true);'); putBar(80,82);
|
||||
check('a whole bar leaves a right screw', watchBar(100,'ents.length===1 && ents[0].ix>=96'));
|
||||
// steam and smoke drift through screws near furnaces: a bar passes them as it does in the open
|
||||
barScrew();
|
||||
check('steam and smoke in a screw 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'));
|
||||
barScrew(true);
|
||||
check('a bar turns from an up screw into a right screw 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);
|
||||
run('for(let bx=20;bx<24;bx++) placeBlock(BP_SCREW,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();
|
||||
barScrew();
|
||||
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);
|
||||
barScrew(); 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'));
|
||||
check('a bar saved mid-screw 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'));
|
||||
fresh(); run('placeBlock(BP_SCREW,1,25,25,true); spawnBar(BARCU,100,98,0,0);'); ticks(run,100);
|
||||
check('a bar resting on a horizontal screw 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(`placeBlock(BP_SCREW,${dir},25,20,true); spawnBar(BARCU,100,${dir ? 78 : 84},0,${dir ? 0 : -0.4});`);
|
||||
check(dir ? 'a falling bar enters a down screw from above' : 'an upward moving bar enters the bottom of an up screw',watchBar(20,'entScrewAt(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);
|
||||
fresh(); run('for(let bx=20;bx<24;bx++) placeBlock(BP_SCREW,1,bx,20,true); for(let by=21;by<25;by++) placeBlock(BP_SCREW,2,23,by,true);'); putBar(80,82);
|
||||
check('a bar turns from a right screw down into a vertical lane',watchBar(160,'ents[0].iy>=100'));
|
||||
barScrew(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);
|
||||
barScrew(); 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 screw becomes eight molten grains',run('ents.length===0') && count(run,run('MCU'))===8);
|
||||
|
||||
done();
|
||||
|
||||
@@ -42,13 +42,13 @@ for (const material of ['SAND', 'WATER', 'STEAM']) for (const mirror of [false,
|
||||
// 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}]];
|
||||
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);
|
||||
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'));
|
||||
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.
|
||||
|
||||
@@ -6,11 +6,11 @@ const { check, done } = suite();
|
||||
function fresh() { flatWorld(run, 256, 192, 176); run('RS=7; nextPowerBuild=0; money=10000; AMB.fill(20); temp.fill(20); hot.fill(0);'); }
|
||||
function line(v, chain = false) {
|
||||
fresh();
|
||||
const [dx, dy] = run(`TUBE_DIRS[${v}]`), bx = dx > 0 ? 16 : 36, by = dy < 0 ? 32 : 12;
|
||||
run(`for(let k=0;k<10;k++) placeBlock(BP_TUBE,${v},${bx}+k*${dx},${by}+k*${dy},true);`);
|
||||
const [dx, dy] = run(`SCREW_DIRS[${v}]`), bx = dx > 0 ? 16 : 36, by = dy < 0 ? 32 : 12;
|
||||
run(`for(let k=0;k<10;k++) placeBlock(BP_SCREW,${v},${bx}+k*${dx},${by}+k*${dy},true);`);
|
||||
let tail = bx, head = bx + 9 * dx;
|
||||
if (chain) {
|
||||
run(`for(let k=1;k<=3;k++) { placeBlock(BP_TUBE,${dx > 0 ? 1 : 3},${bx}-k*${dx},${by},true); placeBlock(BP_TUBE,${dx > 0 ? 1 : 3},${head}+k*${dx},${by + 9 * dy},true); }`);
|
||||
run(`for(let k=1;k<=3;k++) { placeBlock(BP_SCREW,${dx > 0 ? 1 : 3},${bx}-k*${dx},${by},true); placeBlock(BP_SCREW,${dx > 0 ? 1 : 3},${head}+k*${dx},${by + 9 * dy},true); }`);
|
||||
tail -= 3 * dx; head += 3 * dx;
|
||||
}
|
||||
return { dx, dy, bx, by, tail, head, hy: by + 9 * dy };
|
||||
@@ -21,7 +21,7 @@ function drain(s, steps, feed = false) {
|
||||
for(let k=0;k<${steps};k++) {
|
||||
${feed ? `for(let l=0;l<4;l++) { const i=(${s.by}*4+l)*W+${s.tail}*4+${s.dx > 0 ? 0 : 3}; if(mat[i]===EMPTY) {setCell(i,SAND,20,0);added++;} }` : ''}
|
||||
simTick();
|
||||
for(let i=0;i<N;i++) if(mat[i]===SAND && !IS_TUBE[part[i]]) {
|
||||
for(let i=0;i<N;i++) if(mat[i]===SAND && !IS_SCREW[part[i]]) {
|
||||
const x=i%W; if(${s.dx}>0 ? x>=${(s.head + 1) * 4} : x<${s.head * 4}) out++; else leaks++;
|
||||
setCell(i,EMPTY,20,0);
|
||||
}
|
||||
@@ -37,7 +37,7 @@ for (const v of [4, 5, 6, 7]) {
|
||||
const s = line(v);
|
||||
check(`direction ${v} uses appended part ids and marks power`,run(`part[${s.by * 4}*W+${s.bx * 4}]===${v + 7} && blockV[${s.by}*BW+${s.bx}]===${v} && pcells.size===160`));
|
||||
// Fill every lane, in every block: a corner or side leak is counted before it can fall away.
|
||||
run('for(let i=0;i<N;i++) if(IS_TUBE[part[i]]) setCell(i,SAND,20,0);');
|
||||
run('for(let i=0;i<N;i++) if(IS_SCREW[part[i]]) setCell(i,SAND,20,0);');
|
||||
const r = drain(s, 300);
|
||||
check(`direction ${v} carries sand through ten blocks and out the head`, r.out === 160, JSON.stringify(r));
|
||||
check(`direction ${v} has no corner or side spills and conserves grains`, r.leaks === 0 && r.out+r.inside === 160,JSON.stringify(r));
|
||||
@@ -54,7 +54,7 @@ for (const v of [0, 4, 5]) {
|
||||
let s;
|
||||
if (v) s=line(v);
|
||||
else {
|
||||
fresh();run('for(let by=23;by<=32;by++) placeBlock(BP_TUBE,0,16,by,true);');
|
||||
fresh();run('for(let by=23;by<=32;by++) placeBlock(BP_SCREW,0,16,by,true);');
|
||||
s={dx:0,tail:16,by:32,head:16};
|
||||
}
|
||||
const result = v ? (drain(s,80,true),drain(s,600,true)) : run(`(() => {
|
||||
@@ -107,7 +107,7 @@ for(const v of [4,5,6,7]) for(const chain of [false,true]) {
|
||||
// Vertical straight joins reach the same tails and heads too.
|
||||
for (const v of [4,5,6,7]) {
|
||||
const s=line(v), vv=s.dy<0?0:2;
|
||||
run(`for(let k=1;k<=3;k++){placeBlock(BP_TUBE,${vv},${s.bx},${s.by}-k*${s.dy},true);placeBlock(BP_TUBE,${vv},${s.head},${s.hy}+k*${s.dy},true);}`);
|
||||
run(`for(let k=1;k<=3;k++){placeBlock(BP_SCREW,${vv},${s.bx},${s.by}-k*${s.dy},true);placeBlock(BP_SCREW,${vv},${s.head},${s.hy}+k*${s.dy},true);}`);
|
||||
putBar(s.bx*4,(s.by-3*s.dy)*4+1);
|
||||
const r=run(`(()=>{let out=false;for(let k=0;k<500;k++){simTick();if(${s.dy}<0?ents[0].iy+2<=${(s.hy-3)*4}:ents[0].iy>=${(s.hy+4)*4})out=true;}return {out,overlap:mat.some((m,i)=>occ[i] && PH[m]===PH_SOLID)};})()`);
|
||||
check(`a bar crosses vertical joins at diagonal ${v}`,r.out && !r.overlap,JSON.stringify(r));
|
||||
@@ -140,11 +140,11 @@ const blocked=line(4);
|
||||
putBar(blocked.bx*4,blocked.by*4+1);
|
||||
rect(run,(blocked.head+1)*4,(blocked.hy-1)*4,(blocked.head+2)*4-1,blocked.hy*4-1,run('STONE'));
|
||||
ticks(run,300);
|
||||
check('a blocked diagonal mouth holds its bar without wall overlap',run('ents.length===1 && entTubeAt(ents[0].ix,ents[0].iy) && !mat.some((m,i)=>m===STONE && occ[i])'));
|
||||
check('a blocked diagonal mouth holds its bar without wall overlap',run('ents.length===1 && entScrewAt(ents[0].ix,ents[0].iy) && !mat.some((m,i)=>m===STONE && occ[i])'));
|
||||
rect(run,(blocked.head+1)*4,(blocked.hy-1)*4,(blocked.head+2)*4-1,blocked.hy*4-1,run('EMPTY'));
|
||||
check('the diagonal bar resumes after the mouth clears',watchBars(blocked).out===1);
|
||||
for(const v of [4,5,6,7]) {
|
||||
fresh(); run(`placeBlock(BP_TUBE,${v},20,20,true);`);
|
||||
fresh(); run(`placeBlock(BP_SCREW,${v},20,20,true);`);
|
||||
run('globalThis.saved=serializeWorld();deserializeWorld(saved.meta,saved.buffers);');
|
||||
check(`save/load keeps direction ${v} and power`,run(`blockV[20*BW+20]===${v} && part[80*W+80]===${v+7} && isPow[80*W+80]`));
|
||||
run('globalThis.clip=copyRegion(80,80,83,83);bot.x=100;bot.y=80;undoRecord(\'paste\',()=>pasteGhost(clip,24,20));ghostTick();');
|
||||
@@ -156,42 +156,42 @@ for(const v of [4,5,6,7]) {
|
||||
run('undoRecord(\'cut\',()=>cutRegion(80,80,83,83));undoLast();');
|
||||
check(`cut and undo restore direction ${v}`,run(`blockV[20*BW+20]===${v} && blockIntegrity(20*BW+20)===16`));
|
||||
run('money=100;removeBlock(20,20);');
|
||||
check(`diagonal ${v} refunds the straight tube price`,run('money')===125);
|
||||
check(`diagonal ${v} refunds the straight screw price`,run('money')===125);
|
||||
}
|
||||
// Exercise the actual UI functions without starting a browser or simulating a replacement.
|
||||
run(fs.readFileSync('src/ui/selection.js','utf8'));
|
||||
run(fs.readFileSync('src/ui/actions.js','utf8'));
|
||||
run('globalThis.GAME={pieceDir:{tube:0}};globalThis.hudDirty=false;globalThis.toolId=()=>\'build\';globalThis.piece=()=>PIECE_BY_ID.tube;globalThis.playSfx=()=>{};');
|
||||
const cycle=run('(()=>{const ds=[];for(let k=0;k<8;k++){ds.push(GAME.pieceDir.tube);rotate();}return ds;})()');
|
||||
check('R cycles all eight tube directions then wraps',cycle.join(',')==='0,1,2,3,4,5,6,7' && run('GAME.pieceDir.tube')===0,cycle);
|
||||
run('globalThis.GAME={pieceDir:{screw:0}};globalThis.hudDirty=false;globalThis.toolId=()=>\'build\';globalThis.piece=()=>PIECE_BY_ID.screw;globalThis.playSfx=()=>{};');
|
||||
const cycle=run('(()=>{const ds=[];for(let k=0;k<8;k++){ds.push(GAME.pieceDir.screw);rotate();}return ds;})()');
|
||||
check('R cycles all eight screw directions then wraps',cycle.join(',')==='0,1,2,3,4,5,6,7' && run('GAME.pieceDir.screw')===0,cycle);
|
||||
for(const [dx,dy,v] of [[5,-4,4],[4,5,5],[-5,4,6],[-4,-5,7]]) {
|
||||
check(`a diagonal drag picks direction ${v}`,run(`dragDirFor(PIECE_BY_ID.tube,{bx:20,by:20},{bx:${20+dx},by:${20+dy}})`)===v);
|
||||
check(`a diagonal drag picks direction ${v}`,run(`dragDirFor(PIECE_BY_ID.screw,{bx:20,by:20},{bx:${20+dx},by:${20+dy}})`)===v);
|
||||
check(`direction ${v} lays a corner-touching diagonal run`,run(`blockLine(20,20,${20+dx},${20+dy},true).every(([x,y],k)=>x===20+k*${Math.sign(dx)} && y===20+k*${Math.sign(dy)})`));
|
||||
}
|
||||
// End elbows follow the adjoining straight piece's axis, regardless of flow direction.
|
||||
run(fs.readFileSync('src/render/world.js', 'utf8'));
|
||||
for (const v of [4,5,6,7]) for (let dir=0;dir<4;dir++) for (const code of ['BP_TUBE','BP_BELT','BP_FILTER']) {
|
||||
if (code !== 'BP_TUBE' && !(dir & 1)) continue;
|
||||
for (const v of [4,5,6,7]) for (let dir=0;dir<4;dir++) for (const code of ['BP_SCREW','BP_BELT','BP_FILTER']) {
|
||||
if (code !== 'BP_SCREW' && !(dir & 1)) continue;
|
||||
fresh();
|
||||
const [dx,dy]=run(`TUBE_DIRS[${dir}]`);
|
||||
run(`placeBlock(BP_TUBE,${v},20,20,true);placeBlock(${code},${dir},${20+dx},${20+dy},true);`);
|
||||
const [dx,dy]=run(`SCREW_DIRS[${dir}]`);
|
||||
run(`placeBlock(BP_SCREW,${v},20,20,true);placeBlock(${code},${dir},${20+dx},${20+dy},true);`);
|
||||
const end = dx ? dx>0 : (v===4 || v===6 ? dy<0 : dy>0);
|
||||
check(`diagonal ${v} bends toward ${code} direction ${dir}`,run(`(diagonalTubeJoins(20,20,${v}) >> ${end?3:0} & 7)`)===dir+1);
|
||||
check(`planned ${code} direction ${dir} bends the diagonal ${v} ghost too`,run(`(()=>{removeBlock(${20+dx},${20+dy});const planned=new Map([[${20+dy}*BW+${20+dx},[${code},${dir}]]]);const j=diagonalTubeJoins(20,20,${v},planned);placeBlock(${code},${dir},${20+dx},${20+dy},true);return (j >> ${end?3:0} & 7)===${dir+1};})()`));
|
||||
check(`diagonal ${v} meets ${code} direction ${dir} across its centred edge`,run(`(()=>{const b=20*BW+20,j=diagonalTubeJoins(20,20,${v});return [0.5,1.5,2.5,3.5].every(t=>diagonalTubeShape(${dx ? 82+dx*1.99 : '80+t'},${dy ? 82+dy*1.99 : '80+t'},b,${v},j)<=0);})()`));
|
||||
check(`diagonal ${v} bends toward ${code} direction ${dir}`,run(`(diagonalScrewJoins(20,20,${v}) >> ${end?3:0} & 7)`)===dir+1);
|
||||
check(`planned ${code} direction ${dir} bends the diagonal ${v} ghost too`,run(`(()=>{removeBlock(${20+dx},${20+dy});const planned=new Map([[${20+dy}*BW+${20+dx},[${code},${dir}]]]);const j=diagonalScrewJoins(20,20,${v},planned);placeBlock(${code},${dir},${20+dx},${20+dy},true);return (j >> ${end?3:0} & 7)===${dir+1};})()`));
|
||||
check(`diagonal ${v} meets ${code} direction ${dir} across its centred edge`,run(`(()=>{const b=20*BW+20,j=diagonalScrewJoins(20,20,${v});return [0.5,1.5,2.5,3.5].every(t=>diagonalScrewShape(${dx ? 82+dx*1.99 : '80+t'},${dy ? 82+dy*1.99 : '80+t'},b,${v},j)<=0);})()`));
|
||||
}
|
||||
// The drawing can bridge a joint only in unoccupied air; transport parts stay square.
|
||||
for (const v of [4,5,6,7]) {
|
||||
flatWorld(run,240,192,176);
|
||||
const rising = v===4 || v===6;
|
||||
run(`placeBlock(BP_TUBE,${v},20,20,true);placeBlock(BP_TUBE,${v},21,${rising ? 19 : 21},true);`);
|
||||
run(`placeBlock(BP_SCREW,${v},20,20,true);placeBlock(BP_SCREW,${v},21,${rising ? 19 : 21},true);`);
|
||||
const x=84, y=rising ? 80 : 83;
|
||||
check(`diagonal ${v} draws over the empty corner joint`,run(`diagonalTubeBlock(${x},${y},false)>=0 && part[${y}*W+${x}]===0`));
|
||||
check(`diagonal ${v} draws over the empty corner joint`,run(`diagonalScrewBlock(${x},${y},false)>=0 && part[${y}*W+${x}]===0`));
|
||||
for(const obstacle of ['mat[j]=STONE','mat[j]=SAND','part[j]=P_PIPE','mach[j]=1','occ[j]=1','blockP[(j/W>>2)*BW+(j%W>>2)]=BP_WALL']) {
|
||||
check(`diagonal ${v} leaves ${obstacle} visible`,run(`(()=>{const j=${y}*W+${x};${obstacle};const hidden=diagonalTubeBlock(${x},${y},true);mat[j]=EMPTY;part[j]=0;mach[j]=0;occ[j]=0;blockP[(${y}>>2)*BW+(${x}>>2)]=0;return hidden<0;})()`));
|
||||
check(`diagonal ${v} leaves ${obstacle} visible`,run(`(()=>{const j=${y}*W+${x};${obstacle};const hidden=diagonalScrewBlock(${x},${y},true);mat[j]=EMPTY;part[j]=0;mach[j]=0;occ[j]=0;blockP[(${y}>>2)*BW+(${x}>>2)]=0;return hidden<0;})()`));
|
||||
}
|
||||
check(`diagonal ${v} keeps its continuation uncapped`,run(`(diagonalTubeJoins(20,20,${v}) & 128)===0 && (diagonalTubeJoins(21,${rising ? 19 : 21},${v}) & 64)===0`));
|
||||
check(`diagonal ${v} keeps its continuation uncapped`,run(`(diagonalScrewJoins(20,20,${v}) & 128)===0 && (diagonalScrewJoins(21,${rising ? 19 : 21},${v}) & 64)===0`));
|
||||
run(`removeBlock(21,${rising ? 19 : 21});`);
|
||||
check(`diagonal ${v} has no stray corner after its end`,run(`diagonalTubeBlock(${x+1},${rising ? y-1 : y+1},false)===-1`));
|
||||
check(`diagonal ${v} has no stray corner after its end`,run(`diagonalScrewBlock(${x+1},${rising ? y-1 : y+1},false)===-1`));
|
||||
}
|
||||
done();
|
||||
+4
-4
@@ -21,7 +21,7 @@ run(`
|
||||
placeBlock(BP_WALL, RSTONE, 29, by, true);
|
||||
}
|
||||
placeBlock(BP_BELT, 1, 12, 30, true);
|
||||
placeBlock(BP_TUBE, 0, 14, 30, true);
|
||||
placeBlock(BP_SCREW, 0, 14, 30, true);
|
||||
addMachine('silo', 18, 30, 0);
|
||||
addMold('bit', 24, 30, RSTONE, true);
|
||||
for (let y = 80; y < 88; y++) for (let x = 180; x < 280; x++) setCell(y * W + x, STONE, AMB[y], F_NAT);
|
||||
@@ -37,7 +37,7 @@ for (const mode of ['full', 'fast']) {
|
||||
check(`${mode} ${channel}: natural overhang still shades`, rock < open * 0.95, `${rock} / ${open}`);
|
||||
}
|
||||
// Directly check transmission as daylight can hide an opaque piece in the open.
|
||||
for (const [name, x, y] of [['wall', 32, 120], ['belt', 48, 120], ['tube', 56, 120], ['machine', 72, 120], ['mold', 96, 120]]) {
|
||||
for (const [name, x, y] of [['wall', 32, 120], ['belt', 48, 120], ['screw', 56, 120], ['machine', 72, 120], ['mold', 96, 120]]) {
|
||||
const a = run(`lightAt(R.latt, ${x}, ${y})`);
|
||||
check(`${mode}: ${name} transmits like air`, Math.abs(a - Math.pow(0.978, mode === 'fast' ? 4 : 2)) < 1e-6, a);
|
||||
}
|
||||
@@ -99,8 +99,8 @@ for (const [name, support, depth, gap, expected] of [
|
||||
['natural ledge', 'mat[i] = STONE; flags[i] = F_NAT', 3, '', false],
|
||||
['deep natural heap', 'mat[i] = SAND; flags[i] = F_NAT', 13, '', false],
|
||||
['wall floor', 'mat[i] = STONE', 3, '', true],
|
||||
['tube', 'part[i] = P_TR', 3, '', true],
|
||||
['loaded tube', 'mat[i] = SAND; part[i] = P_TR', 3, '', true],
|
||||
['screw', 'part[i] = P_SCREW_R', 3, '', true],
|
||||
['loaded screw', 'mat[i] = SAND; part[i] = P_SCREW_R', 3, '', true],
|
||||
]) {
|
||||
run(`
|
||||
for (let y = 40; y <= 53; y++) { const i = y * W + 300; mat[i] = SAND; part[i] = 0; flags[i] = 0; }
|
||||
|
||||
+2
-2
@@ -64,10 +64,10 @@ check('missing rack metadata defaults to empty and closed', run('rackState.get(2
|
||||
fresh(3);
|
||||
run('rackPush(rackState.get(20 * BW + 10), BARFE, 3); rackPush(rackState.get(22 * BW + 10), BARCU, 1); interactAt(40, 84); for (let t = 0; t < 20; t++) rackTick();');
|
||||
check('refilling crosses an empty middle block without losing FIFO order', run('ents[0].m === BARCU && rackState.get(22 * BW + 10).n === 3 && rackState.get(20 * BW + 10).n === 0'));
|
||||
for (const delivery of ['belt', 'tube', 'thrown', 'mold']) {
|
||||
for (const delivery of ['belt', 'screw', 'thrown', 'mold']) {
|
||||
fresh();
|
||||
if (delivery === 'belt') run('for (let bx = 6; bx <= 9; bx++) placeBlock(BP_BELT, 1, bx, 20, true); spawnBar(BARCU, 30, 78);');
|
||||
if (delivery === 'tube') run('placeBlock(BP_TUBE, 2, 10, 17, true); placeBlock(BP_TUBE, 2, 10, 18, true); spawnBar(BARCU, 40, 68);');
|
||||
if (delivery === 'screw') run('placeBlock(BP_SCREW, 2, 10, 17, true); placeBlock(BP_SCREW, 2, 10, 18, true); spawnBar(BARCU, 40, 68);');
|
||||
if (delivery === 'thrown') run('spawnBar(BARCU, 34, 66, 0.35, -0.3);');
|
||||
if (delivery === 'mold') run("addMold('ingot', 10, 17, RSTONE, true); for (const i of molds[0].cavs[0].slice(0, 8)) setCell(i, COPPER, 20, F_CAST); checkMolds();");
|
||||
run('for (let t = 0; t < 300; t++) simTick();');
|
||||
|
||||
@@ -62,9 +62,9 @@ check('free end rounds its corners and stops at the end', run(`(() => {
|
||||
blockP[b] = BP_BELT_DOWN;
|
||||
return slopeBeltShape(x + 0.8, y + 0.8, b, 0) < 0 && slopeBeltShape(x - 1, y - 1, b, 0) > 0;
|
||||
})()`));
|
||||
check('flat belts still meet diagonal tube elbows without an end cap', run(`(() => {
|
||||
check('flat belts still meet diagonal screw elbows without an end cap', run(`(() => {
|
||||
blockP[artB - 1] = BP_BELT;
|
||||
blockP[artB] = BP_TUBE; blockV[artB] = 4;
|
||||
blockP[artB] = BP_SCREW; blockV[artB] = 4;
|
||||
const joined = !(flatBeltCaps(artB - 1) & 2);
|
||||
blockP[artB] = BP_BELT_DOWN;
|
||||
return joined;
|
||||
|
||||
@@ -71,14 +71,14 @@ for(const grade of [-1,1]) {
|
||||
check(`grade ${grade} keeps a bar moving forward off an open end`,barLaunch);
|
||||
for(const diagonal of [false,true]) {
|
||||
line(grade);
|
||||
run(`for(let bx=26;bx<32;bx++)removeBlock(bx,${s.outlet});for(let k=0;k<3;k++)placeBlock(BP_TUBE,${diagonal?(grade<0?4:5):1},26+k,${s.by+9*grade+(grade<0?-1:0)}+k*${diagonal?grade:0},true);`);
|
||||
run(`for(let bx=26;bx<32;bx++)removeBlock(bx,${s.outlet});for(let k=0;k<3;k++)placeBlock(BP_SCREW,${diagonal?(grade<0?4:5):1},26+k,${s.by+9*grade+(grade<0?-1:0)}+k*${diagonal?grade:0},true);`);
|
||||
rect(run,40,s.inlet*4-8,55,s.inlet*4-1,run('SAND'));
|
||||
rect(run,39,20,39,s.inlet*4-1,run('STONE'));
|
||||
const r=collect(s,1000,false,116);
|
||||
check(`grade ${grade} unloads grains into a ${diagonal?'diagonal':'straight'} tube`,r.out===128 && r.left===0,JSON.stringify(r));
|
||||
check(`grade ${grade} unloads grains into a ${diagonal?'diagonal':'straight'} screw`,r.out===128 && r.left===0,JSON.stringify(r));
|
||||
run(`spawnBar(BARCU,44,${s.inlet*4-2},0,0);`);
|
||||
ticks(run,600);
|
||||
check(`grade ${grade} unloads a bar into a ${diagonal?'diagonal':'straight'} tube`,run('ents.length===1 && ents[0].ix>=116 && !mat.some((m,i)=>occ[i] && PH[m]===PH_SOLID)'),run('ents.map(e=>[e.ix,e.iy])'));
|
||||
check(`grade ${grade} unloads a bar into a ${diagonal?'diagonal':'straight'} screw`,run('ents.length===1 && ents[0].ix>=116 && !mat.some((m,i)=>occ[i] && PH[m]===PH_SOLID)'),run('ents.map(e=>[e.ix,e.iy])'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -216,16 +216,16 @@ check('blown grains never come back into the bag', run('bag[SAND]') === 0 && cou
|
||||
|
||||
// --- round 6: lifts, falling trees, machines without power, the bot and walls
|
||||
fresh();
|
||||
// a shaft 16 wide, a column of up-tubes from its floor, turned sideways at the top to unload onto the ground
|
||||
// a shaft 16 wide, a column of up-screws from its floor, turned sideways at the top to unload onto the ground
|
||||
run('for (let y = 100; y < 132; y++) for (let x = 120; x < 128; x++) setCell(y*W+x, EMPTY, AMB[y], 0)');
|
||||
run('bot.x = 110; bot.y = 90;');
|
||||
// unload well above the ground: a pile that reaches the outlet would block it
|
||||
run('for (let by = 19; by <= 32; by++) placeBlock(BP_TUBE, 0, 30, by, true); for (let bx = 30; bx <= 35; bx++) placeBlock(BP_TUBE, 1, bx, 18, true);');
|
||||
run('for (let by = 19; by <= 32; by++) placeBlock(BP_SCREW, 0, 30, by, true); for (let bx = 30; bx <= 35; bx++) placeBlock(BP_SCREW, 1, bx, 18, true);');
|
||||
box(120, 116, 127, 131, run('SAND'));
|
||||
const sand0 = count(run, run('SAND'));
|
||||
ticks(run, 1800);
|
||||
const lifted = count(run, run('SAND'), 136, 0, 239, 99);
|
||||
check('a column of upward screw tubes lifts sand out of a shaft, unpowered', lifted > sand0 * 0.8, `${lifted} of ${sand0} grains brought up and out`);
|
||||
check('a column of upward screw conveyors lifts sand out of a shaft, unpowered', lifted > sand0 * 0.8, `${lifted} of ${sand0} grains brought up and out`);
|
||||
fresh();
|
||||
run('for (let bx = 25; bx <= 30; bx++) placeBlock(BP_BELT, 1, bx, 22, true); rebuildPower();');
|
||||
box(101, 84, 104, 87, run('SAND'));
|
||||
|
||||
Reference in New Issue
Block a user