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
This commit is contained in:
2026-10-23 16:05:31 -04:00
co-authored by Claude Opus 5.5
commit 3f971a94c2
100 changed files with 11928 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Build Deepgrain Foundry into one self-contained HTML file.
The source files listed in src/manifest.json are joined in order (sim first, then the browser
code) into one classic script, so they share one global scope and 'use strict' at the top of
src/sim/base.js applies to all of it. No bundler, no dependencies.
Writes:
dist/index.html the full page: open it, or serve dist/ with python -m http.server
dist/deepgrain-foundry.html the same without <!doctype>/<head>/<body>, for hosts (like a
claude.ai artifact) that wrap the page themselves
dist/bundle-lines.json where each source file starts in dist/index.html, so a line number
from a browser error can be traced back: python3 tools/where.py LINE
"""
import json, pathlib
root = pathlib.Path(__file__).resolve().parent
src = root / 'src'
dist = root / 'dist'
man = json.loads((src / 'manifest.json').read_text())
def join(files):
"""Join files with one newline between them; return the text and [first line, file] pairs."""
parts, starts, line = [], [], 1
for f in files:
text = (src / f).read_text().rstrip('\n')
starts.append([line, f])
parts.append(text)
line += text.count('\n') + 1
return '\n'.join(parts), starts
def main():
shell = (src / man['shell']).read_text()
css, _ = join(man['css'])
js, starts = join(man['sim'] + man['client'])
head, tail = shell.split('//__JS__')
body = head.replace('/*__CSS__*/', css)
page = body + js + tail
pre = ('<!doctype html><html><head><meta charset="utf-8">'
'<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">'
'</head><body>')
full = pre + page + '</body></html>\n'
dist.mkdir(exist_ok=True)
(dist / 'deepgrain-foundry.html').write_text(page)
(dist / 'index.html').write_text(full)
# the script starts on this line of index.html (the doctype prefix has no newlines)
js_line = body.count('\n') + 1
(dist / 'bundle-lines.json').write_text(json.dumps(
{'page': 'dist/index.html', 'files': [[js_line + s - 1, f] for s, f in starts]}, indent=1) + '\n')
print(f'built dist/index.html ({len(full):,} bytes, {len(starts)} source files)')
if __name__ == '__main__':
main()