181 lines
10 KiB
Markdown
181 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. Run `kandown work`. It prints the user's task board and the rules for working it (see
|
||
"Working from the task board" 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 the task board
|
||
|
||
The user's to-do list is a kandown board: one Markdown file per task in `tasks/`. **Read
|
||
`docs/kandown.md` before using it**: it says how the board works here, which commands are safe
|
||
(never run `kandown <command> --help`: it runs the command), and what a task file looks like.
|
||
The rules for working the board are in `.kandown/kandown_work.md`, and `kandown work` prints
|
||
them. In short:
|
||
|
||
- Work on tasks only when asked. Todo tasks are built; Backlog tasks tagged `discussion` get
|
||
options with trade-offs and a recommendation, and nothing is built until the user decides.
|
||
- Never reword the user's words in a task's `## Context`. Add your notes as subtasks with
|
||
`report:` lines, or in the `report` field.
|
||
- When a task is finished, move it to **Review** with a `report` saying what changed and how to
|
||
check it in the game. Only the user moves tasks to Done.
|
||
- **Log your work.** Add an entry at the top of `docs/agent-log.md` with the date, the task,
|
||
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 tools/serve.py # build, serve at http://localhost:8000, rebuild on src/ changes
|
||
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) and Python 3. Playwright for Python and its Chromium live in
|
||
`.venv/` in the repo (not committed); set up as in README.md → Develop. The browser scripts and
|
||
`tools/shot.py` switch to `.venv` by themselves (`tools/use_venv.py`), so the commands above work
|
||
with plain `python3`.
|
||
|
||
## 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.
|
||
|
||
This project uses Kandown. Before task work, run `kandown work` and follow its output. <!-- kandown:agent-ref -->
|