Loose grains and solid ground look different: a seam on the rock, pebbly lighter grains (t33)

- Smooth look: natural ground gets a soft seam where loose grains meet it; packed grains keep a
  pebble texture and are slightly lighter. Pixel look and thermal lens: the same idea per pixel.
- play.py: the F-on-a-mold toast check records toasts the key press makes instead of counting
  the ones on screen, which failed when an older toast expired at the same moment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 16:59:54 -04:00
co-authored by Claude Opus 5.5
parent 20d0627d75
commit 1a300bf744
7 changed files with 108 additions and 7 deletions
+41
View File
@@ -35,6 +35,47 @@ with sync_playwright() as p:
ev("SET.look = 'smooth'"); pg.wait_for_timeout(1500)
ev("SM.scale = 0.5"); pg.wait_for_timeout(1500)
st = ev('[R.smoothOn, SM.scale, SM.canvas.width, R.canvas.width, SET.look, SM.ok, SM.why]'); check('half-resolution smooth still draws', (st[0] and st[2] == round(st[3] * st[1])) or st[1] > 0.5, st)
# Uniform colours isolate the solid/loose seam from material noise and lighting.
ground = ev('''() => {
GAME.paused = true; requestAnimationFrame = () => 0;
SET.lighting = 'off'; SET.glow = 'off'; SET.grain = 0; SET.zoom = 6;
resizeRender(); bot.x = 138; bot.y = 118; snapCamera();
for (let y = 60; y < 220; y++) for (let x = 40; x < 240; x++) {
const i = y * W + x;
setCell(i, STONE, 22, F_NAT); bgm[i] = 0; noiseB[i] = 128;
}
const fill = m => { for (let y = 100; y < 140; y++) for (let x = 100; x < 180; x++) setCell(y * W + x, m, 22); };
const at = (x, y) => { const c = R.px[(y - R.vy0) * R.bw + x - R.vx0]; return (c & 255) + (c >>> 8 & 255) + (c >>> 16 & 255); };
const out = {};
for (const lens of [false, true]) {
R.lens = lens; SET.look = 'pixel'; fill(RSTONE); drawWorld();
const edge = at(140, 140), inside = at(140, 142), loose = at(140, 130);
fill(STONE); drawWorld();
out[lens ? 'thermal' : 'pixel'] = [edge, inside, loose, at(140, 140)];
}
R.lens = false;
const i = 140 * W + 140;
flags[i] = 0; fill(RSTONE); drawWorld(); const built = at(140, 140);
fill(STONE); drawWorld(); out.built = [built, at(140, 140)]; flags[i] = F_NAT;
fill(RSTONE); SET.look = 'smooth'; SM.scale = 1; drawWorld();
const sample = (x, y) => {
const p = worldToScreenPx(x, y);
const c = R.ctx.getImageData(Math.floor(p.x), Math.floor(p.y), 1, 1).data;
return c[0] + c[1] + c[2];
};
out.smooth = [sample(140.5, 140.08), sample(140.5, 142.5)];
const packed = [];
for (let y = 120; y < 121; y += 1 / 6) for (let x = 140; x < 141; x += 1 / 6) packed.push(sample(x, y));
out.texture = Math.max(...packed) - Math.min(...packed);
return out;
}''')
for look in ['pixel', 'thermal']:
edge, inside, loose, solid = ground[look]
check(f'{look}: ground has a darker edge against loose grains', edge < inside * 0.85 and inside == solid, ground[look])
check(f'{look}: loose rubble is lighter than solid stone', loose > inside, ground[look])
check('built solids get no loose-grain outline', ground['built'][0] == ground['built'][1], ground['built'])
check('smooth: a thin darker seam separates ground and rubble', ground['smooth'][0] < ground['smooth'][1] * 0.9, ground['smooth'])
check('smooth: packed rubble keeps pebble shading', ground['texture'] > 30, ground['texture'])
# a browser without hardware drawing gets the pixel look and a note
pg2 = b.new_page(viewport={'width': 800, 'height': 500})
pg2.add_init_script('localStorage.clear();')