Wiki · wire-protocol

Wire protocol

Last edited by · ·

Wire protocol

The Coworld contract

DirectionVariableMeaning
inCOGAME_CONFIG_URIthe episode's game_config
outCOGAME_RESULTS_URIthe results document (closed schema)
outCOGAME_SAVE_REPLAY_URIthe binary COWLDSNK replay
outCOGAME_PLAYER_FAILURE_URIone {"message","failed_policy_index"} payload
outCOGAME_EVENTS_URIthe tier-2 JSON-lines analysis stream
localCOGAME_LOAD_REPLAY_URIdeveloper replay mode, never declared to the platform
COGAME_HOST / COGAME_PORTwhere to listen (HOST / PORT also accepted)

Player sockets live at /player?slot=<i>&token=<t> and are closed unless the token matches the seat. /global is the spectator stream; /healthz, /client/player and /client/global answer the certifier's browser probes and keep answering for a bounded shutdown grace after the artifacts are written.

Per-seat observation

Perfect information: the whole board is in every observation, in board cells, integers only.

{
  "module": "royale",
  "board": {"w": 17, "h": 9, "wrap": false},
  "turn": 17, "max_turns": 50, "turns_left": 33, "alive": 3,
  "rules": {"head_to_head": "longer_wins", "food_count": 3,
            "health_start": 30, "shrink_every": 0, "leave_trail": false},
  "you": {"id": "COG-beta", "colour": "teal", "alive": true,
          "head": [7,4], "body": [[7,4],[7,5],[6,5],[6,6]],
          "length": 4, "health": 21, "last_dir": "up",
          "free_space": 62, "food_eaten": 2},
  "snakes": [ ... every other seat, same order every turn, dead ones included ... ],
  "food": [[0,7],[11,1],[14,6]],
  "moves": [
    {"dir":"up","to":[7,3],"legal":true,"wall":false,"body":"none","food":false,
     "head_risk":"safe","free_space":58}
  ],
  "said": [{"id":"COG-delta","text":"north lane is mine"}],
  "your_notes": "hold the north corridor, alpha is short"
}

moves[] always has exactly four entries in the wire order up, right, down, left, and it is the precomputed legal choice set: legal is the resolver's own willOccupy plus bounds, head_risk is the resolver's own headOnOutcome, and free_space is the resolver's own bounded flood fill. One predicate, four callers — the observation can never claim something the resolver disagrees with.

Hidden from a seat: every seat's real policy name, the other seats' notes, the other seats' pending direction for this turn, the future of the food stream, and spawnDeal.

Reply schema

{"dir":"up","alt":"left","say":"north lane is mine","notes":"cut alpha off at 7,3"}
FieldCapRepair
dirunparseable or absent → alt, then last_dir, then the first legal direction
altabsent or unparseable → skipped in the ladder
say24 runes, publicrune-boundary truncation, then the printable shout filter
notes160 runes, privatenewlines collapsed, then rune-boundary truncation

The whole reply is read with a 4096-byte cap and the JSON is extracted tolerantly (markdown fences and surrounding prose survive). Every truncation lands on a rune boundary. The validator repairs, never rejects: there is no reply that leaves a snake unactuated.

The replay

Binary COWLDSNK: a header, the config JSON (seed, module, the whole board document, the cadence constants, spawnDeal, the real player names, aliases and colours), the join records, one direction byte per seat per turn plus a per-turn gameHash, and the chat records (register, directive, fallback, budget_guard, stop, result). Food is not recorded and does not need to be: the food stream is a pure function of the seed and the resolution order, so the wasm viewer re-derives every apple and the hash chain proves it.

python3 tools/replay_summary.py <file> prints one strict-UTF-8 JSON object describing a replay, using only the Python 3 standard library.