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
10 KiB
Deepgrain Foundry: instructions for coding agents
Deepgrain Foundry is a 2D falling-sand factory game in the style of Sandustry, set in a Terraria-like side-view world. Every grain of sand, ore, water and fire is simulated. The player flies a small bot, digs with a resonance drill, vacuums up loose material, builds furnaces, belts, tubes and machines from blocks, casts metal in molds and sells goods at a trade dock to pay off a land claim. It runs in the browser as one self-contained HTML page, built from small JavaScript files with no dependencies.
The owner (the user) directs the work and is the real playtester. You build, test and report.
Start of every session
- Read
NOTES.md. It is the user's to-do inbox (see "Working from NOTES.md" below). - Read
docs/PROJECT_MAP.mdbefore searching the code. It says where everything lives. Its index lists every top-level function and variable by file. - Before changing how the game plays, read the relevant part of
docs/DESIGN.md. Many choices were made deliberately and are recorded there with the reason. - Look at the top of
docs/agent-log.mdfor what was done recently.
Honesty and accuracy
These rules matter more than speed.
- Do not guess or assume. If you have not checked something, say so. Keep "I tested this and it works" separate from "this should work".
- Prove it with the game. Run the tests. For anything visible, take a screenshot and look at it before saying it looks right (see "Testing"). A passing test only covers what it checks.
- Never invent names. Look a function or variable up in the project map's index, or search
for it (
rg name src), before you use it or talk about it. - When you are not confident, check more than one source. Use the web for anything outside this repository: browser APIs, Playwright, WebGL, real-world materials and temperatures, physics, how other games do something. Confirm important facts with at least two independent sources, and give the links.
- Report problems plainly. Say when a test fails, when something is only partly done, or when you could not reproduce a problem. Never weaken or delete a test just to make it pass. If a test's expectation is outdated because the design changed on purpose, update it and say why.
- Ask when it matters. If a request is unclear and getting it wrong would waste a lot of work, ask first. If the choice is small or easy to undo, take the sensible reading, say which one you took, and carry on.
Working from NOTES.md
NOTES.md belongs to the user. They add notes at any time and ask you to work on them ("do the
notes", "do the belt note"). Work on notes only when asked.
- Kinds of note.
- Tasks ("add...", "make...", "fix...") are built.
- Discussion notes ("let's think about...", "brainstorm...", "what can we do about...", "let's talk about...") get options with trade-offs and a recommendation. Do not build them until the user decides.
- Questions get answers.
- Keep the user's wording. Never rewrite or tidy a note. If you need to add context, add a
sub-bullet that starts with
(agent). - When a task is finished, move the note, word for word, into the "Done, please check"
section at the bottom. Under it, add an
(agent)sub-bullet saying what changed and how to check it in the game. Do not delete it: the user deletes it after checking. If the user says it is not fixed, move it back up and add their feedback as a sub-bullet. - Log your work. Add an entry at the top of
docs/agent-log.mdwith the date, the note, what changed, the files touched, the tests run, and anything left open. - Record decisions. When the user decides something about the design, add it to the
decisions list in
docs/DESIGN.mdwith the date and the reason they gave.
Commands
python3 build.py # build dist/index.html from src/ (takes a second)
python3 -m http.server -d dist # play at http://localhost:8000 (or open dist/index.html)
python3 test/run_all.py # all headless test suites, in parallel (about a minute)
python3 test/run_all.py pipes # only suites whose name contains "pipes"
python3 test/run_all.py --browser # also the browser tests (rebuilds first; about 3 more minutes)
node test/t_pipes.js # one suite
python3 tools/shot.py shots/x.png --setup tools/scenes/clear-sky.js --js "..." --ticks 60
python3 tools/where.py 7614 # which source line is line 7614 of dist/index.html
python3 tools/map.py # regenerate the index in docs/PROJECT_MAP.md
You need Node (tested with 22), Python 3, and Playwright for Python with Chromium
(pip install -r requirements.txt, then python3 -m playwright install chromium). Playwright
officially supports Ubuntu and Debian. On other Linux distributions, such as Arch, its bundled
Chromium usually works; if it does not, launch the system Chromium through Playwright's
executable_path.
How the code is put together
build.pyjoins the files listed insrc/manifest.json, in order, into one classic script insidesrc/shell.html. The output isdist/index.html. There are no modules and no import/export: every file shares one global scope.- Order matters only for code that runs when the page loads. A function can call a function
from any file. But a top-level
const/letmust come before top-level code that uses it. New files go into the manifest in the right place: simulation first, then render, then UI. 'use strict'at the top ofsrc/sim/base.jsapplies to the whole bundle.src/sim/must never touch the DOM (nodocument,window, canvas orlocalStorage). The headless tests load only these files, and keeping the simulation pure keeps a later port to Rust or WebAssembly possible.- Keep files small and on one topic. Aim for under about 400 lines. Split a file that grows
past that along a clear seam, add the new file to the manifest, and rebuild. Then run
python3 tools/map.pyand update the hand-written part of the project map. - Errors point at real files. In Node tests, stack traces name the source file and line. In
the browser they point into
dist/index.html; usetools/where.pyto find the source line (tools/shot.pydoes this for you).
Rules for the simulation
- Performance. The simulation runs 60 ticks a second over millions of cells.
- Use the existing typed arrays.
- Do not allocate objects or arrays inside per-cell loops.
- Only chunks that are awake (32×32 grains) are simulated. After changing cells directly,
wake them;
setCellalready does. - Measure before and after anything in a hot path (
tools/perf.py,tools/experiments/factory_cost.js).
- Determinism. Randomness in
src/sim/comes only fromrnd()(seeded) ormulberry32. Never useMath.randomthere. The headless tests rely on this: the same code gives the same output, line for line. - Save compatibility. The user has saved worlds they care about. Material ids, block codes
(
BP_*), part ids (P_*) and machine type names are stored in saves.- Never renumber or reuse them. Add new ones at the end (raise
NMATfor materials). - New saved fields need a default when an older save lacks them. Migrations go in
deserializeWorld(src/sim/save.js). - Only raise
SAVE_VERSIONif old saves truly cannot be read any more, and tell the user first.
- Never renumber or reuse them. Add new ones at the end (raise
- Undo. Every building action the player takes must be undoable. Wrap it in
undoRecord(label, fn). The functions that place and remove things record their own steps withundoNote. The bot building ghosts is never an undo step of its own.
Checklists
A new material. It needs:
- an id constant in
src/sim/materials.js(at the end, beforeNMAT, and raiseNMAT); - a
def(...)entry with a plain description; - its price in
PRICE(src/sim/economy.js) if it can be sold; - its place in
BAG_ORDER(src/ui/state.js) if it can go in the bag; - its place in
MARKET_GROUPS(src/ui/notebook.js) if it is sold; - any special tables it belongs to:
CASTMETAL,SOLIDMETAL,METAL_TIER,BAR_OF,WALL_OF/WALL_LIST,REFINE,HOTLIQ,DROSS.
Check its colour in both looks.
A new buildable piece. It needs:
- an entry in
PIECES(src/sim/pieces.js) with cost and description; - a block code or machine footprint;
- placing and removing that support undo and Ctrl-replace;
- a tile in
BUILD_CATSand an icon inPIECE_ICONS(src/ui/build-menu.js); - hover info (
hoverInfoinsrc/ui/info.js); - drawing in
src/render/(pixel look, and a smooth-look class if it needs one); - saving and loading of any state it keeps;
- a line in the controls help (
controlsHtmlinsrc/ui/notebook.js) if it adds controls; - a headless test.
Text the player reads (descriptions, toasts, hover info, help):
- plain words and short sentences;
- what it does and how to use it;
- °C for temperatures, "grains" for cells, "blocks" for the 4×4 build grid;
- no programmer words.
Match the existing descriptions in PIECES and the material defs.
Testing
docs/TESTING.md has the details: the harness, how to write a suite, the browser tests,
screenshots and the pitfalls. The essentials:
- Every change to the simulation gets a headless check in a
test/t_*.jssuite. Write the check first when fixing a bug, and see it fail. - Test in the pixel look (the default in the tools). Use the smooth look only when checking graphics, and then check both looks.
- Look at your screenshots. Open the PNG and check it shows what you meant, especially after changing anything drawn.
- Before you say you are finished, run
python3 test/run_all.py. After changing anything insrc/ui/orsrc/render/, runpython3 test/run_all.py --browsertoo. - For a refactor that should not change behaviour, save the suites' output before and after and diff them. The headless suites are deterministic, so any difference is a real change (lines with timings in ms excepted).
Git
- Small commits, one change each, with a message that says what changed for the player or developer. Run the tests first.
dist/andshots/are build output and are not committed.- Never commit secrets: tokens, passwords, or keys.
- Work happens one agent at a time. If running several agents side by side is set up later, this section will say how they coordinate.