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
182 lines
10 KiB
Markdown
182 lines
10 KiB
Markdown
# 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
|
||
|
||
1. Read `NOTES.md`. It is the user's to-do inbox (see "Working from NOTES.md" below).
|
||
2. Read `docs/PROJECT_MAP.md` before searching the code. It says where everything lives. Its
|
||
index lists every top-level function and variable by file.
|
||
3. 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.
|
||
4. Look at the top of `docs/agent-log.md` for 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.md` with 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.md` with the date and the reason they gave.
|
||
|
||
## Commands
|
||
|
||
```sh
|
||
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.py` joins the files listed in `src/manifest.json`, in order, into one classic script
|
||
inside `src/shell.html`. The output is `dist/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`/`let` must 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 of `src/sim/base.js` applies to the whole bundle.
|
||
- **`src/sim/` must never touch the DOM** (no `document`, `window`, canvas or `localStorage`).
|
||
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.py` and 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`; use `tools/where.py` to find the source line
|
||
(`tools/shot.py` does 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; `setCell` already 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 from `rnd()` (seeded) or `mulberry32`.
|
||
Never use `Math.random` there. 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 `NMAT` for materials).
|
||
- New saved fields need a default when an older save lacks them. Migrations go in
|
||
`deserializeWorld` (`src/sim/save.js`).
|
||
- Only raise `SAVE_VERSION` if old saves truly cannot be read any more, and tell the user first.
|
||
- **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
|
||
with `undoNote`. 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, before `NMAT`, and raise `NMAT`);
|
||
- 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_CATS` and an icon in `PIECE_ICONS` (`src/ui/build-menu.js`);
|
||
- hover info (`hoverInfo` in `src/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 (`controlsHtml` in `src/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 `def`s.
|
||
|
||
## 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_*.js` suite. 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 in
|
||
`src/ui/` or `src/render/`, run `python3 test/run_all.py --browser` too.
|
||
- **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/` and `shots/` 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.
|