Square wall corners hold grains, liquids and gas (t39)

Powder, liquid and gas no longer step diagonally between two solid
cells; flyers and diagonal tube intakes obey the same corner. World
generation settles with the rule off, so seeds make the same worlds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 18:27:10 -04:00
co-authored by Claude Opus 5.5
parent 0cf615eb3d
commit 9bc5a6c3af
8 changed files with 131 additions and 14 deletions
+8 -7
View File
@@ -67,8 +67,9 @@ let SORTP = 0.06;
const SORTP_FUEL = 0.003;
// Liquids step up to LSTEP cells toward a drop in one tick (fewer the thicker they are), and a top-layer grain looks LFILM
// times as far for one (a two-times range strands the far end of a 200-grain-wide ledge).
// World generation settles with both at 1, so every seed keeps making the same world.
// World generation settles with both at 1 and corner sealing off, so seeds keep the same world.
let LSTEP = 8, LFILM = 4;
let CORNER_SEAL = true;
function powder(i, x, y, m) {
if (y >= H - 1) return;
const b = i + W;
@@ -82,9 +83,9 @@ function powder(i, x, y, m) {
if (rnd() < STICK[m]) {
const d = (RS & 2) ? 1 : -1;
let nx = x + d;
if (nx >= 0 && nx < W && enter(m, PH_POWDER, b + d, d, 1)) { move(i, b + d, x, y, nx, y + 1); return; }
if (nx >= 0 && nx < W && (!CORNER_SEAL || PH[mat[b]] !== PH_SOLID || PH[mat[i + d]] !== PH_SOLID) && enter(m, PH_POWDER, b + d, d, 1)) { move(i, b + d, x, y, nx, y + 1); return; }
nx = x - d;
if (nx >= 0 && nx < W && enter(m, PH_POWDER, b - d, -d, 1)) { move(i, b - d, x, y, nx, y + 1); return; }
if (nx >= 0 && nx < W && (!CORNER_SEAL || PH[mat[b]] !== PH_SOLID || PH[mat[i - d]] !== PH_SOLID) && enter(m, PH_POWDER, b - d, -d, 1)) { move(i, b - d, x, y, nx, y + 1); return; }
}
}
function passable(m, ph, j, dx, dy) {
@@ -106,9 +107,9 @@ function liquid(i, x, y, m) {
if (enter(m, PH_LIQUID, b, 0, 1)) { move(i, b, x, y, x, y + 1); return; }
const d = (RS & 2) ? 1 : -1;
let nx = x + d;
if (nx >= 0 && nx < W && enter(m, PH_LIQUID, b + d, d, 1)) { move(i, b + d, x, y, nx, y + 1); return; }
if (nx >= 0 && nx < W && (!CORNER_SEAL || PH[mat[b]] !== PH_SOLID || PH[mat[i + d]] !== PH_SOLID) && enter(m, PH_LIQUID, b + d, d, 1)) { move(i, b + d, x, y, nx, y + 1); return; }
nx = x - d;
if (nx >= 0 && nx < W && enter(m, PH_LIQUID, b - d, -d, 1)) { move(i, b - d, x, y, nx, y + 1); return; }
if (nx >= 0 && nx < W && (!CORNER_SEAL || PH[mat[b]] !== PH_SOLID || PH[mat[i - d]] !== PH_SOLID) && enter(m, PH_LIQUID, b - d, -d, 1)) { move(i, b - d, x, y, nx, y + 1); return; }
}
if (rnd() > FLUID[m]) return;
const sp = SPREAD[m] * (y === 0 || PH[mat[i - W]] !== PH_LIQUID ? LFILM : 1);
@@ -153,9 +154,9 @@ function gas(i, x, y, m) {
if (enter(m, PH_GAS, u, 0, -1)) { move(i, u, x, y, x, y - 1); return; }
const d = (RS & 2) ? 1 : -1;
let nx = x + d;
if (nx >= 0 && nx < W && enter(m, PH_GAS, u + d, d, -1)) { move(i, u + d, x, y, nx, y - 1); return; }
if (nx >= 0 && nx < W && (!CORNER_SEAL || PH[mat[u]] !== PH_SOLID || PH[mat[i + d]] !== PH_SOLID) && enter(m, PH_GAS, u + d, d, -1)) { move(i, u + d, x, y, nx, y - 1); return; }
nx = x - d;
if (nx >= 0 && nx < W && enter(m, PH_GAS, u - d, -d, -1)) { move(i, u - d, x, y, nx, y - 1); return; }
if (nx >= 0 && nx < W && (!CORNER_SEAL || PH[mat[u]] !== PH_SOLID || PH[mat[i - d]] !== PH_SOLID) && enter(m, PH_GAS, u - d, -d, -1)) { move(i, u - d, x, y, nx, y - 1); return; }
if (rnd() < 0.6) {
nx = x + d;
if (nx >= 0 && nx < W && mat[i + d] === EMPTY && passable(m, PH_GAS, i + d, d, 0)) { move(i, i + d, x, y, nx, y); }
+1
View File
@@ -169,6 +169,7 @@ function tubeIntake(i, p, dx, dy) {
const oy = diagonal ? (k === 0 ? 0 : -dy) : k === 0 ? -dy : k === 1 ? dx : -dx;
const nx = x + ox, ny = y + oy;
if (nx < 0 || ny < 0 || nx >= W || ny >= H) continue;
if (ox && oy && PH[mat[y * W + nx]] === PH_SOLID && PH[mat[ny * W + x]] === PH_SOLID) continue;
const j = ny * W + nx, m = mat[j];
if (m === EMPTY || occ[j] || stamp[j] === tickStamp) continue;
if (PH[m] !== PH_POWDER) continue; // the screw only takes loose material; liquids just flow through
+4 -1
View File
@@ -146,7 +146,10 @@ function flyTick() {
if (cx < 0 || cx >= W) { f.vx = 0; continue; } // the world's edge stops it; it drops from there
if (cy >= H) { landFlyer(f, px, H - 1); done = true; break; }
if (cy < 0) { f.x = nx; f.y = ny; continue; }
const open = flyOpen(f.m, cy * W + cx);
// A free flyer must not cut across the point where two solid cells meet.
const sealed = cx !== px && cy !== py && px >= 0 && px < W && py >= 0 && py < H &&
PH[mat[py * W + cx]] === PH_SOLID && PH[mat[cy * W + px]] === PH_SOLID;
const open = !sealed && flyOpen(f.m, cy * W + cx);
if (!f.free) {
// still inside whatever the bot is flying through: pass until the first open cell
// a blocked nozzle spills: the grain backs up into the nearest open space
+2 -2
View File
@@ -245,7 +245,7 @@ function* worldSteps(seedIn, w, h) {
yield [0.75, 'Settling loose ground'];
// settle loose material at the water and sorting speeds the seeds were made with
awake.fill(3);
const lstep = LSTEP, lfilm = LFILM, sortp = SORTP; LSTEP = LFILM = 1; SORTP = 0.003;
const lstep = LSTEP, lfilm = LFILM, sortp = SORTP, cornerSeal = CORNER_SEAL; LSTEP = LFILM = 1; SORTP = 0.003; CORNER_SEAL = false;
for (let k = 0; k < 260; k++) {
tick++; tickStamp = (tickStamp % 255) + 1;
simMove();
@@ -253,7 +253,7 @@ function* worldSteps(seedIn, w, h) {
if (!any) break;
if ((k & 31) === 0) yield [0.75 + 0.24 * k / 260, 'Settling loose ground'];
}
LSTEP = lstep; LFILM = lfilm; SORTP = sortp;
LSTEP = lstep; LFILM = lfilm; SORTP = sortp; CORNER_SEAL = cornerSeal;
yield [0.99, 'Finishing'];
// re-ambient after settling, then start clean
for (let i = 0; i < N; i++) { const m = mat[i]; if (m !== MAGMA) temp[i] = AMB[(i / w) | 0]; life[i] = PH[m] === PH_LIQUID && FRZ[m] ? 255 : 0; }