Lake banks slope down to the water instead of ending in a cliff (t96)

Digging basins to the lower rim left a 9-18 grain vertical cliff on each lake's high side
(owner screenshots, seed 766733). From each edge inward the ground now falls at most 0.7 grains
a column and is never raised above the natural surface. New no-cliff check in t_lakes.js;
world hashes re-baselined (shallow ore: 2 cells changed, on a lake bank).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-29 20:00:48 -04:00
co-authored by Claude Opus 5.5
parent 80668a4baf
commit e3e01a84b1
6 changed files with 37 additions and 11 deletions
+4
View File
@@ -607,6 +607,10 @@ above, including:
and each lake's half-width is capped so its edge stays 80 grains from spawn (and the dock). and each lake's half-width is capped so its edge stays 80 grains from spawn (and the dock).
Lakes are smaller and lower: about 180-250 grains wide instead of 250-330, 5-15 grains Lakes are smaller and lower: about 180-250 grains wide instead of 250-330, 5-15 grains
lower. The random draws keep their order. lower. The random draws keep their order.
Dug to the lower rim, the high side ended in a vertical cliff as tall as the gap between the
rims (15-18 grains on seeds 766733, 7, 42; owner screenshots). From each edge inward the
ground may now fall at most 0.7 grains a column and is never raised above the natural
surface, so the high side is a sloped bank down to the water.
## Ideas not built yet ## Ideas not built yet
+6
View File
@@ -15,6 +15,12 @@ open or not verified. Keep each entry short; the git history has the details.
414 of 28,291 cells changed, all under the two new lake basins). t_lakes.py width threshold 414 of 28,291 cells changed, all under the two new lake basins). t_lakes.py width threshold
300 -> 150. Golden fixtures now also freeze surfY/dirtY/AMB from the previous generator. 300 -> 150. Golden fixtures now also freeze surfY/dirtY/AMB from the previous generator.
- Full `run_all.py --browser`: see the commit; t_smooth.py is the known flake (t99). - Full `run_all.py --browser`: see the commit; t_smooth.py is the known flake (t99).
- Follow-up (owner screenshots, seeds 766733/12446/23565): the lower-rim lip left a 9-18 grain
cliff on each lake's high side. Banks now slope (at most 0.7 grains a column inward from each
edge, never above natural ground); max step beside lakes is 1 on all checked seeds. New
t_lakes.js check "lake banks slope with no cliff" fails on the cliff version for 6 of 7 seeds.
Remaining 17-18 grain steps are at the trade dock's own foundation edge (seed 12446 had it
before this change too); not changed.
## 2026-10-29: t96 round 2 — level lakes during generation (Codex) ## 2026-10-29: t96 round 2 — level lakes during generation (Codex)
+14 -3
View File
@@ -74,12 +74,23 @@ function* worldSteps(seedIn, w, h) {
if (f.k !== 'lake') continue; if (f.k !== 'lake') continue;
const lw = Math.min(Math.round(90 + rng() * 40), Math.abs(f.x - spawnX) - 80), deep = 22 + rng() * 8; const lw = Math.min(Math.round(90 + rng() * 40), Math.abs(f.x - spawnX) - 80), deep = 22 + rng() * 8;
const lip = Math.max(surfY[Math.max(0, f.x - lw)], surfY[Math.min(w - 1, f.x + lw)]) - 1; const lip = Math.max(surfY[Math.max(0, f.x - lw)], surfY[Math.min(w - 1, f.x + lw)]) - 1;
for (let x = f.x - lw; x <= f.x + lw; x++) { const x0 = Math.max(0, f.x - lw), x1 = Math.min(w - 1, f.x + lw), nat = surfY.slice(x0, x1 + 1);
if (x < 0 || x >= w) continue; for (let x = x0; x <= x1; x++) {
const u = (x - f.x) / lw, depth = Math.max(0, 1 - u * u) * deep; const u = (x - f.x) / lw, depth = Math.max(0, 1 - u * u) * deep;
if (depth > 0) { surfY[x] = Math.max(surfY[x], Math.round(lip + depth)); dirtY[x] = Math.max(dirtY[x], surfY[x] + 8); } if (depth > 0) surfY[x] = Math.max(surfY[x], Math.round(lip + depth));
lakeTop[x] = lip + 2; lakeTop[x] = lip + 2;
} }
// Dug to the lower rim, the high side would end in a cliff as tall as the gap between the
// rims: from each edge inward the ground may fall at most 0.7 grains a column, never above nature.
for (const d of [1, -1]) {
let prev = surfY[d > 0 ? Math.max(0, x0 - 1) : Math.min(w - 1, x1 + 1)];
for (let x = d > 0 ? x0 : x1; x >= x0 && x <= x1; x += d) {
prev = Math.min(prev + 0.7, surfY[x]);
surfY[x] = Math.max(nat[x - x0], Math.round(prev));
prev = Math.max(prev, surfY[x]);
}
}
for (let x = x0; x <= x1; x++) dirtY[x] = Math.max(dirtY[x], surfY[x] + 8);
for (let x = Math.max(0, f.x - lw - 6); x <= Math.min(w - 1, f.x + lw + 6); x++) noTree[x] = 1; for (let x = Math.max(0, f.x - lw - 6); x <= Math.min(w - 1, f.x + lw + 6); x++) noTree[x] = 1;
} }
for (const f of feats) { for (const f of feats) {
+3 -3
View File
@@ -6,9 +6,9 @@ const { check, done } = suite();
// t96 re-baseline: only lake generation (rim height, spawn clearance, levelling) changes these material bytes. // t96 re-baseline: only lake generation (rim height, spawn clearance, levelling) changes these material bytes.
for (const [seed, expected] of [ for (const [seed, expected] of [
[777, '7d60a0f1a92b42438471c8c15c5cc92576a75a4aa54b8c519c42904acb7088a6'], [777, 'dc0a19b076f341bcd944e1692651446198f57c7353db5fdea36d6bdd886ab6ff'],
[77, '724eb1730ca5fead6aeaa13994beb2e066bb9d65c7db7e0b790857e2d72b7c2a'], [77, 'aa979290d81a758e6ff68fa22b06513fb546aa73a80d09e69b35899a46758a0b'],
[4242, '9ffdabf1ffe8ab6cd5855ccfe05c31221d1a0517d960bf2e2273018e9dbf03f9'], [4242, '25737574612fe0395a2b0304a1ac698f817aa68bcd9e2b13a24ba794201ba6dc'],
]) { ]) {
run(`generateWorld(${seed}, 512, 384)`); run(`generateWorld(${seed}, 512, 384)`);
check(`seed ${seed} restores corner sealing`,run('CORNER_SEAL')===true); check(`seed ${seed} restores corner sealing`,run('CORNER_SEAL')===true);
+8 -3
View File
@@ -17,9 +17,9 @@ function surfacePools() {
})()`); })()`);
} }
for (const [seed, expected] of [ for (const [seed, expected] of [
[7, '4c4778417e6cf6e9577b831e66111bd374068a14e15642bae304b8a45a68b879'], [7, '5e230523dc4dda212e7c67ceb7f3b496ff201d826366e1d7c379efd1351d1956'],
[42, '907f2ed79b9fda1ae99c5ee55fa19ce85eff8ffdcd7dadac14a2deb42223217d'], [42, 'cc3d150a91caac6ab88dfefbd45d85c5bc5c38fc77d7913b9de157647f4b9d7b'],
[424242, '40080ea6039d0d4247e36fd9a5837d0675100414f198d77419ecf500c7f9d6ad'], [424242, '1339c90f4f7f1d245c2e21aa4b8d23b21c199b8251392256f6de5ed704e2b396'],
[2, null], [2, null],
[1, null], [1, null],
// Before t96 round 3 these lakes ran over their lower rim up to the trade dock. // Before t96 round 3 these lakes ran over their lower rim up to the trade dock.
@@ -36,6 +36,11 @@ for (const [seed, expected] of [
check(`seed ${seed} conserves water through levelling`, run('mat.reduce((n, m) => n + (m === WATER), 0) === waterBeforeLevel')); check(`seed ${seed} conserves water through levelling`, run('mat.reduce((n, m) => n + (m === WATER), 0) === waterBeforeLevel'));
const initial = surfacePools(); const initial = surfacePools();
check(`seed ${seed} starts with flat surface pools`, initial.length > 0 && initial.every(p => p.max - p.min <= 1), JSON.stringify(initial)); check(`seed ${seed} starts with flat surface pools`, initial.length > 0 && initial.every(p => p.max - p.min <= 1), JSON.stringify(initial));
// Lakes dug to their lower rim once left a cliff on the high side (seed 766733's 15-grain step).
check(`seed ${seed} lake banks slope with no cliff`, run(`(() => { const d = machines.find(m => m && m.type === 'dock');
const top = x => { let y = 0; while (y < H && (mat[y * W + x] === EMPTY || mat[y * W + x] === WATER)) y++; return y; };
return ${JSON.stringify(initial)}.every(p => { for (let x = Math.max(1, p.x0 - 40); x < Math.min(W - 1, p.x1 + 40); x++)
if (x < d.x - 2 || x > d.x + 26) { if (Math.abs(top(x + 1) - top(x)) > 3) return false; } return true; }); })()`), JSON.stringify(initial));
check(`seed ${seed} keeps its lakes clear of the trade dock`, run(`(() => { const d = machines.find(m => m && m.type === 'dock'); check(`seed ${seed} keeps its lakes clear of the trade dock`, run(`(() => { const d = machines.find(m => m && m.type === 'dock');
return ${JSON.stringify(initial)}.every(p => p.x1 < d.x - 8 || p.x0 > d.x + 24 + 8); })()`), JSON.stringify(initial)); return ${JSON.stringify(initial)}.every(p => p.x1 < d.x - 8 || p.x0 > d.x + 24 + 8); })()`), JSON.stringify(initial));
check(`seed ${seed} restores pinned generation switches`, run('LSTEP === 8 && LFILM === 4 && SORTP === 0.06 && CORNER_SEAL && POWDER_FAN')); check(`seed ${seed} restores pinned generation switches`, run('LSTEP === 8 && LFILM === 4 && SORTP === 0.06 && CORNER_SEAL && POWDER_FAN'));
+2 -2
View File
@@ -185,8 +185,8 @@ oreNames.forEach((name, j) => {
}); });
const totalOre = oreBalance.counts.reduce((a, b) => a + b, 0); const totalOre = oreBalance.counts.reduce((a, b) => a + b, 0);
check('total ore is about 4 to 4.5 times the original', totalOre >= 162519 * 4 && totalOre <= 162519 * 4.5, totalOre); check('total ore is about 4 to 4.5 times the original', totalOre >= 162519 * 4 && totalOre <= 162519 * 4.5, totalOre);
// t96 round 3: lake basins moved, so ore under them changed (414 cells, all under the two lakes). // t96: lake basins moved and their high banks now slope, so ore there changed (only at the two lakes).
check('shallow ore matches the original generator', oreBalance.shallowHash === 607138519, oreBalance.shallowHash); check('shallow ore matches the original generator', oreBalance.shallowHash === 1640715415, oreBalance.shallowHash);
run('generateWorld(99, 4096, 1536, null)'); run('generateWorld(99, 4096, 1536, null)');
const marks = run('(()=>{let lakes=0,peaks=0,inLake=false;for(let x=1;x<W-1;x++){const l=mat[(surfY[x]-1)*W+x]===WATER;if(l&&!inLake)lakes++;inLake=l;if(surfY[x]<surfBase-100&&surfY[x]<=surfY[x-1]&&surfY[x]<surfY[x+1])peaks++;}return [lakes,peaks]})()'); const marks = run('(()=>{let lakes=0,peaks=0,inLake=false;for(let x=1;x<W-1;x++){const l=mat[(surfY[x]-1)*W+x]===WATER;if(l&&!inLake)lakes++;inLake=l;if(surfY[x]<surfBase-100&&surfY[x]<=surfY[x-1]&&surfY[x]<surfY[x+1])peaks++;}return [lakes,peaks]})()');
check('a large world has several lakes and mountains across it', marks[0] >= 2 && marks[1] >= 2, marks); check('a large world has several lakes and mountains across it', marks[0] >= 2 && marks[1] >= 2, marks);