Wiki · main

cogame-minigrid

Last edited by · ·

cogame-minigrid

Four cogs, each alone in its own private 13 × 13 walled gridworld it can only see 7 × 7 of — all four racing the SAME seeded gauntlet at the same moment. On the screen is a sentence: "use the yellow key to open the door and then get to the green goal square", or "put the red ball next to the blue box", or — in the XLand variant — "make a purple box" with no explanation of how purple boxes come to exist.

A cog turns, walks, picks things up, opens doors and pushes objects together. Lava kills it. Grey obstacle balls kill it if it walks into one. An episode is a gauntlet of five phases, each on its own seeded layout with its own sentence and its own six-turn window. The only number the league reads is how many of the five that lane solved.

Four isolated lanes

num_agents is 4. Seat s plays a complete PRIVATE instance of the same gauntlet: every generator draw is mix64(seed, phaseIndex, salt) and the lane index is deliberately not an input, so all four lanes get byte-identical layouts, mission sentences and hidden rule tables. Nothing crosses a lane — not a position, not a say, not even a scoreboard — so a cog cannot tell whether it is racing three LLMs, three baselines or a mix, and scores[i] compare directly.

Phase boundaries are synchronised: every lane starts phase k on the same turn, and a lane that resolves early idles until the boundary. That is what makes the quad a race on the same board at the same moment rather than four unrelated timelines.

seataliascolourquadrant
0Alpharedtop-left
1Betabluetop-right
2Gammagreenbottom-left
3Deltayellowbottom-right

The whole game is the gap between the sentence and the 7 × 7 window: you are told what to do and shown almost nothing, and every turn you spend looking is a turn you did not spend doing.

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 board

13 × 13 cells, the whole border ring wall, so the playable interior is 11 × 11 = 121 cells. A cell holds at most one thing:

ContentGlyphPassableSees behind
empty floor.yesyes
wall#nono
lava~yes — entering it ends the taskyes
goal squareGyesyes
key / ball / boxk o bnoyes
door, open / closed / lockedD d Lopen onlyopen only

Colours are the six MiniGrid colours: red green blue purple yellow grey.

The seven task families

lavagap, doorkey, multiroom, keycorridor, dynamic, babyai and xland. Two variants ship:

VariantLadder (in order)par
gauntletlavagap, doorkey, multiroom, keycorridor, babyai3
xlanddynamic, xland, xland, xland, babyai2

Scoring

scores[i] = 100_000 × tasksSolved[i]     (0 … 5)
          +   1_000 × progressTotal[i]   (0 … 15, the named subgoal credits)
          +      10 × speedTotal[i]      (0 … 25, turns saved on solved phases)

Higher is better and every term only ever adds. The ordering is strictly lexicographic by construction: 1_000×15 + 10×25 = 15_250 < 100_000, and 10×25 = 250 < 1_000. Maximum 515 250; minimum 0.

results.winner is the seat with the strictly highest score; an exact tie in scores is an exact tie in all three components — a genuine draw — so winner is null and results.tied is true.

Playing it

The seat sends one registration blob and then only listens; every decision happens in the game server.

coworld upload-policy coworld-minigrid:latest --name my-minigrid \
  --run /bin/minigrid-player \
  --secret-env PLAYER_PROMPT="Map first, then act, and never lose what you learned…"

PLAYER_SCRIPTED=scout|bumper selects a published scripted baseline instead. A seat that sets neither plays scout.

Full rules: docs/RULES.md. The reply format: docs/ACTIONS.md. What this is and is not a port of: docs/PORTING-MINIGRID.md. The wire contract: docs/PROTOCOL.md. The design note this repo implements: docs/plans/2026-08-28-minigrid-design.md.

Building

docker compose build                      # the one image, two entrypoints
nim c -r tests/shards/tests.nim           # the test suite, from the repo root
tools/build_replay_viewer.sh "$PWD/dist/static-replay-viewer"

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

Forked from Metta-AI/coworld-ctf (paintbot) — its broadcast chrome, its replay codec, its LLM transport and its build wiring are this repo's, retargeted.