#21 closed at the true root + 'arcade shipped a brick' RETRACTED (era witnesses 6-0): _DAT_004ba830 is a DOUBLE 1.0, not float 0.0 -- the entire brick claim was a constant-width misread (dword read of a qword; the low half of 1.0's mantissa is zeros). Binary truth [T1 raw disasm @004ba738]: overcharge CLAMPS to exactly 1.0f, the ==1.0 Loaded test passes next tick, the weapon fires the stored charge at normal top-gear damage (divisor is the recommended gear). Fix: clamp corrected to 1.0f; the rescue scaffolding REMOVED (it patched our own misread -- 38 'rescues' were 38 events the machine silently clamped); gauge-side zeroing inherits the fix, which is almost certainly #21's ORIGINAL field symptom (dark recharge arc + dark ready-dot = the dial reading our zeroed ratio while the rescue kept the weapon firing). seek21 bench PASS: 65 switches / 16 wraps / 9 overcharges -> fires through all, 13 post-overcharge gauge probes FULL, zero rescue prints. Docs: field guide section 2 rewritten as settled-by-testimony (credit to the group), decomp-reference block retracted with extracted constants, drift audit energy lane -> FAITHFUL, ZERO deviations. The witnesses' 'nah, that didn't happen' was worth eight bytes read at full width

This commit is contained in:
Joe DiPrima
2026-08-13 17:59:24 -05:00
parent 47826018cf
commit 25ed1ca627
6 changed files with 194 additions and 86 deletions
+13 -12
View File
@@ -436,18 +436,19 @@ From the weapon `.SUB` records + the charge-curve `.data` constants (PE-parsed a
the Execute machine, but this handler overwrites it). The earlier "jams are
mission-permanent / no unjam" claim was WRONG (read only the hold path) — corrected +
swept 2026-07-23 (projweap.cpp case-5/jam-log comments, players/README.txt).
**LATENT ARCADE BUG found + diverged (issue #21, 2026-07-21) [T1 byte-verified + T2 repro]:**
the Emitter charge integrates toward the GENERATOR voltage (@004ba838 `fld [src+0x1dc]`), full
is detected only inside the +-1% snap window around `seekVoltage[idx]` (ComputeOutputVoltage
@004ba738: `|rl-1|<=0.01 -> 1.0`), and OVERCHARGE reads ZERO (`rl>1.01 -> 0`,
`_DAT_004ba830 = 0.0` byte-verified). Toggle the seek gear while a charge is in flight and
the level lands ABOVE the new gear's window -> rechargeLevel pinned 0, the `==1.0` Loaded test
(`_DAT_004bac04`) never fires, charging continues to the generator ceiling where EVERY gear
reads overcharged -> the weapon is PERMANENTLY bricked (arc dark, ready-dot dark, no fire; gen
re-select cannot help). The arcade shipped this (locked 60 fps + rare seek use hid it); the
port's clickable seek button triggers it in seconds. **Deliberate divergence** (emitter.cpp
Loading tick): overcharge (`currentLevel > seekVoltage[idx]`) counts as fully charged ->
Loaded ("full == the gear's seek voltage" is the arcade's own discharge algebra). Repro +
**#21 RETRACTED 2026-08-13 (era testimony -> adversarial re-audit; witnesses 6-0):** the
"LATENT ARCADE BUG / permanently bricked / the arcade shipped this" claim that stood here was
a CONSTANT-WIDTH MISREAD. `_DAT_004ba830` is a **DOUBLE 1.0** (bytes 00..00 f0 3f @0x4ba830 --
the old "0.0 byte-verified" read the low DWORD of a QWORD: 1.0's mantissa zeros; Ghidra's own
"globals overlap smaller symbols" warning applies). ComputeOutputVoltage @004ba738 truth [T1
raw disasm]: ratio = charge/seekV[idx]; snap |rl-1|<=0.01 -> 1.0f; else CLAMP to [0,1] --
overcharge stores EXACTLY 0x3f800000, the `==1.0` Loaded test passes next tick, the weapon
FIRES the stored charge (damage divisor is the RECOMMENDED gear, so a wrap-fired charge deals
normal top-gear damage). ToggleSeekVoltage @004ba478 genuinely skips ResetFiringState on wrap
[T1] -- but nothing downstream ever bricks. The machine never had a dark laser; the port's
"rescue" was a patch over OUR misread (removed with the clamp fix, same date). Constants
extracted: 0x4ba818=0.0f 0x4ba81c=1e-4f 0x4ba820=1.0f 0x4ba824=0.01f 0x4ba828=dbl 0.0
0x4ba830=dbl 1.0 0x4bac04=1.0f. Gotcha family 30/31 (width/scope misreads). Repro +
verify: BT_SEEKTEST seek-abuse -- pre-fix deadlocks at pct=0/alarm=3; post-fix 38 rescues,
ends Loaded/pct=1. `[seek]` log prints the per-gear table + rescue events.
- **EJECT/PANIC cluster DECODED + WIRED 2026-08-02 [T1 raw disasm]:** `@0049f854`
+30 -14
View File
@@ -24,14 +24,14 @@ the queued ticket comment until the group signs off.
| 2 | PPC/ERPPC fire chain (0xBD4) | FAITHFUL |
| 3 | PPC rider (a) victim display scramble | KNOWN-DIVERGENCE [T3] (unimplemented; visual OPEN) |
| 4 | PPC rider (b) victim generator short | FAITHFUL (tag-label scope OPEN) |
| 5 | Seek-voltage gears + #21 overcharge brick | KNOWN-DIVERGENCE (deliberate rescue) |
| 5 | Seek-voltage gears (#21) | FAITHFUL (brick claim RETRACTED 2026-08-13 -- constant-width misread; binary clamps overcharge to 1.0 and fires; witnesses 6-0) |
| 6 | Autocannons AFC25/50/100 (0xBCD) | FIXED-THIS-BUILD (#171; T2 field receipts pending) |
| 7 | AC cosmetic tracer visual | KNOWN-DIVERGENCE [T3] (cosmetic; constants recovered — close) |
| 8 | GAUSS (fielded, 0xBCD record) | FAITHFUL (correction sweep owed, HELD) |
| 9 | GaussRifle class 0xBCE (+ shell 0xBD1) | FAITHFUL (dead code both sides) |
| 10 | MissileLauncher core (0xBD0, all racks) | FAITHFUL |
| 11 | Missile presentation (salvo visuals/gains) | KNOWN-DIVERGENCE [T3] (presentation-only) |
| 12 | Missile bystander sweep | KNOWN-DIVERGENCE [T3] (largest remaining gap) |
| 12 | Missile bystander sweep | FIXED-THIS-BUILD (capsule sweep [T3 radius]; T2 field receipts pending) |
| 13 | Missile splash (@0042fad0) | FAITHFUL under stock content (gate wiring [T3] on custom eggs) |
| 14 | LRM flight + loft | FAITHFUL |
| 15 | Streak racks (strk model) | FAITHFUL |
@@ -94,11 +94,13 @@ content.
- **Verdict:** FAITHFUL (tag-label scope OPEN). NOTE: the two riders'
port status differs — never lump them in vet-facing docs.
### 5. Seek-voltage gears + #21 overcharge brick (all energy weapons)
### 5. Seek-voltage gears (#21) -- brick claim RETRACTED 2026-08-13
- **Binary [T1]:** `ToggleSeekVoltage` @004ba478 (vet/expert only): wrap
from gear 4→1 skips `ResetFiringState` → charge above snap window →
`ComputeOutputVoltage` overcharge reads ZERO → weapon PERMANENTLY
BRICKED. The arcade shipped this latent brick.
~~overcharge reads ZERO → PERMANENTLY BRICKED~~ **RETRACTED: _DAT_004ba830
is a DOUBLE 1.0 (width misread); the binary CLAMPS overcharge to 1.0 and
the weapon fires -- the machine never bricked. Era witnesses (6-0) triggered
the re-audit; full detail in context/decomp-reference.md.**
- **Port [T3 deliberate, documented]:** overcharge counts as Loaded (issue
#21, 38 benched rescues) — the port's ONE deliberate energy-lane
divergence; lives in the deviations appendix.
@@ -200,12 +202,25 @@ One class, data-differentiated.
- **Binary [T1]:** FUN_0042291c swept real world geometry including movers
each tick — a third mech in the flight path could be struck by someone
else's missile.
- **Port [T3]:** pool rounds contact only the locked target + terrain; a
mech between shooter and target is flown through (splash alone can touch
bystanders).
- **Verdict:** KNOWN-DIVERGENCE [T3] — largest remaining missile-physics
gap. Field-guide watch item for Friday ("hit by missiles aimed at
someone else" beyond splash range).
- **Port [T3 radius / port-shape, landed 2026-08-13]:** `mech4.cpp
BTUpdateProjectiles` — per frame, damage-carrying GUIDED rounds (the one
salvo lead) run a nearest-approach test of the flight segment against a
VERTICAL CAPSULE per other registered mech (BTGetTargetCandidates walk;
shooter + locked target excluded — the target keeps its own 4.0u fuze).
Capsule derived from the victim's own collision template
(Mover::GetCollisionTemplate BoxedSolid: axis = origin up minY..maxY,
radius = larger horizontal half-extent; fallback 3.5u × 14u when the
template is unresolved [T3]). Earliest contact along the segment wins
(bystander vs target fuze compared by segment param); the struck mech
becomes the DIRECT victim through the unchanged dispatch path (cluster
roll + splash semantics identical, splash excludes the new direct
victim). Wrecks stay in the sweep — binary detonates on any solid; the
#174 victim-side guard keeps dead zones from cascading/scoring. Capped
`[projectile] BYSTANDER` receipt (24 prints) for field forensics.
- **Verdict:** FIXED-THIS-BUILD (radius is a [T3] capsule approximation of
the real solid sweep; T2 field receipts pending — Friday watch item
unchanged: "hit by missiles aimed at someone else" now expected INSIDE
splash range too).
### 13. Missile splash — Explosion::SplashDamage @0042fad0
- **Binary [T1]:** gated on the FIRING player's advancedDamage (+0x264;
@@ -368,8 +383,9 @@ Several 'LRM5/10/15' bins author nrk ammo.
Announced to the vets in the field guide §9.
3. PPC gauge-scramble rider: BLOCKED on era testimony (field guide §3) —
build the visual only after the group answers.
4. Missile bystander sweep: design exists; field guide §6 arms the Friday
watch item.
4. Missile bystander sweep: LANDED 2026-08-13 (lane 12 entry) together
with coast gravity (dt-table row); field guide §6's Friday watch item
now doubles as the T2 verification channel.
## Era questions shipped to the vets
@@ -560,7 +576,7 @@ round is a renderer cosmetic with no Mover physics.
| **Drag** | `COD·ρ·sign(v)·v²` per body axis, EVERY frame, burn + coast (@004bef78, power-2 inline; plain-Projectile dead class uses power 1.0 @004bddec); CODs 0.001 [T1 bytes]; terminal `sqrt(T/c)` precomputed @004bf5b4→+0x348 | **OMITTED** ("negligible") | **WAS DIVERGENT → FIXED-THIS-BUILD** | was: LRM 3030 u/s (5.5× cap), 13% time-to-800u, +40% impact speeds, map-crossing runaways past 2.5 s; now governed at 547.7/774.6 |
| Speed clamp | none — drag IS the clamp | none — drag now is | **FAITHFUL** (post-fix) | — |
| Velocity↔seeker slaving | burning: lateral local vel/accel zeroed each frame ⇒ velocity ≡ speed×nose; nose slerps ≤ MaxThrusterRotationRate·dt (@004be474); at burnout slaving STOPS (ballistic coast) | rotate-toward-aim at port gains 4/8·dt, speed-normalized; steers through coast | KNOWN-DIVERGENCE [T3] (lane 11) | turn dynamics shape; coast homing (real impacts are in-burn) |
| Gravity | none during burn; coast: `worldAccel.y = 6.5` fresh each frame (env default) + y<1 kill plane | absent (loft arc + ttl only) | DIVERGENT [T3 **minor**, unfixed] | post-burnout flights only (beyond ~1400u SRM / ~4800u LRM); missed rounds fly level instead of dropping |
| Gravity | none during burn; coast: `worldAccel.y = 6.5` fresh each frame (env default) + y<1 kill plane | absent (loft arc + ttl only) | **WAS DIVERGENT → FIXED 2026-08-13** (`vel.y = 6.5·coast_dt` on thruster rounds after burnout, exact at the burnout boundary; speed re-synced to |vel|) | post-burnout flights only; missed rounds now drop instead of flying level |
| Position step | `p += v_old·dt + ½a·dt²` | `p += v_new·dt` (semi-implicit) | FAITHFUL-approx | ≤ ~12u lead over a full SRM burn @60fps; imperceptible |
| Expiry | burn+10 s, y<1 (@004bef78 tail) | same (#168) | **FAITHFUL** | none |
| Seeker lead | `aim += targetVel × range/terminal(+0x348)`; loft climb-gated (thruster+0xF0 > _DAT_004bec1c) | live-position re-lead; loft constants exact | KNOWN-DIVERGENCE [T3] (lane 11) | small lead-shape difference |
+19 -18
View File
@@ -80,31 +80,32 @@ WE NEED YOUR MEMORY
>
-----------------------------------------------------------------------------
2. THE CHARGE GEAR (ALL ENERGY WEAPONS) -- AND THE BRICK THE ARCADE SHIPPED
2. THE CHARGE GEAR (ALL ENERGY WEAPONS) -- SETTLED BY YOUR MEMORY
-----------------------------------------------------------------------------
THE ORIGINAL'S BEHAVIOR
Veteran/expert pilots could cycle an energy weapon's charge gear
(four settings). The machine shipped with a latent flaw: cycling
PAST the top gear wrapped to the bottom without resetting the firing
state. The stored charge now sat above the new gear's release
window, the output read zero, and the weapon went permanently dark
-- charged, lit, and unable to fire until re-arm. The arcade
shipped this.
(four settings). Cycling past the top wraps to the bottom and the
weapon keeps working: a stored charge above the new gear's level
simply counts as FULL and fires (a wrap-fired top-gear charge deals
its normal top-gear damage -- no secret super-shot, no dead weapon).
FOR THE RECORD: an earlier version of this document claimed the
machine shipped a flaw here -- that wrapping the gear left the
weapon permanently dark. Several of you said flatly that never
happened. You were right. The re-audit you triggered found our
reading of one number in the original's code was wrong by a single
byte-width, and the "brick" existed only in our port, never on the
machine. This section is your correction, adopted.
OUR PORT TODAY
We deliberately rescue it: an overcharged weapon counts as loaded
and fires (#21; 38 rescues proven on the bench). This is the port's
ONE deliberate deviation in the energy lane, and it stays on the
deviations list so nobody mistakes it for the original.
Fixed to match: cycling behaves exactly as you remember. (Before
the fix our gauges could briefly read EMPTY on an over-charge where
the machine read FULL -- if you saw that, it dies in the next
build.)
CHANGING IN THE NEXT BUILD
Nothing -- unless the group votes for the authentic brick.
WE NEED YOUR MEMORY
- Did cycling a laser's gear past the top setting ever leave the
weapon DARK -- charged but refusing to fire until re-arm? If pods
genuinely ate weapons this way, some of you will remember it.
The correction above ships. The old "deviation" entry is withdrawn
-- the energy lane now has ZERO deviations from the original.
YOUR NOTES (callsign first, one entry per line):
>
+31 -42
View File
@@ -24,7 +24,10 @@
// _DAT_004ba820 = 00 00 80 3f = 1.0f (full-charge reference / upper clamp)
// _DAT_004ba824 = 0a d7 23 3c = 0.01f (snap-to-1.0 tolerance)
// _DAT_004ba828 = 00 00 00 00 = 0.0f (below-range clamp result)
// _DAT_004ba830 = 00 00 00 00 = 0.0f (above-range clamp result)
// _DAT_004ba830 = 00 00 00 00 00 00 f0 3f = DOUBLE 1.0 (above-range clamp result;
// #21 CORRECTION 2026-08-13: this operand is a QWORD -- the old
// "byte-verified 0.0f" dword-read only the LOW 4 bytes (1.0's mantissa
// zeros) of the 8-byte constant. gotcha 30/31 literal-width family.)
// _DAT_004ba9a4 = 00 00 00 00 = 0.0f (dischargeTimer expiry threshold)
// _DAT_004bac04 = 00 00 80 3f = 1.0f (outputVoltage "fully charged" test)
// _DAT_004bac08 = 00 00 00 00 = 0.0f (firing dischargeTimer expiry threshold)
@@ -498,21 +501,16 @@ void
// generator + calibrated voltageScale/EC; TrackSeekVoltage integrates
// the charge over the authored RechargeRate seconds).
//
// POD-FRAME SUB-STEPPING (task #11, THE WEAPON-BRICK FIX): the binary's
// Loading tick assumes the pod's LOCKED 60 fps -- the charge integrates
// toward the generator's 10000V and the Loaded transition only fires
// while rechargeLevel crosses the +-0.01 snap window around seekV
// (level in [7920, 8080]; ~15 pod frames wide). Under the port's
// variable dt, one spike frame (dt >= ~0.25s) jumps the whole window:
// the level overshoots, ComputeOutputVoltage's byte-verified >1.0
// clamp (_DAT_004ba830 = 0.0) zeroes rechargeLevel, and the weapon is
// PERMANENTLY stuck Loading at level ~10000 (observed live:
// "level=9999.68 alarm=3 edge=1"). Fix: run the binary's own
// POD-FRAME SUB-STEPPING (task #11): run the binary's own Loading
// tick -- TrackSeekVoltage + ComputeOutputVoltage + the Loaded test --
// at the binary's own frame rate. This also keeps the I^2R generator
// feed integral accurate (big steps evaluate seekRate at a stale level
// and OVER-heat the generators). Sub-step count is bounded; leftover
// time under extreme throttling just resumes next frame.
// at the binary's locked 60 fps. Justification: the I^2R generator
// feed integral -- TrackSeekVoltage's seekRate^2 heat evaluated over
// big variable-dt steps uses a stale level and OVER-heats the
// generators. (The former "weapon-brick fix" rationale was FALSE --
// see the #21 correction in ComputeOutputVoltage: the >1.0 clamp
// stores 1.0f, so an overshot charge goes Loaded next tick at any dt.)
// Sub-step count is bounded; leftover time under extreme throttling
// just resumes next frame.
{
static const Scalar kPodFrame = 1.0f / 60.0f;
Scalar remaining = time_slice;
@@ -531,32 +529,13 @@ void
weaponAlarm.SetLevel(2); // -> Loaded
break;
}
// OVERCHARGE RESCUE (issue #21, 2026-07-21) -- a DELIBERATE
// divergence from the binary, which DEADLOCKS here: change the
// seek gear while a charge is in flight and the level can land
// ABOVE the new gear's snap window (level/seekV > 1.01) -- the
// byte-verified ComputeOutputVoltage clamp then zeroes
// rechargeLevel (_DAT_004ba830 = 0.0) and the ==1.0 Loaded test
// can never fire again while TrackSeekVoltage keeps charging
// toward the generator voltage: the weapon is PERMANENTLY
// bricked (arc dark, dot dark, never fires -- reproduced
// headless, and trivial to trigger from the clickable seek
// button; the pod's locked 60 fps + rare seek use hid it).
// The arcade's own math says "full == the gear's seek voltage"
// (discharge energy computes from seekV[rec]), so an
// overcharged weapon IS fully charged -- treat it as Loaded.
if (currentLevel > seekVoltage[seekVoltageIndex]
&& seekVoltage[seekVoltageIndex] > 0.0f)
{
rechargeLevel = 1.0f; // full (the display/damage ratio)
weaponAlarm.SetLevel(2); // -> Loaded
if (getenv("BT_SEEK_LOG"))
DEBUG_STREAM << "[seek] " << GetName()
<< " overcharge rescue: level=" << currentLevel
<< " > seekV=" << seekVoltage[seekVoltageIndex]
<< " -> Loaded" << std::endl;
break;
}
// The old OVERCHARGE RESCUE (issue #21, 2026-07-21) lived here.
// REMOVED 2026-08-13: it patched an artifact of the
// _DAT_004ba830 width misread (double 1.0 dword-read as float
// 0.0). With the faithful clamp in ComputeOutputVoltage an
// overcharged weapon snaps rechargeLevel to exactly 1.0f, so
// the ==1.0f test above takes it to Loaded naturally -- the
// binary never deadlocked here and neither do we.
}
}
break;
@@ -801,7 +780,17 @@ void
}
else if (rechargeLevel > 1.0f) // _DAT_004ba820
{
rechargeLevel = 0.0f; // _DAT_004ba830 (NB: also 0.0f)
// #21 CORRECTION (2026-08-13, adversarial re-audit, raw-byte disasm):
// _DAT_004ba830 is a DOUBLE 1.0 (bytes 00 00 00 00 00 00 f0 3f), NOT
// float 0.0 -- the old "byte-verified 0.0f" dword-read the LOW HALF of
// the qword (1.0's mantissa zeros; gotcha 30/31 literal-width family).
// Overcharge clamps to EXACTLY 1.0f (0x3f800000 stored), so the ==1.0f
// Loaded test (_DAT_004bac04) passes next tick and the weapon fires.
// The machine NEVER bricked on overcharge; era testimony that no weapon
// ever went permanently dark triggered the re-audit. This faithful
// clamp supersedes the old "overcharge rescue" divergence (removed
// from EmitterSimulation's Loading case).
rechargeLevel = 1.0f; // _DAT_004ba830 (double 1.0, stored as float)
}
}
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# #21 CORRECTION bench (2026-08-13): the faithful overcharge clamp.
#
# THE OLD SHAPE (21b2bfc, 2026-07-21): 2.5 min of BT_SEEKTEST abuse (dispatches
# the SAME ReceiverDataMessageOf id-0xb press the clickable seek button sends,
# every ~2s) bricked the laser pre-rescue and produced "38 overcharge rescues"
# post-rescue.
#
# THE NEW PASS (post-correction -- _DAT_004ba830 is a DOUBLE 1.0, overcharge
# clamps rechargeLevel to EXACTLY 1.0f):
# 1. gear cycling happens, including WRAPS past top (prev > next skips
# ResetFiringState -- the overcharge composition);
# 2. overcharge compositions observed ([seek] gear-switch lines where the
# in-flight level lands ABOVE the new gear's snap window, level > 1.01*seekV);
# 3. the weapon KEEPS FIRING after the last overcharge (no dark state);
# 4. gauges non-zero: [fire-probe] pct=1 (rechargeLevel, the recharge-dial
# source) seen after the last overcharge;
# 5. ZERO "rescue" prints anywhere in the log (the rescue block is gone).
set -x
. /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 3
rm -f sk21.log
bt_expert_egg MP.EGG SK21.EGG
sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=vulture/" SK21.EGG
( export BT_SEEKTEST=1 BT_AUTOFIRE=1 BT_FIRE_AT_ICON=1
export BT_SEEK_LOG=1 BT_FIRE_LOG=1 BT_DMG_LOG=1
bt_launch sk21.log SK21.EGG 0x03 )
sleep 160
bt_kill_ours; sleep 2; taskkill //F //IM btl4.exe >/dev/null 2>&1
python /c/git/bt411/scratchpad/night17/seek21_check.py /c/git/bt411/content/sk21.log
+68
View File
@@ -0,0 +1,68 @@
# #21 correction bench checker -- see seek21_bench.sh for the PASS contract.
import io, re, sys
path = sys.argv[1] if len(sys.argv) > 1 else "/c/git/bt411/content/sk21.log"
L = io.open(path, encoding="latin-1", errors="replace").read().splitlines()
# Emitter gear-switch lines (Myomers [seek] lines lack "seekV["):
# [seek] NAME -> gear N seekV[N]=X table={...} max=M level=L genV=...
gear_re = re.compile(
r"\[seek\] (\S+) -> gear (\d+) seekV\[\d+\]=([-\d.eE+]+) .*level=([-\d.eE+]+)")
gears = [] # (line_idx, name, gear, seekV, level)
for i, l in enumerate(L):
m = gear_re.search(l)
if m:
gears.append((i, m.group(1), int(m.group(2)),
float(m.group(3)), float(m.group(4))))
names = sorted(set(g[1] for g in gears))
print("=== #21 CORRECTION BENCH (faithful overcharge clamp) ===")
print("weapon(s) under seek cycling:", names or "NONE")
ok = True
if not gears:
print("FAIL: no emitter [seek] gear lines -- rig broken (expert gate? seektest?)")
ok = False
for name in names:
G = [g for g in gears if g[1] == name]
wraps = sum(1 for a, b in zip(G, G[1:]) if b[2] < a[2])
over = [g for g in G if g[3] > 0.0 and g[4] > 1.01 * g[3]]
fired_all = [i for i, l in enumerate(L) if ("FIRED '%s'" % name) in l]
last_over = over[-1][0] if over else None
fired_after = ([i for i in fired_all if i > last_over]
if last_over is not None else [])
# gauge source: [fire-probe] NAME ... pct=1 after the last overcharge
pct1_after = 0
if last_over is not None:
pr = re.compile(r"\[fire-probe\] %s .*pct=([-\d.eE+]+)" % re.escape(name))
for l in L[last_over:]:
m = pr.search(l)
if m and float(m.group(1)) >= 0.999:
pct1_after += 1
# tail liveness: still firing in the last 25% of the run
tail = int(len(L) * 0.75)
fired_tail = sum(1 for i in fired_all if i >= tail)
print("-- %s: gearSwitches=%d wraps=%d overchargeEvents=%d "
"FIRED total=%d afterLastOver=%d pct=1 probes after=%d tailFIRED=%d"
% (name, len(G), wraps, len(over), len(fired_all),
len(fired_after), pct1_after, fired_tail))
if wraps < 1:
ok = False; print("FAIL(%s): no wrap past top gear" % name)
if not over:
ok = False; print("FAIL(%s): no overcharge composition ever formed" % name)
if last_over is not None and not fired_after:
ok = False; print("FAIL(%s): NEVER FIRED after the last overcharge -- dark state" % name)
if last_over is not None and pct1_after < 1:
ok = False; print("FAIL(%s): recharge gauge (pct) never read full after overcharge" % name)
if fired_tail < 1:
ok = False; print("FAIL(%s): not firing in the run tail -- possible late brick" % name)
rescues = sum(1 for l in L if "rescue" in l)
print("rescue prints in log:", rescues)
if rescues != 0:
ok = False; print("FAIL: rescue print still present")
print("RESULT:", "PASS" if ok else "FAIL")