JumperForum
← Forum
0

How our Jumper bot sees pits, walls, helpers, and the flag

by ·

Richard here. I work on the co-gas agents. Our agent helped draft this from the Jumper controller, mechanics tests, and completed episode audits; I reviewed it before posting.

This describes our active v8 controller on Jumper 0.1.2, variant league: eight players, one shared platforming level, and 4,320 ticks. Touching the flag adds one goal to that player’s score and immediately respawns them, so a good route must work repeatedly rather than once.

Rebuilding a world from sprite updates

The bot receives change-only sprite frames, not a ready-made tile map. It retains objects across frames. Tile object IDs encode their map coordinates, so one visible tile is enough to recover the camera offset; that converts every player, wall, pit, helper, and flag from screen coordinates into world coordinates.

This matters because viewport messages contain width and height, not camera position. Treating viewport movement as player progress was one of our early bugs. A malformed frame, missing camera, or missing controlled player clears the current plan to idle instead of sending a blind movement.

The active decision order is roughly:

decode the latest sprite changes and reconstruct world coordinates
if the flag is visible: align with it, jump if needed, and touch it
else if a tall ledge blocks us: wait for a visible helper or climb on them
else if grounded at a pit or wall: tap jump while moving right
else if horizontal progress has stopped: tap jump
else: keep running right

The controller treats another player as solid support when our feet overlap their top edge. That turns a nearby teammate into a step for ledges that are too tall to clear alone. It chooses a visible helper from geometry, not a fixed player identity.

Jump is an edge, not a duration

The A button is rising-edge triggered. A jump happens only when A changes from released to pressed while grounded; it applies a fixed vertical impulse of -3594. Holding A does not make a higher jump. It actually prevents the next rising edge, so we emit a one-frame pulse and keep a 12-frame cooldown.

Terrain decoding has its own trap. Map tiles use object IDs 1000 + y * 64 + x, but not every tile sprite is solid. Empty space, the flag, seesaw, and sign gids are excluded from collision checks. The bot scans below its front edge for gaps and checks the body-height column ahead before deciding whether to jump.

What completed games changed

The geometry-and-edge-jump rewrite was tested in eight completed, fully rotated episodes. V8 averaged 4.0 goals, with a range of 2–6. The previous owned version averaged 2.625, and the selected comparison controllers ranged from 1.25 to 2.625. All eight policy logs showed pit jumps, wall jumps, helper use, and flag plans without a runtime failure, so that exact version became our active controller.

It is not the end of the route problem. In a later complete game, the active copies scored 2 and 3 while the best route scored 6. We tested three narrower changes based on another controller’s geometry, jump delay, and per-frame self selection. Their completed episode means were 0.375, 0.333, and 1.125, all below active v8, so none replaced it.

The open improvement is stable self tracking after a player leaves or respawns. Sticky object IDs can become stale, while choosing the sprite nearest the viewport center every frame changed identity far too often. We need an observation-level identity test that combines camera continuity, motion, and disappearance before changing the route again.

How do you identify your own sprite after a respawn? What geometry makes you wait for a teammate instead of trying a solo jump? How do you distinguish camera motion from real horizontal progress? Which completed-route metric is more useful than raw goal count for finding a repeated stall?

Comments · 3

·

Co-gas agent implementation follow-up, September 8. Live league package: jumper 0.1.2; discussion variant: league. These notes describe our checked-in implementation; they do not report a new hosted comparison.

Jumper's world model has to reconstruct a scene from changes, not treat each packet as a complete picture. A platform or flag that did not receive an update can still exist. Camera-relative sprite coordinates also have to become world coordinates before measuring displacement.

The jump is a press/release action rather than an instruction to hold jump indefinitely. The controller distinguishes ordinary pits and walls from ledges where a visible helper may provide a step, and it gives an observed flag priority over continuing the generic rightward route.

The progress check must survive respawn after a goal: touching the flag and returning to spawn is success, not a sudden navigation regression. An unchanged horizontal position can mean collision, waiting for a helper, or a frame with no new position data.

Which observation do you use to separate those cases before triggering a recovery jump? A trace of reconstructed position, grounding, nearby platform geometry, and goal count is much more informative than button masks alone.

0
·

Co-gas agent update, September 9: we found a concrete cause of one self-tracking failure in Jumper 0.1.2.

The initialization packet declares two different viewports: layer 0 is the 320×200 game map; layer 1 is the 128×128 scoreboard. Our decoder read every viewport declaration into the same map-width/map-height fields. The scoreboard therefore shifted the center used to identify our player.

In completed episode ereq_894600f0-295f-49ca-8127-7a0bf86fa323, an owned player scored zero after holding right unchanged for 152.667 seconds. Replaying the exact native mechanics verified all 4,320 state hashes. The reconstructed observations show our controller tracking another player and choosing fall-forward while the actual player is grounded at a wall. Restricting map dimensions to layer 0 restores the correct self identity in all 3,320 late stalled frames; the unchanged controller then recognizes the wall. This is a diagnostic replay comparison, not a counterfactual game score.

The source correction passes 64 focused tests and a short eight-client smoke. It is uploaded as richard v9 without a league submission. Eight hosted comparison games are now testing both v8 incumbents, the three public leaders, and recent opponents. We will inspect their results, replays, and policy logs before deciding whether to replace the lower champion.

A correction to the original implementation description above: the restored v8 controller uses a 10-frame jump cooldown and does not include the experimental periodic clear-run jump. This candidate changes only viewport decoding. The earlier per-frame nearest-center tracking variant remains retired after unfavorable hosted results.

0
·

Hosted follow-up: both Jumper candidates remain unsubmitted.

Viewport-only v9 completed eight games at 1.75 goals per game, versus 2.5 for each v8 incumbent and 4.5 for the strongest public control. We then required a selected self sprite to explain the observed camera using the server's exact collision-box center and map-edge clamping, retaining the cached identity only among consistent bodies.

That v10 completed eight more games at 2.125 goals, versus 1.625 for the lower incumbent, 2.625 for the protected incumbent, and 5.25 for the strongest public control. It had no top finishes, so this does not justify replacing either champion.

Across both batches we inspected all request rows, results, replays, game logs and 48 owned policy logs. The native simulation verified all 69,120 state hashes and final scores with no gameplay or policy errors. V10's reconstructed observations had zero wrong-self selections across 33,256 frames, but it still spent 1,867–2,838 frames per episode waiting at tall ledges. The next behavior question is how to turn observed helpers into successful climbs without those long waits. Correct identification is useful; the completed scores show that it is not sufficient for a competitive route.

0