Files
deepgrain-foundry/tools/playthrough.py
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

70 lines
3.4 KiB
Python

"""Headless browser run: load the page, start a world, play a little, take screenshots."""
import sys, time, pathlib
from playwright.sync_api import sync_playwright
root = pathlib.Path(__file__).resolve().parent.parent
out = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else root / 'shots' / 'playthrough')
out.mkdir(parents=True, exist_ok=True)
url = (root / 'dist' / 'index.html').as_uri()
with sync_playwright() as p:
b = p.chromium.launch(args=['--autoplay-policy=no-user-gesture-required'])
pg = b.new_page(viewport={'width': 1440, 'height': 860})
logs = []
pg.on('console', lambda m: logs.append(f'{m.type}: {m.text}'))
pg.on('pageerror', lambda e: logs.append(f'PAGEERROR: {e}'))
pg.goto(url)
pg.wait_for_timeout(800)
pg.screenshot(path=str(out / '01-title.png'))
pg.click('#btn-new')
pg.fill('#nw-seed', '424242')
pg.click('#btn-generate')
pg.wait_for_function('GAME.running === true', timeout=20000)
pg.wait_for_timeout(1500)
pg.screenshot(path=str(out / '02-spawn.png'))
# dig straight down for a while
pg.mouse.move(720, 560)
pg.mouse.down()
pg.keyboard.down('s')
pg.wait_for_timeout(2500)
pg.keyboard.up('s')
pg.mouse.up()
pg.wait_for_timeout(400)
pg.screenshot(path=str(out / '03-digging.png'))
print('bag', pg.evaluate('bagTotal'), pg.evaluate('JSON.stringify(BAG_ORDER.filter(m=>bag[m]>0).map(m=>[MD[m].name,bag[m]]))'))
# build a coal fire scenario directly through the core API near the bot, then ignite with the tool
pg.evaluate('''() => {
const bx = Math.floor(bot.x)+12, by = Math.floor(bot.y)+3;
bag[COAL] += 300; bag[CUORE] += 120; bag[STONE] += 400; bagTotal += 820;
for (let y=by-14; y<=by+6; y++) for (let x=bx-2; x<=bx+26; x++) setCell(y*W+x, EMPTY, AMB[y], 0);
for (let y=by-12; y<=by+6; y++) { setCell(y*W+bx-2, STONE, AMB[y], 0); setCell(y*W+bx-1, STONE, AMB[y], 0); setCell(y*W+bx+25, STONE, AMB[y], 0); setCell(y*W+bx+26, STONE, AMB[y], 0); }
for (let x=bx-2; x<=bx+26; x++) { if (x < bx+11 || x > bx+13) { setCell((by-12)*W+x, STONE, AMB[by-12], 0); setCell((by-13)*W+x, STONE, AMB[by-13], 0);} setCell((by+6)*W+x, STONE, AMB[by+6], 0); }
const r = mulberry32(4);
for (let y=by-4; y<=by+5; y++) for (let x=bx; x<=bx+24; x++) setCell(y*W+x, r()<0.3 ? CUORE : COAL, AMB[y], 0);
for (let k=0;k<50;k++){ toolIgnite(bx+3, by-5); toolIgnite(bx+20, by-5); simTick(); }
window.__fx = {bx, by};
}''')
pg.wait_for_timeout(9000)
pg.screenshot(path=str(out / '04-furnace.png'))
pg.keyboard.press('h')
pg.wait_for_timeout(300)
pg.screenshot(path=str(out / '05-lens.png'))
pg.keyboard.press('h')
pg.keyboard.press('3')
pg.wait_for_timeout(200)
pg.screenshot(path=str(out / '06-parts.png'))
pg.keyboard.press('n')
pg.wait_for_timeout(300)
pg.screenshot(path=str(out / '07-notebook.png'))
pg.keyboard.press('Escape')
pg.keyboard.press('Escape')
pg.wait_for_timeout(200)
pg.click('#pm-settings')
pg.wait_for_timeout(200)
pg.screenshot(path=str(out / '08-settings.png'))
perf = pg.evaluate('JSON.stringify({frameMs, simMs, fps: fpsSm, view: [R.bw, R.bh]})')
print('perf', perf)
print('molten copper', pg.evaluate('(()=>{let n=0,c=0;for(let i=0;i<N;i++){if(mat[i]===MCU)n++; if(mat[i]===COPPER)c++;} return [n,c]})()'))
print('\n'.join(l for l in logs if 'error' in l.lower() or 'PAGEERROR' in l) or 'no errors')
b.close()