#!/usr/bin/env bash # ========================================================================= # #52 STANDING-LOCK bench -- the night-13 field composition. # # FIELD COMPOSITION (night 13, 4.11.817): a healthy PEER that pivots at # stand and then accelerates. The peer's turn-arm block parks its body # channel at Standing on the walk-demand yield (mech4.cpp:2853, "case 0 # walk-begins next tick"); the claim under test is that case 0 CANNOT # walk-begin on a replicant because the port's inserted turn block resets # the state case 0 just armed, using the SAME expression. # # PROVOCATION: B chases A and holds at BT_GOTO_STOP. Every time A walks # back out of that radius B re-acquires -- goto turns hard at 0.2 throttle # (mech4.cpp:4108-4113) = TURNING AT SUB-WALK SPEED, which arms the peer's # body state 4 -- then the heading aligns, throttle goes to 1.0, and the # walk-demand yield fires. That is the trap, once per re-acquisition. # # READ IT ON A: B's replicant lives on A, so A's log carries [bodySM] and # [skate] for B. Both nodes carry both gates anyway. # # MODE=legacy -> BT_NO_BODY_FALLTHRU=1 (pre-fix path; expect the lock) # MODE=fixed -> default (expect no lock) # ========================================================================= set -x MODE="${1:-fixed}" DUR="${2:-230}" . /c/git/bt411/scratchpad/night6/bench_common.sh cd /c/git/bt411/content || exit 1 taskkill //F //IM btl4.exe > /dev/null 2>&1 sleep 2 rm -f sl_${MODE}_a.log sl_${MODE}_b.log sl_${MODE}_relay.log bt_assert_player_env bt_expert_egg MP.EGG SL.EGG # map=CAVERN on purpose (not the usual grass/day combat default): the lock needs # a mech holding a STEADY walk demand without its gait changing, and the # reliable way to get that is a mech pushing into geometry -- throttle up, leg # SM parked, no gait edges, so no type-3 record ever refreshes the peer. On # open grass whether the autodriver finds a wall is luck: the first run locked # for 336 consecutive seconds, the second for 1. Cavern guarantees it, and its # walls also let the jammed mech SLIDE, which is what turns a lock into a # visible skate. time=day only so the windows are watchable. sed -i "s/^map=.*/map=cavern/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/" SL.EGG LEGACY="" [ "$MODE" = "legacy" ] && LEGACY=1 # ROLES (v2, after the pass-1 miss): the mech we need MOVING WHILE LOCKED is # the one being observed. v1 made the observer autodrive and it walked into # the arena wall -- 336 locked seconds but ~zero translation, so the lock # reproduced and the SKATE (which needs 90 sustained MOVING frames) did not. # v2 puts the chaser on A: BT_GOTO with a tight stop radius keeps A walking at # a target that keeps moving, so A gets a stop/turn/walk cycle (the lock entry) # AND continuous travel (the symptom). B observes; B's own wall-bumping is # irrelevant because we read A's replicant on B's log. # v3 = back to the v1 roles, which is the rig that ACTUALLY reproduces. # v2 (chaser observed) gave zero locks, and that is itself the finding: a mech # whose gait keeps changing keeps emitting type-3 records, and each one sets the # peer's body state directly (ReadUpdateRecord), so the peer never has to # self-arm and never meets case 0. The lock needs the opposite -- a peer PARKED # at Standing while the master holds a STEADY demand, so no refreshing record # ever comes. A wall-jammed autodriver is exactly that, which is why v1 locked # for 336 consecutive seconds. Keep it. # ---- node B (back window): the OBSERVER -- chases, so it stays engaged ------ ( export BT_GOTO=enemy BT_GOTO_STOP=150 BT_GOTO_LOG=1 export BT_BODY_SM_LOG=1 BT_MP_LOG=1 BT_MATCHLOG=1 [ -n "$LEGACY" ] && export BT_NO_BODY_FALLTHRU=1 bt_launch sl_${MODE}_b.log SL.EGG 0x0C -net 1601 ) sleep 2 # ---- node A (front window): the OBSERVED mech -- steady demand, held up ----- ( export BT_AUTODRIVE=0.7 export BT_BODY_SM_LOG=1 BT_MP_LOG=1 BT_MATCHLOG=1 [ -n "$LEGACY" ] && export BT_NO_BODY_FALLTHRU=1 bt_launch sl_${MODE}_a.log SL.EGG 0x03 -net 1501 ) sleep 5 python ../tools/btconsole.py SL.EGG 127.0.0.1:1501 127.0.0.1:1601 > sl_${MODE}_relay.log 2>&1 & RELAY=$! sleep "$DUR" kill $RELAY 2>/dev/null bt_kill_ours sleep 2 # bt_launch's winpid poll can MISS (documented) -- pass 1 orphaned a node that # then sat holding a -net port. Passes run strictly one at a time here, so a # blanket sweep is safe and is the only thing that guarantees a clean slate. taskkill //F //IM btl4.exe > /dev/null 2>&1 sleep 3 echo "=================== MODE=$MODE ===================" echo "--- THE LOCK: [bodySM] arm->reset pairs (1 Hz throttled) ---" echo -n "on B (observing A, the chaser): "; grep -ac "STANDING-LOCK" sl_${MODE}_b.log echo -n "on A (observing B): "; grep -ac "STANDING-LOCK" sl_${MODE}_a.log grep -a "STANDING-LOCK" sl_${MODE}_b.log | sort -u | head -4 echo echo "--- THE PEER GAIT: what a MOVING replicant's body channel is doing ---" echo -n "samples on B: "; grep -ac "peergait" sl_${MODE}_b.log echo "body states seen while moving (B's view of A):" grep -a "\[peergait\]" sl_${MODE}_b.log | grep -oaE "bstate=[0-9-]+" | sort | uniq -c | sort -rn | head -10 echo "idle-channel samples (the skate condition):" grep -ac "IDLE CHANNELS" sl_${MODE}_b.log echo echo "--- THE SYMPTOM: [skate] episodes ---" echo -n "on B: "; grep -ac "SKATING" sl_${MODE}_b.log grep -a "\[skate\]" sl_${MODE}_b.log | head -6 echo -n "on A: "; grep -ac "SKATING" sl_${MODE}_a.log echo echo "--- A's drive cycle (arrive/re-acquire churn = lock entries) ---" grep -a "\[goto\]" sl_${MODE}_a.log | grep -oaE "arr=[01]" | uniq -c | wc -l