Cogriculture
Last edited by · ·
Cogriculture
A Coworld cogame of Kaggle's Kaggriculture: two farmers work adjacent 10×10 farms for a 30-day season, growing crops, raising livestock, hiring help, and trading into a market whose prices move with what everyone sells. Most money in the bank at the end wins.
The simulation is not a clone — it is the upstream simulator. Cogriculture
vendors kaggriculture.py from a pinned kaggle-environments release and runs
it unmodified. There is no reimplementation to drift, no rule transcribed twice.
See vendor/UPSTREAM.md.
Bots are portable in both directions. A Cogriculture bot is a plain Kaggle
agent — agent(obs, config) -> action_dict — and the observation it receives
over the wire is byte-for-byte the observation Kaggle would hand it. The same
file submits to Kaggle and competes in a Softmax league.
See docs/KAGGLE_SUBMISSION.md.
The authoritative ruleset is docs/RULES.md (upstream's own
README, vendored alongside the code it documents).
Rules at a glance
- 2 players, 720 turns — 24 turns a day for 30 days. One action per unit per turn; up to 10 market orders per turn.
- Grow things. Wheat, carrot, tomato, strawberry and melon differ in seed cost, time to yield, and whether they yield once or keep producing. Every plant must be watered daily — miss two days running and the tile becomes a weed. A tile planted today is already one day from that, so plant and water in the same day.
- Or raise animals. Geese, cows and sheep need a coop or pasture, daily wheat feed, and produce eggs, milk and wool. Miss two feeds and they escape for good.
- Hire help. A farm hand costs
fib(n)for the n-th hire that day — 1, 1, 2, 3, 5, 8 — and gives you a whole extra unit's worth of actions until nightfall. - Sell into a live market. Every product's price moves with market inventory: it climbs as the town's shops consume stock and falls as players sell. Each product has its own curve, and the premium ones (strawberry, melon, milk, wool) collapse to the $1 floor on a modest glut.
- Buy land. Each farm starts on one 5×5 quadrant; the other three cost $1,000, $2,000 and $4,000.
Quick start
Run an episode in-process — fastest loop for bot work:
python tools/eval.py bots/baseline.py bots/starter.py --episodes 3 --diagnose
Run one over the real websocket protocol, exactly as a Coworld episode does:
python tools/run_local_episode.py --steps 720 --serve
That prints a live view at /client/global and, with --serve, a replay at
/client/replay rendered by Kaggle's own visualizer.
Run the tests:
python -m pytest tests/ -q
Variants and wild-market climates
The default variant is the Kaggle competition exactly. The wild-market-*
variants run the same unmodified simulator on a different market: a named
"climate" of marketParams overrides (glut, scarcity, inverted) or a
market sampled per episode from the seed (random). A bot that hard-codes the
default price table is wrong from turn one; a bot that reads market.params
and the price track adapts. See docs/VARIANTS.md.
python tools/eval.py bots/baseline.py bots/starter.py --climate random --episodes 5
Writing a bot
A bot is one self-contained file with a module-level agent function:
def agent(obs, config):
farm = obs["farms"][obs["player"]]
return {"farmer": ["WATER"], "hands": [], "market": [["SELL", "CARROT", 3]]}
Point the player adapter at it:
python -m cogriculture.player --bot path/to/mybot.py
Two are bundled:
| Bot | What it is |
|---|---|
bots/starter.py | Upstream's starter_agent — a one-tile carrot loop. Weak on purpose; a fixed reference point. |
bots/baseline.py | The shipped baseline: zoned unit assignment, staged crop portfolio, rate-limited selling. Scores ~6× starter. |
bots/baseline.py opens with the reasoning behind its strategy — worth reading
before writing your own, if only to disagree with it.
Layout
vendor/kaggle_environments/ the upstream simulator, byte-for-byte (see UPSTREAM.md)
cogriculture/sim.py the one place that decides which simulator we run
cogriculture/climates.py wild-market climates: marketParams sets for the same simulator
cogriculture/server.py Coworld game runnable: websockets around env.step()
cogriculture/player.py Coworld player runnable: websockets around a Kaggle agent
cogriculture/client/ live viewer, admin, player docs
bots/ plain Kaggle agents, portable to Kaggle unchanged
tests/ vendor identity + upstream parity
tools/ vendoring, golden recording, eval, local episodes
How identity is enforced
Three layers, because "we run upstream" is a claim that has to stay true:
- Vendored bytes. Everything under
vendor/kaggle_environments/is upstream's, with one 40-line__init__.pyof ours that registers a single environment instead of all 24 (upstream's version drags in jax, open_spiel and transformers, which kaggriculture does not use). tests/test_vendor_identity.pydownloads the pinned wheel and asserts every vendored file matches it byte-for-byte, that our__init__.pyis the only deviation, and that it contains no game logic.tests/test_parity.pyreplays six scenarios — including a full 720-turn season — against goldens recorded from a real installedkaggle-environments, comparing a full-state hash at every step.
Upstream bumps are a re-vendor plus a golden re-record, never a port:
python tools/vendor_upstream.py --version <new-version>
python tools/record_parity_goldens.py
python -m pytest tests/ -q
Known characteristics
- Replays are gzipped episode JSON — the artifact is
env.toJSON(), the Kaggle episode format (which re-states every agent's full observation at every step, ~12 MB raw for a full season), stored gzip-compressed at ~180 KB, with the platform-supplied player names recorded asinfo.TeamNamesso the visualizer can label players.gunzipof the artifact is byte-for-byte a real kaggriculture episode; readers sniff the gzip magic, so pre-compression replays (plain JSON) still load. Seecogriculture/artifacts.py. - Replays play in a static viewer, not a game container. The manifest
declares
game.replay_viewer.bundle, so Observatory watches a replay by opening a static bundle — upstream's own visualizer split into an immutable-cacheableapp.js(~0.6 MB of code, with the inlined 1024px sprite PNGs extracted to content-addressedsprites/*.webp, ~90% smaller and fetched in parallel) plus a small loaderindex.html— with the replay URL in?replay=. No Kubernetes job, no image pull, no proxied ~26 MB page.tools/build_replay_viewer.shbuilds the bundle (make replay-viewerlocally); the game image's/client/replayremains as the local-dev and fallback path. - Episodes are fast — a full 720-turn season with rule-based bots runs in
about 4 seconds wall-clock.
actTimeout(default 1 s, matching Kaggle) is the ceiling that matters for slower or LLM-backed players: 720 turns × 1 s is ~12 minutes worst case. - Score scale is unbounded.
scoreModedefaults tomoney— literally the Kaggle reward — and can be set towinlossfor ±1 scoring if a ladder's mean-score ranking proves noisy against a strategy-dependent money scale.