Saw cuts wood chunks into Planks; stone bricks are now Stone blocks (t4)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 16:50:35 -04:00
co-authored by Claude Opus 5.5
parent f5e5591e2d
commit 08d2d65242
9 changed files with 65 additions and 38 deletions
+32 -11
View File
@@ -31,12 +31,12 @@ check('opening space below drops glass bricks', bars(run('BARGL')) >= 2 && run('
check('glass bricks sell for much more than the sand in them', run('PRICE[BARGL]') >= 8 * run('PRICE[SAND]') * 10, run('PRICE[BARGL]'));
check('glass in a bit mold is too brittle to make a bit', run("METAL_TIER[GLASS]") === 0);
// ---- Saw bricks ride a belt and build walls
// ---- Saw stone blocks ride a belt and build walls
flatWorld(run, 240, 160, 100);
run("money = 9999; bot.x = 60; bot.y = 40; stats.bricks = 0; placeBlock(BP_SAW, 0, 26, 19, true); for (let bx = 24; bx <= 34; bx++) placeBlock(BP_BELT, 1, bx, 21, true);");
run('for (let k = 0; k < 100; k++) { setCell(75 * W + 104, RSTONE, AMB[75], 0); stamp[75 * W + 104] = 0; for (let t = 0; t < 4; t++) { tick++; tickStamp++; procBlocksTick(); } for (let t = 0; t < 10; t++) simTick(); } for (let k = 0; k < 300; k++) simTick();');
check('Saw bricks ride a belt away from under it', run('ents.filter(e => e.m === BARSB && e.ix > 120).length') >= 3, run('JSON.stringify(ents.filter(e => e.m === BARSB).map(e => e.ix))'));
check('stone bricks build cut stone walls, 2 a block', run('WALL_OF[BARSB]') === run('CUTST') && JSON.stringify(run('wallCost(BARSB, 16)')) === JSON.stringify([[run('BARSB'), 2]]));
check('Saw stone blocks ride a belt away from under it', run('ents.filter(e => e.m === BARSB && e.ix > 120).length') >= 3, run('JSON.stringify(ents.filter(e => e.m === BARSB).map(e => e.ix))'));
check('stone blocks build cut stone walls, 2 a block', run('WALL_OF[BARSB]') === run('CUTST') && JSON.stringify(run('wallCost(BARSB, 16)')) === JSON.stringify([[run('BARSB'), 2]]));
check('cut stone stands more heat than rough stone', run('MELT[CUTST]') > run('MELT[STONE]'));
run('bag[BARSB] = 4; bagTotal = 4; placeBlock(BP_WALL, BARSB, 10, 20, false);');
const wallOk = count(run, run('CUTST')) === 16 && run('bag[BARSB]') === 2;
@@ -64,10 +64,10 @@ check('what is in a silo is saved', run(`${silo}.sn`) === held - out1 && run(`${
// ---- block hoppers: feed the top edge, clear the outlet between jobs
function hopper(code, stack = false) {
flatWorld(run, 240, 160, 120);
run(`money = 9999; stats.refined = 0; stats.bricks = 0; placeBlock(${code}, 0, 20, 18, true); ${stack ? 'placeBlock(BP_GRIND, 0, 20, 19, true);' : ''}`);
run(`money = 9999; stats.refined = 0; stats.bricks = 0; stats.planks = 0; placeBlock(${code}, 0, 20, 18, true); ${stack ? 'placeBlock(BP_GRIND, 0, 20, 19, true);' : ''}`);
}
function feedHopper(m, n, bottom = 76) {
run(`for (const e of ents.slice()) removeEnt(e);
function feedHopper(m, n, bottom = 76, clearItems = true) {
run(`${clearItems ? "for (const e of ents.slice()) removeEnt(e);" : ""}
for (let k = 0; k < ${n}; k++) {
for (let y = ${bottom}; y < ${bottom}+2; y++) for (let x = 80; x < 84; x++) setCell(y*W+x, EMPTY, 20, 0);
setCell(71*W+80, ${m}, 60, 0); stamp[71*W+80] = 0;
@@ -75,12 +75,30 @@ function feedHopper(m, n, bottom = 76) {
}`);
}
hopper('BP_SAW'); feedHopper('RSTONE', 8);
check('a Saw cuts eight stone grains into one brick below', run('stats.bricks === 1 && ents[0].iy === 76'));
check('a Saw cuts eight stone grains into one stone block below', run('stats.bricks === 1 && ents[0].iy === 76'));
hopper('BP_SAW'); feedHopper('LWOOD', 8);
check('a Saw cuts eight wood chunks into one plank below', run('stats.planks === 1 && ents.length === 1 && ents[0].m === PLANK && ents[0].iy === 76 && seen[PLANK]'));
check('two planks pay for a wood wall block', JSON.stringify(run('wallCost(PLANK, 16)')) === JSON.stringify([[run('PLANK'), 2]]));
run('bag[PLANK] = 4; bagTotal = 4; bot.x = 40; bot.y = 80; placeBlock(BP_WALL, PLANK, 10, 20, false);');
check('placing a plank wall builds wood and uses two planks', count(run, run('WOOD')) === 16 && run('bag[PLANK] === 2'));
run('removeBlock(10, 20);');
check('removing a plank wall refunds its two planks', run('bag[PLANK] === 4') && count(run, run('WOOD')) === 0);
check('cut stone is named Stone block', run('MD[BARSB].name') === 'Stone block');
hopper('BP_SAW'); feedHopper('RSTONE', 5); feedHopper('LWOOD', 5); feedHopper('RSTONE', 3);
check('mixed feed finishes only the stone block first', run('stats.bricks === 1 && stats.planks === 0 && ents.length === 1 && ents[0].m === BARSB && procState.get(18*BW+20).cut[LWOOD] === 5'));
run('entMark(ents[0], 0); ents[0].ix = 96; ents[0].x = 96; entMark(ents[0], 1);');
feedHopper('LWOOD', 3, 76, false);
check('mixed feed keeps separate wood and stone counts', run('stats.bricks === 1 && stats.planks === 1 && ents.length === 2 && ents.filter(e => e.m === BARSB).length === 1 && ents.filter(e => e.m === PLANK).length === 1'));
feedHopper('CUORE', 1);
check('a Saw passes other grains through at their temperature', run('mat[76*W+81] === CUORE && temp[76*W+81] === 60'));
hopper('BP_SAW');
run('for (let x = 80; x < 84; x++) setCell(76*W+x, STONE, 20, 0); setCell(71*W+80, RSTONE, 20, 0); stamp[71*W+80] = 0; for (let k = 0; k < 8; k++) { tick++; tickStamp++; procBlocksTick(); }');
check('a full outlet stops a Saw before consuming stone', run('mat[71*W+80] === RSTONE && procState.get(18*BW+20).blocked'));
hopper('BP_SAW'); feedHopper('LWOOD', 7);
run('setCell(77*W+80, STONE, 20, 0); setCell(71*W+80, LWOOD, 20, 0); stamp[71*W+80] = 0; for (let k = 0; k < 8; k++) { tick++; tickStamp++; procBlocksTick(); }');
check('a Saw holds the eighth wood chunk until a whole plank fits', run('mat[71*W+80] === LWOOD && procState.get(18*BW+20).cut[LWOOD] === 7 && procState.get(18*BW+20).blocked && stats.planks === 0'));
hopper('BP_SAW', true); feedHopper('LWOOD', 8, 80);
check('a plank passes the Grinder and drops below the stack', run('stats.planks === 1 && stats.refined === 0 && ents[0].m === PLANK && ents[0].iy === 80'));
hopper('BP_GRIND'); feedHopper('RSTONE', 200);
check('a Grinder counts every ground grain and reduces rubble', run('stats.refined === 200') && count(run, run('RSTONE')) === 0);
hopper('BP_GRIND');
@@ -94,7 +112,7 @@ run('removeBlock(20, 18); removeBlock(20, 19);');
check('removing hoppers refunds costs, clears cells and state', run('money === 10149 && mat[72*W+80] === EMPTY && mat[76*W+80] === EMPTY && procState.size === 0'));
hopper('BP_SAW'); feedHopper('RSTONE', 3);
run('(() => { const s = serializeWorld(); deserializeWorld(s.meta, s.buffers); })(); procBlocksTick();');
check('loading rebuilds processors and drops partial brick state', run('procBlocks.length === 1 && procState.get(18*BW+20).cut === 0 && SAVE_VERSION === 2'));
check('loading rebuilds processors and drops partial brick state', run('procBlocks.length === 1 && procState.get(18*BW+20).cut[RSTONE] === 0 && procState.get(18*BW+20).cut[LWOOD] === 0 && SAVE_VERSION === 2'));
// ---- removed machines in old saves are refunded and leave no hull or machine ids
flatWorld(run, 240, 160, 100);
@@ -111,12 +129,15 @@ for (const [type, refund] of [['refiner', 120], ['cutter', 90], ['unknown', 0]])
check(`loading a saved ${type} skips it and refunds exactly $${refund}`, result[0] && result[1] === refund && result[2], result);
}
// ---- builders' orders: bulk rubble, gravel and stone bricks at a few times a tiny price
// ---- builders' orders: bulk rubble, gravel and stone blocks at a few times a tiny price
flatWorld(run, 240, 160, 100);
run('seen[RSTONE] = 1; seen[GRAVEL] = 1; seen[BARSB] = 1; orders = []; ordersDone = 0;');
run('seen[PLANK] = 0; seen[RSTONE] = 1; seen[GRAVEL] = 1; seen[BARSB] = 1; orders = []; ordersDone = 0;');
const os = JSON.parse(run('JSON.stringify(Array.from({ length: 40 }, () => makeBuildersOrder()))'));
const okOrders = os.every(o => o && o.who === 'Builders' && [run('RSTONE'), run('GRAVEL'), run('SAND'), run('BARSB')].includes(o.m) && o.pay > 0 && o.pay < 400);
check("builders post orders for rubble, gravel and stone bricks that pay a little", okOrders, os.slice(0, 4).map(o => `${o.qty} of ${o.m} for $${o.pay}`).join(', '));
check("builders post orders for rubble, gravel and stone blocks that pay a little", okOrders, os.slice(0, 4).map(o => `${o.qty} of ${o.m} for $${o.pay}`).join(', '));
run('seen[PLANK] = 1; orders = BUILD_GOODS.filter(([m]) => m !== PLANK).map(([m]) => ({ m }));');
check('builders buy planks in lots of 20 to 80', run('(() => { const o = makeBuildersOrder(); return o.m === PLANK && o.qty >= 20 && o.qty <= 80 && o.pay > 0 && Math.abs(PRICE[PLANK] - 2.4) < 0.00001; })()'));
run('orders = [];');
const regular = JSON.parse(run('JSON.stringify(Array.from({ length: 60 }, () => { const o = makeOrder(); return o && !o.who ? o.m : 0; }))'));
check('ordinary buyers never ask for thousands of grains of rubble', !regular.includes(run('RSTONE')) && !regular.includes(run('GRAVEL')));