Let a swimming bot pick a node it can actually swim to - #248
Merged
Conversation
The e1m2 companion spent a whole session floating in one cell. Nothing
in the nav layer knew that a bot in water cannot reach a seat high above
it, so it kept steering at one and swam at it until the map ended.
WHAT THE TAPE SHOWED. Instrumenting the vertical swim decision printed
the answer in one line: "wl 2 mode 2 z 171 nodez 308". The bot floated
at the surface of the flooded court, z 171, steering at the platform
seat 137 units over its head. Probing the BSP, that platform's lip
stands 108 to 120u above the water across the whole region, and the
water vault reaches 64 (320 up under gravity 800). There is no exit
upward anywhere near it. Meanwhile the pool floor seats it could have
used sit 79u down and lead, through eight swim-exit links, to 167 of
the map's 198 nodes. The route out existed the entire time.
Two things had to be wrong at once for this to persist. The routed dive
only fires when the node is BELOW - this one was above, so the vertical
drive fell through to the surface tread. And the displacement guard that
would normally re-plan measures HORIZONTAL distance: a node directly
overhead reads zero, so 340 never tripped.
Argus_NearestNode now PREFERS a node at or below a query point that is
in water, and only takes one above when nothing below exists. It is a
preference, not a height cut, and that distinction is the whole fix: a
first attempt used a 64u ceiling and e1m2 promptly started a 20-hop
route from a seat 61u up, then stalled under it 231 times. Tape kept as
the graveyard entry below.
The displacement guard gains the vertical case: a swimmer whose steering
node stands more than a vault above it has been displaced - it fell in -
and re-plans. Every typed hop is exempt, because a swim exit, a lift and
a train all legitimately target a node above, and e1m2's own swim links
rise 112u.
LADDER, e1m2 co-op, 175 s, same map and skill:
cells busiest cell z range swim/min routes/min
before 15 93% 169..312 167.5 0.5
64u ceiling 8 95% 146..264 241.5 1.0
preference 65 25% 93..478 9.3 20.0
The bot now works the map from the pool floors at z 93 to the platforms
at 478 instead of treading one puddle. Stalls read 43 against the old 2,
which is the right direction: the old tape stalled twice because a bot
pinned in a stall-suppressed swim never moves enough to stall.
DEATHMATCH, dm4, 180 s, uncontended, against current main: stalls
24 vs 7, world deaths 4 vs 6, goals 21 vs 17. Both changes are gated on
water - pointcontents at the query point for the node preference,
waterlevel and ar_feettype for the guard - and dm4 froze in both arms of
the ladder at columns that are dry air over solid floor, where neither
can execute: '57 -273 -296' on the fix and '351 -488 21' on main. dm4
has an intermittent freeze class of its own and it is not this. The
28.3 s one is the longest seen today and worth an eye on the next tape.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This was referenced Sep 5, 2026
saworbit
added a commit
that referenced
this pull request
Sep 5, 2026
CHANGELOG gains v4.09: the six shipped builds of 2026-09-05, each with its mechanism and its ladder. Navigation seats past a ledge lip, the co-op fetch rest, door contact as an overlap, co-op friendly fire, the unstick, and the AI tick livelock. It also records the corrections, because three of the day's results were reported wrongly first and the record should carry that: #248's headline ladder was invalidated by a stationary puppet acting as a second co-op client, the dm2 door result did not reproduce past one pair, and the "latent perception bug" in co-op does not exist because Argus_Perceive returns before its player scan whenever coop is on. README gains the two instruments and their limits. tools/ argus_pointfile.py draws nav data into the running game through the engine's own pointfile channel, and tools/argus_edicts.py reads an edicts dump for live bot state. Both note what they cannot do: pointfile colour cycles every 16 points and needs a listen game, and the edict dump is lossy under load. The MCP operator guide documents the dump on the tune whitelist (edicts, edict <n>, edictcount, all read only) and the reader that goes with it. Co-authored-by: Shane Wall <shane.wall@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The e1m2 companion spent a whole session floating in one cell. Nothing in the nav layer knew a bot in water cannot reach a seat high above it, so it kept steering at one and swam at it until the map ended.
What the tape showed
Instrumenting the vertical swim decision printed the answer in one line:
wl 2 mode 2 z 171 nodez 308. The bot floated at the surface of the flooded court, steering at a platform seat 137 units over its head. Probing the BSP, that platform's lip stands 108-120u above the water across the whole region; the water vault reaches 64. There is no exit upward anywhere near it. The pool floor seats it could have used sit 79u down and lead, through eight swim-exit links, to 167 of the map's 198 nodes. The route out existed the whole time.Two things had to be wrong at once. The routed dive only fires when the node is below - this one was above, so the vertical drive fell through to the surface tread. And the displacement guard that would normally re-plan measures horizontal distance: a node directly overhead reads zero, so the 340u test never tripped.
The fix
Argus_NearestNodeprefers a node at or below a query point that is in water, taking one above only when nothing below exists. A preference, not a height cut - that distinction is the whole fix. My first attempt used a 64u ceiling and e1m2 promptly started a 20-hop route from a seat 61u up, then stalled under it 231 times.The displacement guard gains the vertical case. Typed hops are exempt: swim exits, lifts and trains all legitimately target a node above, and e1m2's own swim links rise 112u.
Ladder, e1m2 co-op, 175 s
Stalls read 43 against the old 2, which is the right direction: a bot pinned in a stall-suppressed swim never moves enough to stall.
Deathmatch
dm4, 180 s, uncontended, against current main: stalls 24 vs 7, world 4 vs 6, goals 21 vs 17. Both changes are water-gated, and dm4 froze in both arms at columns that are dry air over solid floor where neither can execute -
57 -273 -296on the fix,351 -488 21on main. dm4 has its own intermittent freeze class. The 28.3 s one is the longest seen today and worth an eye on the next tape.