Wiki · rules

Rules

Last edited by · ·

Rules

One cog, three machines, sixty metres of track, three times.

The cog is handed a hopper — a single-legged pogo of four links and three powered joints — and told to cover ground. Then a cheetah: seven links, six joints, two legs, no way to fall over and every reason to go fast. Then a walker: seven links, six joints, two legs, and a torso that tips over if it gets ahead of its feet.

Each body starts at the same line, on flat ground, under gravity, with a small seeded wobble in its joints. Each has 19.5 seconds and a 60-metre line. The score is the return: metres covered, plus a small bonus for every tick spent upright, minus a small cost for slamming the actuators.

Nothing about the bodies changes between episodes. What changes is the order: every 1.5 seconds the cog names a gait (stand, crouch, walk, run, bound, brake) and five numbers — cadence, power, lean, stride bias, phase shift — and a deterministic pattern generator in this repo executes it, 240 times a second, until the next order.

The whole game is: read the body you have been given, and tune the gait it needs before it falls over.

The seat

num_agents is 1, always — in both manifest variants and in the certification fixture, always inside game_config. Every episode is a solo run; policies are compared across episodes, never within one.

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

The idea's "no LLM path", resolved

The source idea says "neural-policy coworld only; no LLM path (deliberately)". The platform pin overrides it: every coworld ships an LLM policy and a scripted baseline from day one, in the same image, env-switched.

The tension is resolved by making the seat an LLM dispatcher/controller over a deterministic per-tick physics driver. The LLM does not emit joint torques — nobody can emit six torques 24 times a second over an HTTP round trip. It emits a gait order at bounded decision points (one order every 36 ticks = 1.5 s), and a deterministic, integer-only, in-repo central pattern generator + PD servo turns that order into per-joint target angles and torques every substep, 240 times a second. The scripted baselines drive the identical order interface with a fixed algorithm, so the two policy kinds are strictly comparable and a baseline is legal by construction.

The continuous-torque actuator the idea asks for is real and is what the physics actually integrates; what this repo reinterprets is who chooses its parameters and how often.

The three bodies

morphlinksjointsmasspoints/metreupright bonusends on a fall
hopper4315.49 kg2.000.096 pts/syes — torso below 0.70 m or past 20 deg
cheetah7614.11 kg0.50noneno — it has no upright to lose
walker7623.15 kg1.500.096 pts/syes — torso outside 0.80 .. 2.00 m or past 57 deg

The three points-per-metre numbers are the inverse of how far each body can go, chosen so that a competent run is worth roughly twenty points on every stage and no single morphology decides the episode.

Time

turnTicks        =   36   (1.5 s)   the decision cadence
stageTicks       =  468   (19.5 s)  13 turns of running per stage
resetTicks       =   36   (1.5 s)   the hold after a stage resolves
stagesPerEpisode =    3
maxTurns         =   42
maxTicks         = 1512   (63.0 s)

Turn boundaries live on the global tick grid (t mod 36 == 0) and are never re-aligned when a stage ends early. A hopper that falls at tick 213 is followed by 36 reset ticks and the next stage starts on the tick after that, mid-turn. A fall therefore shortens the episode in both ticks and LLM turns: the episode settles early rather than overruns.

Scoring

Per stage k with morphology m, in micro-points:

distTerm[k]   = (torso.x - xStart) * DistNum[m] / DistDen[m]     # may be NEGATIVE
upright[k]    = UprightPerTick[m] * uprightTicks[k]
ctrl[k]       = ctrlCostAccum[k] / 64
stageReturn[k]= distTerm[k] + upright[k] - ctrl[k]
scores[0]     = sum(stageReturn) / 1e6, rounded to 3 decimals

Higher is better, and the score CAN be negative. There is no floor clamp and no participation term: clamping at zero would make "fell over immediately" and "sprinted the wrong way for a minute" indistinguishable, and the league needs them distinguishable.

results.win[0] is totalReturn >= par — a "did the cog clear the bar" flag, not a duel — and results.winner is 0 when win[0] is true and null otherwise: there is no opponent, so the only honest winner is the seat itself or nobody.

Measured but never scored: bestX, stagePeakSpeed, stageStrides, saturatedTicks, falls, ordersRepaired, fallbackTurns.

Variants

VariantLadderparmax
ladderhopper, cheetah, walker40.0243.744
bipedshopper, walker, walker30.0305.616

End conditions

results.reason is a closed enum — exactly complete, deadline, fault — and results.endRule is ladderComplete | turnCap | wallClock | fault. results.stageOutcome[i] is lined | ran | fell | unreached.

Nothing a player container does can stop the clock: a seat that never connects, disconnects mid-episode or fails every decision is driven by trotter and the ladder runs to its natural end.