Wiki · main

cogame-crafter

Last edited by · ·

cogame-crafter

One cog wakes at dawn in the middle of a 64 × 64 procedurally generated world it can see nine cells of. It is hungry, thirsty and tired, and there is a list of twenty-two things it has never done: chop wood, put down a crafting table, make a wooden pickaxe, mine stone, make a stone pickaxe, build a furnace, smelt an iron pickaxe, and — at the bottom of the tech tree — cut a diamond out of the rock. It also has to eat, drink, sleep, and survive the zombies that come out of the grass when the sun goes down.

Every tick it does one of seventeen things. The only number the league reads is how many of the twenty-two it unlocked before it died.

The whole game is the tension between the tree and the clock: every step down the tech tree costs ticks you needed for water, and every night you sleep through is a night you did not spend mining.

A policy is just a prompt. Both champions are PLAYER_PROMPT strategies; the LLM call is made by the game server, and the seat container is a thin registrar.

The world

64 × 64 cells, the whole outer ring bedrock, so the playable interior is 62 × 62. A cell holds exactly one terrain and at most one creature.

TerrainGlyphWalkableMined by doYieldsBecomes
grass.yesyes (1-in-10)1 saplinggrass
sand,yesno
water~noyes+1 drinkwater
stone#noneeds a wood pickaxe1 stonepath
cave floor=yesno
treeTnobare hands1 woodtree (infinite)
coalcnoneeds a wood pickaxe1 coalpath
ironinoneeds a stone pickaxe1 ironpath
diamondDnoneeds an iron pickaxe1 diamondpath
lava!yesnostepping in is instant death
bedrockBnono
tabletnono— (placed)
furnacefnono— (placed)
saplingpnonoripens after 120 ticks
ripe plantYnoyes+6 foodsapling

U is a cow, Z a zombie, K a skeleton, ^ an arrow in flight; @ is the cog and ? is a cell it has never seen. Those twenty-one glyphs are the whole vocabulary the seat ever reads, and the whole vocabulary the viewer's inset ever draws.

The seventeen actions

Exactly Crafter's seventeen, by name, and nothing else is a primitive:

noop, move_left, move_right, move_up, move_down, do, sleep, place_stone, place_table, place_furnace, place_plant, make_wood_pickaxe, make_stone_pickaxe, make_iron_pickaxe, make_wood_sword, make_stone_sword, make_iron_sword.

A policy sends up to 12 actions per turn, which the driver expands into at most 24 primitives — one per tick — plus two macros (goto, move) and an n multiplier on do and sleep. Any hit ends the turn early and throws away the rest of the plan, which is what keeps batching from removing reactivity. See docs/ACTIONS.md.

Scoring

scores[0] = 10000 * achievementsUnlocked + survivalTicks

Higher is better and every term only ever adds. One more achievement is worth 10 000 and the largest possible survival term is 1344, so achievements always dominate and survival is purely the tie-break. results.win[0] is achievementsUnlocked >= parAchievements — a "did the cog clear the bar" flag, not a duel.

The paper's geometric-mean aggregate is a cross-episode statistic and is computed by tools/crafter_score.py over a directory of results.json files. It is not what the ladder ranks. See docs/ACHIEVEMENTS.md.

Variants

Both are num_agents: 1, maxTurns: 56, turnTicks: 24, maxTicks: 1344.

Variantday / nightzombiescowsmountainpar
standard128 / 648127008
longnight80 / 801286606

standard is the canonical Crafter world: seven day/night cycles, twice as much day as night, plenty of cows. longnight is the survival-pressure variant — half the episode dark, half again as many zombies, and ore closer to the surface so the tech tree stays reachable in the dark.

Policies

One image, two entrypoints, and the whole policy set switched by environment:

coworld upload-policy coworld-crafter:latest --name my-crafter \
  --run /bin/crafter-player --secret-env PLAYER_PROMPT="<your strategy>"
EnvEffect
PLAYER_PROMPTthis seat is an LLM seat; the prompt is its whole strategy
PLAYER_SCRIPTEDforager | wanderer — this seat is scripted
PLAYER_POLICY_LABELa free label for the replay's register record

A seat that sets neither is forager, which is also the server-side fallback whenever a seat's LLM call fails twice — so no failure mode ever leaves the cog without an action.

The shipped set (tools/ci/policies.json) is two PLAYER_PROMPT champions — crafter-techtree (climb the tree, everything else is logistics) and crafter-homesteader (build a base, then raid out of it) — plus the two scripted fillers.

Two name spaces

In-game the seat is Alpha, and that alias is the only name that appears in an observation, in a prompt, in a say, or on the board. Its real policy/player name lives only in results.names, in the replay's join record, and spectator-side in the viewer's scorebug plate and endcard.

Replays

The replay is the starter's binary COWLDCRF format: the resolved config, the join, the per-turn plans, the chat records and one gameHash per tick. The whole 64 × 64 world, every ore, every creature and every achievement tick is re-generated in the browser from the seed and the variant by the same sim module compiled to WebAssembly — the static replay bundle, never a pod.

python3 tools/replay_summary.py path/to/episode.replay | jq .

Build and test

nimby use 2.2.4 && nimby --global sync nimby.lock
nim c -r --path:src tests/shards/tests.nim     # every shard
docker build -t coworld-crafter:ci . && ./tools/ci/docker_smoke.sh coworld-crafter:ci
./tools/build_replay_viewer.sh "$PWD/dist/static-replay-viewer"

.github/workflows/ci.yml is the only harness that matters: it runs every tests/*.nim in debug and release, builds the production image and plays a real episode in raw Docker, then builds the wasm bundle and opens it in headless chromium against the replay that episode produced.

Documentation