From 26d17c2b1d244d06655123866eb6291d0d567061 Mon Sep 17 00:00:00 2001 From: 44r0n7 <44r0n7+gitea@pm.me> Date: Sat, 24 Oct 2026 17:30:51 -0400 Subject: [PATCH] Deeper is richer: about 4.7x the ore in a world, shallow layers unchanged (t3) Co-Authored-By: Claude Opus 5.5 --- docs/PROJECT_MAP.md | 2 +- src/sim/worldgen.js | 20 +++++++++++++------- test/t_world.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/docs/PROJECT_MAP.md b/docs/PROJECT_MAP.md index fa11110..49a9a73 100644 --- a/docs/PROJECT_MAP.md +++ b/docs/PROJECT_MAP.md @@ -324,7 +324,7 @@ Weather: clouds from steam, rain and snow, and the world water count. simTick: one simulation step, in order. - functions: simTick -### src/sim/worldgen.js (316 lines) +### src/sim/worldgen.js (322 lines) World generation: noise, layers, ores, caves, water, trees, and clearing tiny hard specks. - functions: makeNoise, fbm, clearHardSpecks, generateWorld - variables: SPECK_MIN diff --git a/src/sim/worldgen.js b/src/sim/worldgen.js index 12db774..97a6dc8 100644 --- a/src/sim/worldgen.js +++ b/src/sim/worldgen.js @@ -177,22 +177,28 @@ function generateWorld(seedIn, w, h, onProgress) { } // ores if (m === STONE || m === DIRT || m === GRANITE || m === BASALT || m === CLAY) { + // 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 c = nCoal(x / 13, y / 9); - if (d > 6 && fy < 0.45 && c > 0.5 && m !== CLAY) m = COALV; + if ((d > 6 && fy < 0.45 && c > 0.5 - 0.55 * rich && m !== CLAY) || + (m === BASALT && fy > 0.6 && c > 0.58 - 0.4 * rich)) m = COALV; else { const cu = nCu(x / 9, y / 8); const cuBand = d > 14 && fy < 0.48; - if (cuBand && (m === STONE || m === DIRT) && cu > (d < 140 ? 0.5 : 0.56)) m = CUV; + if ((cuBand && (m === STONE || m === DIRT) && cu > (d < 140 ? 0.5 : 0.56) - 0.65 * rich) || + (fy > 0.48 && fy < 0.8 && (m === STONE || m === GRANITE) && cu > 0.6 - 0.46 * rich)) m = CUV; else { // iron and zinc are harder than the stone around them, so they come in fewer, chunkier // lenses rather than a scatter of specks that a stone bit would leave in the way const fe = nFe(x / 24, y / 13); - if (fy > 0.28 && (m === STONE || m === GRANITE || m === BASALT) && fe > (m === STONE ? 0.55 : 0.47)) m = FEV; + if (fy > 0.28 && (m === STONE || m === GRANITE || m === BASALT) && fe > (m === STONE ? 0.55 : 0.47) - 0.55 * rich) m = FEV; // rarer ores in deeper bands: tin, then zinc, silver in granite, gold deepest - else if (fy > 0.15 && fy < 0.55 && m === STONE && nSn(x / 8, y / 7) > 0.62) m = SNV; - else if (fy > 0.35 && fy < 0.78 && (m === STONE || m === GRANITE) && nZn(x / 22, y / 13) > 0.6) m = ZNV; - else if (fy > 0.5 && fy < 0.88 && m === GRANITE && nAg(x / 7, y / 6) > 0.66) m = AGV; - else if (fy > 0.68 && (m === GRANITE || m === BASALT) && nAu(x / 6, y / 5) > 0.7) m = AUV; + else if (((fy > 0.15 && fy < 0.55 && m === STONE) || + (fy >= 0.55 && fy < 0.8 && (m === STONE || m === GRANITE))) && + nSn(x / 8, y / 7) > 0.62 + Math.max(0, fy - 0.55) * 0.4 - 0.425 * rich) m = SNV; + else if (fy > 0.35 && fy < 0.78 && (m === STONE || m === GRANITE) && nZn(x / 22, y / 13) > 0.6 - 0.49 * rich) m = ZNV; + else if (fy > 0.5 && fy < 0.88 && m === GRANITE && nAg(x / 7, y / 6) > 0.66 - 0.32 * rich) m = AGV; + else if (fy > 0.68 && (m === GRANITE || m === BASALT) && nAu(x / 6, y / 5) > 0.7 - 0.18 * rich) m = AUV; } } } diff --git a/test/t_world.js b/test/t_world.js index 6af04bd..75fe17d 100644 --- a/test/t_world.js +++ b/test/t_world.js @@ -134,6 +134,48 @@ const shallowOre = run('(()=>{let cu=0,fe=0;for(let x=0;x 2000 && shallowOre[1] > 200, shallowOre); check('the deep layers fill the extra depth', run('depthY(0.905) - surfBase') > 600 && run('Math.abs(depthY(0.28) - surfBase - 131) <= 1'), run('[depthY(0.28), depthY(0.5), depthY(0.905)].map(y => y - surfBase)')); check('it is still warm a hundred cells above the ground', run('AMB[surfBase - 100]') > 0, run('AMB[surfBase - 100].toFixed(1)')); +// Compare ore density in the generated shallow and deep underground, excluding air. +const oreDepth = run(`(() => { + let shallow = 0, shallowOre = 0, deep = 0, deepOre = 0, deepCu = 0; + const ores = [COALV, CUV, FEV, SNV, ZNV, AGV, AUV]; + for (let y = 0; y < H; y++) { + const d = y - surfBase; + const fy = d <= shallowD ? d / shallowD * 0.3 : 0.3 + (d - shallowD) / (underD - shallowD) * 0.7; + for (let x = 0; x < W; x++) { + const m = mat[y * W + x]; + if (y < surfY[x] || m === EMPTY) continue; + const ore = ores.includes(m); + if (fy < 0.25) { shallow++; if (ore) shallowOre++; } + if (fy > 0.6) { deep++; if (ore) deepOre++; } + if (fy > 0.5 && m === CUV) deepCu++; + } + } + return [shallowOre / shallow, deepOre / deep, deepCu]; +})()`); +check('deep ground has at least three times the shallow ore density', oreDepth[1] >= 3 * oreDepth[0], `shallow ${oreDepth[0]}, deep ${oreDepth[1]}`); +check('copper veins also form below half depth', oreDepth[2] > 0, oreDepth[2]); +// Medium seed 424242: keep each ore's increase within its intended balance. +run('generateWorld(424242, 3072, 1280, null)'); +const oreBalance = run(`(() => { + const ids = [COALV, CUV, FEV, ZNV, SNV, AGV, AUV], counts = ids.map(() => 0); + let shallowHash = 2166136261; + for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) { + const m = mat[y * W + x], j = ids.indexOf(m); + if (j >= 0) counts[j]++; + if (depthU(y) < 0.25) shallowHash = Math.imul(shallowHash ^ (j >= 0 ? m : 0), 16777619) >>> 0; + } + return { counts, shallowHash }; +})()`); +const oldOre = [33493, 26508, 77679, 11797, 7723, 3638, 1681]; +const oreNames = ['coal', 'copper', 'iron', 'zinc', 'tin', 'silver', 'gold']; +oreNames.forEach((name, 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; + check(`${name} stays within its intended increase`, ratio >= lo && ratio <= hi, ratio.toFixed(2)); +}); +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('shallow ore matches the original generator', oreBalance.shallowHash === 706875613, oreBalance.shallowHash); run('generateWorld(99, 4096, 1536, null)'); const marks = run('(()=>{let lakes=0,peaks=0,inLake=false;for(let x=1;x= 2 && marks[1] >= 2, marks);