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 b270c4e818
97 changed files with 11710 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Run the test suites and report.
python3 test/run_all.py every headless suite (test/t_*.js), in parallel
python3 test/run_all.py pipes stone only suites whose name contains one of these words
python3 test/run_all.py --browser also the browser tests (play.py, then t_smooth.py),
after rebuilding dist/; they run one at a time
python3 test/run_all.py --browser-only
Exit status 0 only if everything passed. Failing checks are printed with their suite.
The headless suites are deterministic: the same code gives the same output, line for line, so
saving the output before and after a refactor and diffing it proves nothing changed.
"""
import concurrent.futures as cf, pathlib, subprocess, sys, time
ROOT = pathlib.Path(__file__).resolve().parent.parent
TEST = ROOT / 'test'
def run_node(path):
t0 = time.time()
p = subprocess.run(['node', str(path)], cwd=ROOT, capture_output=True, text=True, timeout=1800)
return path.stem, p.returncode, p.stdout + p.stderr, time.time() - t0
def run_py(name):
t0 = time.time()
p = subprocess.run([sys.executable, str(TEST / name)], cwd=ROOT, capture_output=True, text=True, timeout=1800)
return name, p.returncode, p.stdout + p.stderr, time.time() - t0
def main():
args = [a for a in sys.argv[1:] if not a.startswith('--')]
browser = '--browser' in sys.argv or '--browser-only' in sys.argv
node = '--browser-only' not in sys.argv
results = []
if node:
suites = sorted(TEST.glob('t_*.js'))
if args: suites = [s for s in suites if any(a in s.stem for a in args)]
with cf.ThreadPoolExecutor(max_workers=8) as ex:
for r in ex.map(run_node, suites): results.append(r)
if browser:
subprocess.run([sys.executable, str(ROOT / 'build.py')], cwd=ROOT, check=True)
# one at a time: two headless browsers at once can starve each other and flake
for name in ['play.py', 't_smooth.py']: results.append(run_py(name))
bad = 0
for name, code, out, secs in results:
fails = [l for l in out.splitlines() if l.startswith('FAIL') or 'Error' in l or l.startswith('FAILS: [') and l != 'FAILS: []']
passes = sum(1 for l in out.splitlines() if l.startswith('PASS'))
ok = code == 0 and not [l for l in fails if l.startswith('FAIL ') or 'Error' in l]
bad += not ok
print(f"{'ok ' if ok else 'FAIL'} {name:22s} {passes:3d} checks passed {secs:5.1f}s")
if not ok:
for l in (fails or out.splitlines()[-15:]): print(' ' + l)
print('ALL PASSED' if not bad else f'{bad} SUITE(S) FAILED')
sys.exit(1 if bad else 0)
if __name__ == '__main__':
main()