No deep ore in rock hot enough to light or melt it; reloaded worlds settle again (t3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -240,6 +240,10 @@ above, including:
|
|||||||
their price). The top quarter of the underground is unchanged; copper also forms in deep stone
|
their price). The top quarter of the underground is unchanged; copper also forms in deep stone
|
||||||
and granite, coal in basalt, tin deeper. Lenses grow from lower noise thresholds only:
|
and granite, coal in basalt, tin deeper. Lenses grow from lower noise thresholds only:
|
||||||
stretching the noise with depth sheared it into diagonal streaks.
|
stretching the noise with depth sheared it into diagonal streaks.
|
||||||
|
No ore is placed where the natural rock temperature would light it (coal) or melt it (metal
|
||||||
|
ores, within 200°C): deep coal and gold next to the magma burned and ran forever once a
|
||||||
|
reload woke those areas, and a Huge world dropped to about 10 fps. So deep coal stays in a
|
||||||
|
cooler mid band, and coal is about 3.2x rather than 5x; total ore about 4.2x.
|
||||||
- Waste rock pays: grinding granite and basalt finds about twice the ore, and builders order
|
- Waste rock pays: grinding granite and basalt finds about twice the ore, and builders order
|
||||||
granite and basalt rubble in bulk.
|
granite and basalt rubble in bulk.
|
||||||
- A Huge size (6144 x 2048); saves are gzip-compressed (a Medium save went from 29 MB to 0.4 MB).
|
- A Huge size (6144 x 2048); saves are gzip-compressed (a Medium save went from 29 MB to 0.4 MB).
|
||||||
|
|||||||
+1
-1
@@ -324,7 +324,7 @@ Weather: clouds from steam, rain and snow, and the world water count.
|
|||||||
simTick: one simulation step, in order.
|
simTick: one simulation step, in order.
|
||||||
- functions: simTick
|
- functions: simTick
|
||||||
|
|
||||||
### src/sim/worldgen.js (322 lines)
|
### src/sim/worldgen.js (326 lines)
|
||||||
World generation: noise, layers, ores, caves, water, trees, and clearing tiny hard specks.
|
World generation: noise, layers, ores, caves, water, trees, and clearing tiny hard specks.
|
||||||
- functions: makeNoise, fbm, clearHardSpecks, generateWorld
|
- functions: makeNoise, fbm, clearHardSpecks, generateWorld
|
||||||
- variables: SPECK_MIN
|
- variables: SPECK_MIN
|
||||||
|
|||||||
@@ -4,6 +4,19 @@ 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
|
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.
|
open or not verified. Keep each entry short; the git history has the details.
|
||||||
|
|
||||||
|
## 2026-10-24: t3 fix: deep ore burned and melted after a reload (Claude Code)
|
||||||
|
|
||||||
|
- Owner: a Huge world ran at about 10 fps after travelling between claims, even after a refresh.
|
||||||
|
Cause: reloading wakes every area, and t3's deep coal (ignites at 430°C, natural rock reaches
|
||||||
|
that at about 70% depth) and gold next to the magma burned and melted forever: about 2,100 of
|
||||||
|
12,288 areas stayed busy, 65 ms per tick. A fresh world hid it because nothing deep was awake.
|
||||||
|
- `worldgen.js`: an ore is not placed where `AMB[y]` is within 120°C of its ignition or 200°C of
|
||||||
|
its melting point; a cooler coal band in stone and granite at 45%+ depth replaces most of the
|
||||||
|
lost coal. Huge after a reload: 335 areas awake, about 5 ms per tick; Medium 104 (126 before
|
||||||
|
t3). Medium seed 424242 ore is now about 4.2x the original (coal 3.2x, gold 2.8x).
|
||||||
|
- `test/t_world.js`: no ore in too-hot rock; a reloaded world settles with no fires and under
|
||||||
|
10% of areas moving; ore balance ranges updated. Existing saves made with the bug keep it.
|
||||||
|
|
||||||
## 2026-10-24: t3: a world that lasts (Claude Code, Codex)
|
## 2026-10-24: t3: a world that lasts (Claude Code, Codex)
|
||||||
|
|
||||||
- `26d17c2` deeper is richer (`worldgen.js` ore thresholds drop with depth below fy 0.25).
|
- `26d17c2` deeper is richer (`worldgen.js` ore thresholds drop with depth below fy 0.25).
|
||||||
|
|||||||
@@ -177,10 +177,12 @@ function generateWorld(seedIn, w, h, onProgress) {
|
|||||||
}
|
}
|
||||||
// ores
|
// ores
|
||||||
if (m === STONE || m === DIRT || m === GRANITE || m === BASALT || m === CLAY) {
|
if (m === STONE || m === DIRT || m === GRANITE || m === BASALT || m === CLAY) {
|
||||||
|
const host = m;
|
||||||
// Leave the shallow quarter unchanged; deeper rock has broader, richer lenses.
|
// Leave the shallow quarter unchanged; deeper rock has broader, richer lenses.
|
||||||
const rich = Math.min(1, Math.max(0, (fy - 0.25) / 0.6));
|
const rich = Math.min(1, Math.max(0, (fy - 0.25) / 0.6));
|
||||||
const c = nCoal(x / 13, y / 9);
|
const c = nCoal(x / 13, y / 9);
|
||||||
if ((d > 6 && fy < 0.45 && c > 0.5 - 0.55 * rich && m !== CLAY) ||
|
if ((d > 6 && fy < 0.45 && c > 0.5 - 0.55 * rich && m !== CLAY) ||
|
||||||
|
(fy >= 0.45 && (m === STONE || m === GRANITE) && c > 0.62 - 0.3 * rich) ||
|
||||||
(m === BASALT && fy > 0.6 && c > 0.58 - 0.4 * rich)) m = COALV;
|
(m === BASALT && fy > 0.6 && c > 0.58 - 0.4 * rich)) m = COALV;
|
||||||
else {
|
else {
|
||||||
const cu = nCu(x / 9, y / 8);
|
const cu = nCu(x / 9, y / 8);
|
||||||
@@ -201,6 +203,8 @@ function generateWorld(seedIn, w, h, onProgress) {
|
|||||||
else if (fy > 0.68 && (m === GRANITE || m === BASALT) && nAu(x / 6, y / 5) > 0.7 - 0.18 * rich) m = AUV;
|
else if (fy > 0.68 && (m === GRANITE || m === BASALT) && nAu(x / 6, y / 5) > 0.7 - 0.18 * rich) m = AUV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// no ore where the rock is naturally hot enough to light or melt it: it would burn or run forever
|
||||||
|
if (m !== host && (IGN[m] ? AMB[y] > IGN[m] - 120 : MELT[m] && AMB[y] > MELT[m] - 200)) m = host;
|
||||||
}
|
}
|
||||||
// caves
|
// caves
|
||||||
if (d > 9) {
|
if (d > 9) {
|
||||||
|
|||||||
+12
-2
@@ -170,11 +170,12 @@ const oldOre = [33493, 26508, 77679, 11797, 7723, 3638, 1681];
|
|||||||
const oreNames = ['coal', 'copper', 'iron', 'zinc', 'tin', 'silver', 'gold'];
|
const oreNames = ['coal', 'copper', 'iron', 'zinc', 'tin', 'silver', 'gold'];
|
||||||
oreNames.forEach((name, j) => {
|
oreNames.forEach((name, j) => {
|
||||||
const ratio = oreBalance.counts[j] / oldOre[j];
|
const ratio = oreBalance.counts[j] / oldOre[j];
|
||||||
const lo = j < 2 ? 4.5 : j < 5 ? 4 : 3, hi = j < 2 ? 5.5 : j < 5 ? 5 : 4;
|
// coal is lower than the rest: seams deep enough to light in the natural heat are left out
|
||||||
|
const lo = j === 0 ? 3 : j < 5 ? 4 : 2.5, hi = j === 0 ? 4 : j < 5 ? 5 : 3.5;
|
||||||
check(`${name} stays within its intended increase`, ratio >= lo && ratio <= hi, ratio.toFixed(2));
|
check(`${name} stays within its intended increase`, ratio >= lo && ratio <= hi, ratio.toFixed(2));
|
||||||
});
|
});
|
||||||
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.5 to 5 times the original', totalOre >= 162519 * 4.5 && totalOre <= 162519 * 5, totalOre);
|
check('total ore is about 4 to 4.5 times the original', totalOre >= 162519 * 4 && totalOre <= 162519 * 4.5, totalOre);
|
||||||
check('shallow ore matches the original generator', oreBalance.shallowHash === 706875613, oreBalance.shallowHash);
|
check('shallow ore matches the original generator', oreBalance.shallowHash === 706875613, 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]})()');
|
||||||
@@ -258,4 +259,13 @@ const pour = (bx) => {
|
|||||||
};
|
};
|
||||||
const [withBot, aboveBot] = pour(118), [without] = pour(200);
|
const [withBot, aboveBot] = pour(118), [without] = pour(200);
|
||||||
check('sand poured onto the bot falls through it to the ground', withBot === without && aboveBot === 0, `${withBot} grains landed (${without} with the bot away), ${aboveBot} held up above it`);
|
check('sand poured onto the bot falls through it to the ground', withBot === without && aboveBot === 0, `${withBot} grains landed (${without} with the bot away), ${aboveBot} held up above it`);
|
||||||
|
|
||||||
|
// deep ore never sits in rock hot enough to light or melt it: a reloaded world wakes every area,
|
||||||
|
// and burning coal or running gold down there kept thousands of areas busy forever
|
||||||
|
run('generateWorld(77, 2048, 1024, null)');
|
||||||
|
const hotOre = run('(() => { let n = 0; for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) { const m = mat[y * W + x]; if ([COALV, CUV, FEV, SNV, ZNV, AGV, AUV].includes(m) && (IGN[m] ? AMB[y] > IGN[m] - 120 : AMB[y] > MELT[m] - 200)) n++; } return n; })()');
|
||||||
|
check('no ore is generated where the rock is hot enough to light or melt it', hotOre <= 20, hotOre);
|
||||||
|
run('globalThis.__s = serializeWorld(); deserializeWorld(JSON.parse(JSON.stringify(__s.meta)), __s.buffers); for (let k = 0; k < 900; k++) simTick();');
|
||||||
|
const busy = run('(() => { let aw = 0, fl = 0; for (let c = 0; c < NCH; c++) if (awake[c]) aw++; for (let i = 0; i < N; i++) if (mat[i] === FLAME) fl++; return [aw, NCH, fl]; })()');
|
||||||
|
check('a reloaded world settles: no fires deep down, few areas still moving', busy[2] === 0 && busy[0] < busy[1] * 0.1, busy);
|
||||||
done();
|
done();
|
||||||
|
|||||||
Reference in New Issue
Block a user