Design the game
Settle the scoring, information, timing, failure, and seed contracts before writing the container.
A Coworld exists to help someone improve a player. Design the evidence for that loop before designing the package.
Start with a short document that answers the questions below. These choices flow into your protocol, schemas, variants, certification fixture, and league configuration.
Choose the player runtime before fixing the transport or packaging design. For game-hosted players, define file validation, execution budgets, isolation, and failure handling alongside the rules. Connection deadlines below apply to WebSocket clients; file execution needs equivalent bounded startup and action deadlines.
Define the seats
Decide how many players an episode supports. A game may use a fixed count or a bounded range.
For every seat, define:
- what the player observes;
- which actions are legal at each decision point;
- whether the seat has a distinct role, team, spawn, or objective;
- how a missing or broken player affects the game; and
- whether seat order changes the meaning of a score.
The supported range later becomes the minItems and maxItems bounds on the manifest’s runner-injected tokens
array. The concrete roster comes from the selected variant, certification fixture, or hosted scheduler.
Choose a useful score
Every successful episode returns one numeric score per slot in results.scores. Define what that number means before
you implement it.
A useful score separates better play from worse play. A policy author should be able to inspect comparable episodes and understand whether a change helped.
Ask:
- Does the score reward the behavior the game is meant to teach?
- Can a legal but inactive policy earn the same score as a competent policy?
- Does seat position or role create predictable score differences?
- Which additional result fields would make the score easier to interpret?
Keep extra metrics in the game’s results schema. Cross-Coworld tooling uses scores; game-specific analysis can use
the additional fields.
Draw the information boundaries
Write down what each audience may see:
| Audience | Surface | Questions to answer |
|---|---|---|
| One player seat | /player and its browser client | Which state is private to this seat? |
| Live spectator | /global and the global viewer | Can spectators see hidden roles or future information? |
| Replay viewer | Replay data and viewer | Should the replay reveal more than a live spectator? |
| Episode consumer | Results and logs | Which facts are safe to retain after the episode? |
Treat these as data boundaries, not presentation choices. Do not send hidden state to a client and rely on its user interface to conceal it.
If players can derive hidden state from a public seed, a fresh seed is part of the security model.
Bound time and failure
Choose the turn model and the fallback behavior together.
- Is play sequential, simultaneous, or continuously ticked?
- How long may players take to connect?
- How long may one action take?
- Can a player retry an invalid action?
- What legal action replaces a timeout, disconnect, or exhausted retry?
- Can the game finish when a player never connects?
Every wait needs a bound. A failed seat should degrade to a documented legal action instead of hanging the episode.
An illegal action is a player failure. Explain the problem, allow a bounded retry when useful, then apply the fallback. Do not crash the game or silently accept an illegal move.
Make seeded runs reproducible
Use one invariant throughout the engine:
The initial state is a pure function of the seed. Each next state is a pure function of the current state and actions.
Route all randomness through a pseudorandom number generator initialized from the game config. Do not mix in wall-clock time or ambient randomness after initialization.
Keep the seed optional:
- With a seed, the same action trajectory must reproduce the same episode.
- Without a seed, the game must mint a fresh random seed.
Do not turn an absent seed into "undefined", an empty string, or a constant. Those values make every unseeded episode
use the same board while appearing healthy.
Add engine tests for both sides of the contract. A fixed seed should reproduce. Several unseeded starts should vary.
Plan the baseline and replay
Define an always-legal baseline action for the game host. Then plan a simple bundled player that uses it correctly. The baseline gives certification a stable client and gives player authors a performance floor.
Also decide what the replay must preserve. A useful replay should make the score explainable without requiring the original live process.
At minimum, test that:
- the same replay bytes produce the same visible sequence;
- playback begins without manual setup;
- playback reaches the end and can restart; and
- hidden information follows the boundary you designed.
Design review checklist
- The supported seat count or range is explicit.
- Each seat’s observation and legal actions are defined.
-
results.scoreshas a clear interpretation. - Player, spectator, replay, and log visibility are documented.
- Connect, action, retry, and episode waits are bounded.
- Every player failure has a legal fallback.
- Seeded runs reproduce and unseeded runs vary.
- A scripted baseline and replay format are planned.