Grains resting on anything built cast no shadow (t37)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 20:21:31 -04:00
co-authored by Claude Opus 5.5
parent 7d1be16094
commit f19facbe5c
4 changed files with 45 additions and 6 deletions
+1 -1
View File
@@ -352,7 +352,7 @@ Drawing (src/render/): browser only. This file: the renderer state R, colour tab
- functions: glowIndex, thermIdx, pipeCellColor, pipeBodyCell, thinPipeColor, beltLike, buildSky, initRender, resizeRender, viewCells, updateCamera, snapCamera, screenToWorld, worldToScreenPx, renderMonoFont
- variables: R, GLOW_W, GLOW_R, GLOW_G, GLOW_B, THERM, PC, TREE_R, TREE_G, TREE_B, BG, SKY_R, SKY_G, SKY_B
### src/render/light.js (104 lines)
### src/render/light.js (103 lines)
Propagated coloured light from glowing and burning cells.
- functions: builtForLight, looseForLight, computeLight
+9
View File
@@ -4,6 +4,15 @@ 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
open or not verified. Keep each entry short; the git history has the details.
## 2026-10-25: t37 follow-up: grains on built things cast no shadows (Claude Code, Codex)
- The owner saw a flickering shadow under a grinder fed by a stream: grains resting on its
housing for a moment shaded. `looseForLight` (`src/render/light.js`) now treats a loose stack
that rests on any `builtForLight` cell, or ends in air or gas, as see-through. Stacks on natural
ground or deeper than 12 cells still shade.
- `test/t_light.js` (a grinder stream checked for 180 ticks in both lighting modes);
`run_all.py --browser` ALL PASSED.
## 2026-10-25: t37 follow-up: falling grains cast no shadows (Claude Code, Codex)
- The owner saw a single falling grain darken a column of sky. `onBeltForLight` became
+1 -2
View File
@@ -8,8 +8,7 @@ function builtForLight(i, x, y) {
function looseForLight(i) {
for (let depth = 0; depth <= 12 && i < N; depth++, i += W) {
const m = mat[i], ph = PH[m];
if (m === BELTL || m === BELTR || part[i] === P_FILTER) return true;
if (part[i]) return false;
if (builtForLight(i, i % W, (i / W) | 0)) return true;
if (ph !== PH_POWDER && ph !== PH_LIQUID) return m === EMPTY || ph === PH_GAS;
}
return false;
+34 -3
View File
@@ -97,9 +97,10 @@ for (const [name, support, depth, gap, expected] of [
['12 falling grains', '', 12, '', true],
['13 falling grains', '', 13, '', false],
['natural ledge', 'mat[i] = STONE; flags[i] = F_NAT', 3, '', false],
['wall floor', 'mat[i] = STONE', 3, '', false],
['tube', 'part[i] = P_TR', 3, '', false],
['loaded tube', 'mat[i] = SAND; part[i] = P_TR', 3, '', false],
['deep natural heap', 'mat[i] = SAND; flags[i] = F_NAT', 13, '', false],
['wall floor', 'mat[i] = STONE', 3, '', true],
['tube', 'part[i] = P_TR', 3, '', true],
['loaded tube', 'mat[i] = SAND; part[i] = P_TR', 3, '', true],
]) {
run(`
for (let y = 40; y <= 53; y++) { const i = y * W + 300; mat[i] = SAND; part[i] = 0; flags[i] = 0; }
@@ -112,4 +113,34 @@ run('mat[40 * W + 300] = SAND; mat[41 * W + 300] = EMPTY;');
check('a single grain in mid-air is see-through', run('looseForLight(40 * W + 300)'));
run('mat[(H - 1) * W + 300] = SAND;');
check('loose cell detection stops at the world bottom', run('!looseForLight((H - 1) * W + 300)'));
flatWorld(run, 320, 200, 160);
run(`
bot.lamp = false; stats.refined = 0;
placeBlock(BP_GRIND, 0, 20, 20, true);
for (let bx = 8; bx < 30; bx++) placeBlock(BP_BELT, 1, bx, 36, true);
setCell(79 * W + 80, SAND, AMB[79], 0);
`);
check('sand resting on grinder housing is see-through', run('looseForLight(79 * W + 80)'));
for (const mode of ['full', 'fast']) {
const result = run(`(() => {
SET.lighting = '${mode}';
let mismatches = 0, first = '';
for (let t = 0; t < 180; t++) {
if (t % 4 === 0) setCell(48 * W + 80, SAND, AMB[48], 0);
simTick();
computeLight();
for (const a of [R.lr, R.lg, R.lb]) for (let x = 76; x < 88; x += 2) {
const below = lightAt(a, x, 160), open = lightAt(a, 152, 160);
if (Math.abs(below - open) > 1e-6) {
mismatches++;
if (!first) first = 'tick ' + tick + ', x ' + x + ': ' + below + ' / ' + open;
}
}
}
return { mismatches, first };
})()`);
check(`${mode}: streaming grinder leaves ground in daylight for 180 consecutive ticks`, result.mismatches === 0, result.first || 'no shadow');
}
check('the streaming grinder actually processes its input', run('stats.refined > 0'), run('stats.refined'));
done();