Files
deepgrain-foundry/src/sim/processors.js
T
44r0n7andClaude Opus 5.5 b270c4e818 Deepgrain Foundry: move from a claude.ai artifact to a local repository
The game as of artifact version 24, ready for local development with
coding agents.

- Source split from three large files into 52 small ones under src/sim,
  src/render and src/ui, joined in the order in src/manifest.json by
  build.py. The joined code is unchanged apart from comments; every
  headless suite gives the same output, and screenshots in both looks
  are pixel-identical to before.
- Light tidying with no change in behaviour: one movement scan loop,
  shared hazard-stripe and belt shader helpers, unused code removed, a
  shared check helper for the test suites.
- World files can be exported as an ordinary download outside claude.ai.
- Tests: broken old scripts removed, experiments moved to tools/,
  test/run_all.py added. Tools: shot.py (repeatable screenshots),
  where.py, map.py.
- Docs: AGENTS.md, CLAUDE.md, NOTES.md (the owner's to-do inbox),
  docs/PROJECT_MAP.md, DESIGN.md, TESTING.md, systems notes, the
  owner's requests and an agent log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9kLmYADKSEjuwUN3rFxDA
2026-10-23 16:05:31 -04:00

100 lines
4.6 KiB
JavaScript

// Processing machines: the Refiner, the Stone cutter and the Silo.
// ---------- Refiner ----------
// Grinding a grain of rock, gravel, sand or dirt turns up the odd grain of ore: [chance, what],
// tried in turn. If none turns up, half the time a grain of fine sand comes out, else nothing:
// the rest is ground to dust and washed away, so a refiner halves a pile of waste rock.
const REFINE = [];
REFINE[RSTONE] = [[0.025, CUORE], [0.02, COAL], [0.015, FEORE], [0.006, SNORE]];
REFINE[RGRAN] = [[0.02, FEORE], [0.012, ZNORE], [0.01, CUORE], [0.002, AGORE]];
REFINE[RBAS] = [[0.03, FEORE], [0.01, ZNORE], [0.003, AUORE]];
REFINE[GRAVEL] = [[0.03, FEORE], [0.02, CUORE], [0.01, SNORE], [0.003, ZNORE]];
REFINE[SAND] = [[0.01, SNORE], [0.008, FEORE], [0.0015, AUORE]];
REFINE[LDIRT] = [[0.08, LCLAY], [0.01, COAL], [0.008, CUORE], [0.003, ACORN]];
const REFINE_SAND = 0.5, REFINE_RATE = 0.5; // grains ground per tick
// a free cell under the refiner's chute, or -1 if the way out is blocked
function refinerSlot(mm) {
const y = mm.y + 12;
if (y >= H) return -1;
for (const dx of [5, 6, 4, 7]) { const j = y * W + mm.x + dx; if (mat[j] === EMPTY && !part[j] && !occ[j] && !mach[j]) return j; }
return -1;
}
function refinerTick(mm) {
mm.work = (mm.work || 0) + REFINE_RATE;
if (mm.work < 1) return;
let jobs = Math.floor(mm.work); mm.work -= jobs;
const y = mm.y + 9;
mm.blocked = false;
for (let k = 0; k < 8 && jobs > 0; k++) {
const x = mm.x + 2 + ((k + tick) & 7), i = y * W + x, m = mat[i];
if (m === EMPTY || PH[m] === PH_GAS || PH[m] === PH_SOLID || stamp[i] === tickStamp) continue;
const out = refinerSlot(mm);
if (out < 0) { mm.blocked = true; break; }
jobs--; mm.busyT = tick;
const T = temp[i];
if (PH[m] === PH_POWDER && REFINE[m]) {
setCell(i, EMPTY, AMB[y], 0);
let got = 0;
for (const [chance, o] of REFINE[m]) if (rnd() < chance) { got = o; break; }
if (got) { stats.refinedOre = (stats.refinedOre || 0) + 1; discover(got, x, y, m, 'refine'); }
else if (rnd() < REFINE_SAND) got = SAND;
if (got) setCell(out, got, T, 0);
stats.refined = (stats.refined || 0) + 1;
} else {
// ore, coal, liquids: nothing to grind, so they go straight through
setCell(out, m, T, 0); setCell(i, EMPTY, AMB[y], 0);
}
}
}
// ---------- Stone cutter ----------
const CUT_PER_BRICK = 8, CUT_RATE = 0.5;
function cutterTick(mm) {
mm.work = (mm.work || 0) + CUT_RATE;
if (mm.work < 1) return;
let jobs = Math.floor(mm.work); mm.work -= jobs;
const y = mm.y + 4;
mm.blocked = false;
for (let k = 0; k < 4 && jobs > 0; k++) {
const x = mm.x + 2 + ((k + tick) & 3), i = y * W + x, m = mat[i];
if (m === EMPTY || PH[m] === PH_GAS || PH[m] === PH_SOLID || stamp[i] === tickStamp) continue;
if (m === RSTONE) {
if ((mm.cut || 0) + 1 >= CUT_PER_BRICK) {
// a brick is ready: it needs room under the machine
if (!entFreeAt(mm.x + 2, mm.y + 8)) { mm.blocked = true; break; }
mm.cut = 0;
const e = spawnBar(BARSB, mm.x + 2, mm.y + 8, (rnd() - 0.5) * 0.1, 0.05);
if (e) { e.T = temp[i]; discover(BARSB, x, y, RSTONE, 'cut'); stats.bricks = (stats.bricks || 0) + 1; }
} else mm.cut = (mm.cut || 0) + 1;
setCell(i, EMPTY, AMB[y], 0);
} else {
// anything else drops through, out of the bottom
const j = (mm.y + 8) * W + mm.x + 3 + (k & 1);
if (mm.y + 8 >= H || mat[j] !== EMPTY || part[j] || occ[j]) { mm.blocked = true; break; }
setCell(j, m, temp[i], 0); setCell(i, EMPTY, AMB[y], 0);
}
jobs--; mm.busyT = tick;
}
}
// ---------- Silo ----------
// One loose material, counted (not simulated grain by grain inside): grains in the roof hatch
// go in, and with the gate open they come out of the bottom of the cone.
const SILO_CAP = 4000, SILO_RATE = 1;
function siloTick(mm) {
for (let dy = 0; dy < 2; dy++) for (let dx = 3; dx <= 8; dx++) {
const i = (mm.y + dy) * W + mm.x + dx, m = mat[i];
if (PH[m] !== PH_POWDER || stamp[i] === tickStamp || (mm.sn > 0 && m !== mm.sm) || (mm.sn || 0) >= SILO_CAP) continue;
mm.sm = m; mm.sn = (mm.sn || 0) + 1; mm.fillT = tick;
setCell(i, EMPTY, AMB[mm.y + dy], 0);
}
if (mm.open && mm.sn > 0) {
let put = 0;
for (let k = 0; k < 4 && put < SILO_RATE && mm.sn > 0; k++) {
const x = mm.x + 4 + ((k + tick) & 3), j = (mm.y + 23) * W + x;
if (mat[j] !== EMPTY || occ[j]) continue;
setCell(j, mm.sm, AMB[mm.y + 23], 0); mm.sn--; put++; mm.pourT = tick;
}
if (!mm.sn) mm.sm = 0;
}
}
// a silo with something in it is emptied before it can be taken down
function siloFull(mm) { return mm && mm.type === 'silo' && mm.sn > 0; }