Screw tubes use all their lanes: grains spread sideways inside a tube
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -188,6 +188,12 @@ above, including:
|
||||
- **The silo has pixel art in the pixel look**, drawn grain by grain like the rest of the world;
|
||||
the smooth look keeps the illustrated silo. The owner noticed it looked the same in both.
|
||||
|
||||
- **Screw tubes use all their lanes.** Grains in an up or down tube shift sideways toward the
|
||||
emptier lanes of the block, and grains taken in from the side go to the far lanes first. A
|
||||
block-wide lift fed by a belt from the side now carries about 90% of what the belt brings
|
||||
(before, only the edge lane filled: the owner saw one line on either side). Belts keep their
|
||||
speed.
|
||||
|
||||
## Ideas not built yet
|
||||
|
||||
- **Concrete**, as a stronger wall material from rubble.
|
||||
|
||||
+3
-3
@@ -244,13 +244,13 @@ Pipes, pumps and outlets: each joined line holds one liquid as a count, not as g
|
||||
- functions: sideOf, pipeClosedSide, pipeJoins, rebuildPipes, pipeNetOf, pipeTakeShare, faceCell, pumpDraw, outletPour, pipesTick
|
||||
- variables: PIPE_CAP, PUMP_RATE, OUTLET_RATE, PUMP_MAX_T, SIDE_V, PUMP_V, OUTLET_V, pipeNets, pipeNetAt, pipesDirty
|
||||
|
||||
### src/sim/machine-tick.js (158 lines)
|
||||
### src/sim/machine-tick.js (166 lines)
|
||||
Per-tick machine work: steam engines, fans, belts, filters, screw tubes (machinesTick).
|
||||
- functions: engineIntake, fanBlow, machinesTick
|
||||
|
||||
### src/sim/conveyors.js (114 lines)
|
||||
### src/sim/conveyors.js (127 lines)
|
||||
How belts, filters and screw tubes move what is on or in them. What rests on a belt or filter rides along: the whole stack standing on the carrying surface steps one cell toward d together (belts run front of the line first, so the way ahead is clear). If a powder grain is blocked, the load above it stands still and the belt slips underneath. A filter's own material drops through it instead, unless it is already full.
|
||||
- functions: carryColumn, beltDrain, launchGrain, rebuildFilters, filterCarry, setFilter, filterRun, roomFor, tubeIntake
|
||||
- functions: carryColumn, beltDrain, launchGrain, rebuildFilters, filterCarry, setFilter, filterRun, roomFor, tubeSpread, tubeIntake
|
||||
- variables: CARRY_DEPTH, filterR, filterL, filtersDirty
|
||||
|
||||
### src/sim/blocks.js (132 lines)
|
||||
|
||||
@@ -4,6 +4,27 @@ What agents have done, newest first. Add an entry for every piece of work: the d
|
||||
note or request it was for, what changed, the files touched, the tests run, and anything left
|
||||
open or not verified. Keep each entry short; the git history has the details.
|
||||
|
||||
## 2026-10-23: screw tubes use all their lanes (Claude Code, with Codex)
|
||||
|
||||
Note: "maybe the belts can move faster/carry more than the tubes… the tubes only carry one line of
|
||||
materials on either side." The owner chose to fill every lane; belt speed is unchanged.
|
||||
|
||||
- Measured first (`test/t_conveyors.js`, a belt with a sand heap into the side of a 6-block up
|
||||
tube, 3,000 ticks): 1,459 grains out, lanes `[2940, 0, 0, 10]`; the edge lane was full (one
|
||||
lane carries at most ~1,500). Spreading only at intake changed little (1,486): belts push
|
||||
straight into the edge lane without going through `tubeIntake`.
|
||||
- `src/sim/machine-tick.js`: on each tube beat a grain in a vertical tube may step sideways
|
||||
within its block to a lane with clearly fewer grains ahead (a strict gap stops shuffling).
|
||||
`src/sim/conveyors.js`: `tubeSpread`, shared by side intake (far lanes first) and corners.
|
||||
- After: 2,071 out (92% of the 2,243 the same belt delivers to an open drop), lanes
|
||||
`[1937, 1116, 486, 872]`, sand conserved. Checks: all four lanes, conservation, at least 90%
|
||||
of the belt's delivery, a side-fed sideways tube fills more than one row. All 11 headless
|
||||
suites and `play.py` pass. `docs/DESIGN.md`, `docs/PROJECT_MAP.md`.
|
||||
- Found on the way: Codex's sandbox (`workspace-write`) blocks network sockets, and headless
|
||||
Chrome crashes at startup without one (SIGTRAP core dumps in pairs). Codex runs now pass
|
||||
`-c sandbox_workspace_write.network_access=true`.
|
||||
- Open: nothing.
|
||||
|
||||
## 2026-10-23: the build card describes the build tool, not the piece (Claude Code)
|
||||
|
||||
Note: "instead of the current info box for the build tool showing the current building selected,
|
||||
|
||||
@@ -91,6 +91,18 @@ function filterRun(b) {
|
||||
|
||||
// A cell a grain can be pushed into: empty, or liquid or gas that simply trades places with it
|
||||
function roomFor(j) { const t = mat[j]; return t === EMPTY || PH[t] === PH_GAS || PH[t] === PH_LIQUID; }
|
||||
// Cross one tube block, far lanes first; corners retain their liquid/gas swaps.
|
||||
function tubeSpread(c, x, y, sx, sy, intake = false) {
|
||||
const p = part[c], bx = x >> 2, by = y >> 2;
|
||||
for (;;) {
|
||||
const nx = x + sx, ny = y + sy;
|
||||
if (nx < 0 || ny < 0 || nx >= W || ny >= H || (nx >> 2) !== bx || (ny >> 2) !== by) break;
|
||||
const j = ny * W + nx;
|
||||
if (part[j] !== p || occ[j] || (intake ? mat[j] !== EMPTY || stamp[j] === tickStamp : !roomFor(j))) break;
|
||||
move(c, j, x, y, nx, ny); c = j; x = nx; y = ny;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
// An empty screw cell draws in loose material from behind it or from its sides
|
||||
// (not from the cell it pushes into), so a tube end sitting in a pile feeds itself.
|
||||
function tubeIntake(i, p, dx, dy) {
|
||||
@@ -109,6 +121,7 @@ function tubeIntake(i, p, dx, dy) {
|
||||
if (pj >= P_TU && pj <= P_TR) continue; // other tube cells move their own contents
|
||||
if (pj === P_GRATE || pj === P_FILTER) continue;
|
||||
move(j, i, nx, ny, x, y);
|
||||
if (ox !== -dx || oy !== -dy) tubeSpread(i, x, y, -ox, -oy, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-9
@@ -126,6 +126,22 @@ function machinesTick() {
|
||||
const x = i % W, y = (i / W) | 0;
|
||||
// a sideways screw carries its load along the bottom: grains settle into empty lanes below
|
||||
if (flat && y + 1 < H && part[i + W] === p && roomFor(i + W) && !occ[i + W]) { move(i, i + W, x, y, x, y + 1); continue; }
|
||||
if (!flat) {
|
||||
let own = 0, shifted = false;
|
||||
const end = dy < 0 ? (y & ~3) : (y | 3);
|
||||
for (let yy = y; dy < 0 ? yy >= end : yy <= end; yy += dy) if (PH[mat[yy * W + x]] === PH_POWDER) own++;
|
||||
const side = ((tick >> 1) + i) & 1 ? 1 : -1;
|
||||
for (let c = 0; c < 2; c++) {
|
||||
const sx = c ? -side : side, xx = x + sx, j = i + sx;
|
||||
if ((xx >> 2) !== (x >> 2) || part[j] !== p || !roomFor(j) || mat[j] !== EMPTY || occ[j] || stamp[j] === tickStamp) continue;
|
||||
let load = 0;
|
||||
for (let yy = y; dy < 0 ? yy >= end : yy <= end; yy += dy) if (PH[mat[yy * W + xx]] === PH_POWDER) load++;
|
||||
// Include this grain: a strict gap prevents a sideways step being undone next beat.
|
||||
if (own <= load + 1) continue;
|
||||
move(i, j, x, y, xx, y); stats.moved++; shifted = true; break;
|
||||
}
|
||||
if (shifted) continue;
|
||||
}
|
||||
const nx = x + dx, ny = y + dy;
|
||||
if (nx < 0 || ny < 0 || nx >= W || ny >= H) continue;
|
||||
const j = i + dj;
|
||||
@@ -143,15 +159,7 @@ function machinesTick() {
|
||||
move(i, j, x, y, nx, ny); stats.moved++;
|
||||
if (tube && pj !== p) {
|
||||
// turning a corner: keep going across the new tube's width so every lane fills
|
||||
let c = j, cx = nx, cy = ny;
|
||||
const bxj = nx >> 2, byj = ny >> 2;
|
||||
for (;;) {
|
||||
const ex = cx + dx, ey = cy + dy;
|
||||
if (ex < 0 || ey < 0 || ex >= W || ey >= H || (ex >> 2) !== bxj || (ey >> 2) !== byj) break;
|
||||
const e = ey * W + ex;
|
||||
if (part[e] !== pj || !roomFor(e) || occ[e]) break;
|
||||
move(c, e, cx, cy, ex, ey); c = e; cx = ex; cy = ey;
|
||||
}
|
||||
tubeSpread(j, nx, ny, dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+62
-1
@@ -37,6 +37,66 @@ 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.
|
||||
for (const horizontal of [false, true]) {
|
||||
fresh();
|
||||
run(`placeBlock(BP_TUBE, ${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'});
|
||||
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;
|
||||
})()`);
|
||||
check(horizontal ? 'a right screw takes grains from above into every row' : 'an up screw takes grains from the side into every column', filled.join() === '3,2,1,0', filled);
|
||||
}
|
||||
|
||||
// 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);');
|
||||
rect(run, 64, 40, 119, 99, run('SAND'));
|
||||
const sideLift = run(`(() => {
|
||||
let out = 0; const lanes = [0, 0, 0, 0];
|
||||
const grains = () => mat.reduce((n, m) => n + (m === SAND), 0) + flyers.filter(f => f.m === SAND).length;
|
||||
const before = grains();
|
||||
for (let k = 0; k < 3000; k++) {
|
||||
simTick();
|
||||
for (let x = 128; x < 132; x++) if (mat[88*W+x] === SAND) lanes[x-128]++;
|
||||
for (let f = flyers.length - 1; f >= 0; f--) if (flyers[f].m === SAND && flyers[f].y < 76 && flyers[f].vy < 0) { out++; flyers.splice(f, 1); }
|
||||
}
|
||||
return { out, lanes, before, after: grains() + out };
|
||||
})()`);
|
||||
console.log('Side-fed lift, 3000 ticks: ' + JSON.stringify(sideLift));
|
||||
check('a belt feeding a lift from the side fills all four lanes', sideLift.lanes.every(n => n > 0), sideLift.lanes);
|
||||
check('the side-fed lift conserves its sand, including mouth flyers', sideLift.before === sideLift.after, `${sideLift.before} -> ${sideLift.after}`);
|
||||
// Before side spreading: 1459 grains left this lift in 3000 ticks (one lane, nearly full).
|
||||
// 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.
|
||||
run(`RS = ${sideSeed}`);
|
||||
fresh();
|
||||
run('RS = 7; for (let bx = 16; bx < 32; bx++) placeBlock(BP_BELT, 1, bx, 25, true);');
|
||||
rect(run, 64, 40, 119, 99, run('SAND'));
|
||||
const beltDelivery = run(`(() => {
|
||||
let out = 0;
|
||||
for (let k = 0; k < 3000; k++) {
|
||||
simTick();
|
||||
for (let y = 0; y < H; y++) for (let x = 128; x < W; x++) {
|
||||
const i = y*W+x;
|
||||
if (mat[i] === SAND) { out++; setCell(i, EMPTY, AMB[y], 0); }
|
||||
}
|
||||
for (let f = flyers.length - 1; f >= 0; f--) if (flyers[f].m === SAND && flyers[f].x >= 128) { out++; flyers.splice(f, 1); }
|
||||
}
|
||||
return out;
|
||||
})()`);
|
||||
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);');
|
||||
@@ -44,6 +104,7 @@ run('for (let y = 0; y < 80; y++) { setCell(y*W+79, STONE, AMB[y], 0); setCell(y
|
||||
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);
|
||||
fresh();
|
||||
run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_TUBE, 1, bx, 20, true);');
|
||||
@@ -174,7 +235,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 <= 25; by++) placeBlock(BP_TUBE, 0, 32, by, true);');
|
||||
if (lift) run('for (let by = 15; by <= 24; by++) placeBlock(BP_TUBE, 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'));
|
||||
|
||||
Reference in New Issue
Block a user