Improve a robot with local trials
Play the same opponent, seed and ruleset before and after a change. Inspect the first decision where behaviour diverges, then repeat across several seeds and both seat orders. Keep a working baseline so a faster run or one win does not hide a control, protocol or startup regression.
For a small standalone project, start with the player kit:
clone your language’s starter, then run make match and make replay. It
uses the shared @clunkyard/cli npm package and downloads only the common
engine and selected SDK. Without Make, use
npx @clunkyard/cli match 42 2d classic and
npx @clunkyard/cli replay replays/trial-42.json.
Use npx @clunkyard/cli for current tooling and record --version with your source commit when comparing trials.
The commands below are for contributors working from the full game repository.
JavaScript and TypeScript projects use the npm SDK under the clunkyard import
name, which also works in hosted builds. Use the language starter's make match and make replay
for its engine and launcher. The npm package also exposes play(robot, lines)
for tests driven by recorded protocol messages; a real match exercises the
engine, opponent and robot together.
There are two repository routes:
| Route | Use it for | Requirements |
|---|---|---|
node src/cli.js match |
Fast iteration, saved replays and custom opponents | Node and the selected robots' local SDKs/toolchains |
node scripts/check-starters.js --house PROJECT |
Building a user project through the hosted profiles and checking its sandbox limits | Linux Docker, matching builder/runtime images, Node |
Run the commands below from the repository root. Local CLI programs are trusted programs on your computer. The Docker route uses the hosted supervisor/relay, fixed profile build recipes and isolated runtime.
Start with a real match
This match needs only Node and the checked-in DSL core:
node src/cli.js match \
--bot @bots/dsl/vane/bot.json --bot @bots/dsl/cadence/bot.json \
--mode 2d --ruleset classic --seed 42 --ticks 240 \
--replay replays/trial-42.json
node src/replay-tool.js inspect replays/trial-42.json
node src/cli.js replay replays/trial-42.json
Open the replay URL printed by the last command. Pause and step through contacts, aim, scanner direction, battery and accepted controls. Replaying uses recorded states and does not rerun the robots. See replay inspection and export for archives and video.
A downloaded workbench project can replace either manifest:
node src/cli.js match \
--bot @/absolute/path/scout/bot.json \
--bot @/absolute/path/baseline/bot.json \
--mode 3d --ruleset zero-g --seed 42 --ticks 360 \
--replay replays/scout-zero-g-42.json
The local runner builds workbench manifests with language and entry for
JavaScript, TypeScript, Python, C, Go, Rust, Java, Vane, Cadence and Glyph.
It uses the installed SDK at /opt/clunkyard/sdk; set CLUNKYARD_SDK for
another prefix. Compiled output goes in .clunkyard/ beside the manifest.
Use node src/cli.js roster to see the current House pack.
For another profile, build and launch it using its binding guide.
A local manifest can supply explicit build and command arrays; these
trusted local commands run with the repository root as their working
directory, so use absolute project paths. An already built executable needs
only a command. For example:
{
"name": "Scout native",
"language": "cpp",
"protocol": 2,
"command": ["/absolute/path/scout/bot"],
"supportedModes": ["2d", "3d"],
"supportedRulesets": ["classic", "inertial-2d", "gravity-flat", "gravity-hills", "zero-g"]
}
The CLI also accepts --bot 'Scout=/absolute/path/scout/bot' for a quick
local command. A saved manifest is easier to repeat and declares supported
rulesets explicitly. Hosted builds use the selected language profile's
recipe rather than executing a manifest's arbitrary build command.
Exercise a user project through the hosted profiles
Use this route for every hosted language, including adapters, compiled
languages, DSLs and ABI 4 guests. Keep the entire project under one directory
with bot.json, its entry and any dependencies included as source. Keep
generated executables and local build files under .clunkyard/ or build/;
the checker omits these directories, .git/, __pycache__/ and
node_modules/ from the bundle. Ordinary files still count toward source
file/size quotas, so move a loose executable out of the source root before
testing. The builder has no network access.
Build the images for your checkout with make images, or select an existing
matching release pair:
node scripts/check-starters.js --house /absolute/path/scout \
--builder r.k3s.ca/slepp/clunkyard-builder:RELEASE_TAG \
--runtime r.k3s.ca/slepp/clunkyard-runtime:RELEASE_TAG \
--rulesets all --seed 42
Replace RELEASE_TAG with an immutable release tag present in local Docker.
With no image arguments, the script uses the current checkout's Git commit
tag, as produced by make images. Both images must already be available.
Use matching images so their installed bindings and runtime libraries agree.
Despite its --house name, this option accepts any project directory. It
builds the project's actual files through sandbox/worker.js and plays it
against the JavaScript starter. --rulesets all exercises every compatible
mode/ruleset the manifest declares; without it, the script checks classic
2D and 3D where supported. It does not accept a custom opponent, replay
output path or tick limit.
A pass requires successful builds, no match failures, at least one shot in
each match and resource use below half the checked limits. A peaceful probe
can therefore fail “never fired” while still speaking valid protocol. Read
FAIL details and both pod logs to separate robot failures from platform
startup failures; a referee-origin startup timeout is an infrastructure
failure, not evidence that the tactic exceeded its CPU budget. The script
exits non-zero if any project fails.
This check reports CPU and own time for Standard, operations for DSL and fuel for WebAssembly. Wall time is printed for information. The frozen Season 3 rates were calibrated for the arena's hardware; a different local CPU or runtime environment can change headroom. Local Docker checks are useful compatibility and regression tests. Use arena trials to confirm competitive headroom under its current limits.
Repeat a comparison
Keep your baseline and candidate in different directories or at immutable build paths. Choose a seed list before comparing, change one tactic at a time and keep the mode, ruleset, objective and tick limit fixed:
node src/cli.js match --bot @/absolute/path/baseline/bot.json \
--bot @/absolute/path/candidate/bot.json \
--mode 2d --ruleset inertial-2d --seed 7 --ticks 360 \
--replay replays/baseline-first-7.json
node src/cli.js match --bot @/absolute/path/candidate/bot.json \
--bot @/absolute/path/baseline/bot.json \
--mode 2d --ruleset inertial-2d --seed 7 --ticks 360 \
--replay replays/candidate-first-7.json
Repeat with several seeds and opponents. Reverse seat order for each seed because starting positions and robot identity can affect the result. A fixed seed reproduces the engine's scenario; reproducibility also requires the same code, configuration and any tactic randomness. Use the SDK's randomness API where available and control any external randomness yourself.
For a round robin, node src/cli.js tournament --bot @... --bot @... --rounds 3 --seed 1000 --out replays/my-tournament saves a summary and one
JSON replay per heat. Tournament seeds advance by heat; this is different
from the paired same-seed comparison above. The local spectator's
Compare revisions tool runs two installed revisions
against a chosen opponent with balanced seat orders and archives their
identities, statistics and replays.
A useful edit-run loop is:
- Record the baseline's failures, shots, hits, damage and result on your chosen seeds. Keep its source and build identity.
- Change one decision, such as scan width or reserve battery.
- Run the same fixtures or memory-session tests for protocol/API mistakes, then replay the same real matches. A fixture smoke test is not a match.
- Inspect the first unwanted turn: was the contact absent, the gun cooling, the shot unreachable, or a control rejected? Log a short status/value or write diagnostics to stderr.
- Keep changes that improve the intended behaviour across the trial set, then run the hosted-profile check before uploading or rebuilding in the arena.
Default CLI matches use --resources open; they are convenient functional
trials and do not reproduce hosted Standard cgroup accounting. --resources trusted is a local resource-reporting mode, not a substitute for the
hosted supervisor. The resource guide explains the classes
and measurements. Replay outcomes alone do not measure CPU headroom.
These commands are implemented by the CLI, manifest resolution and the hosted-project checker.