docs+tools: the per-entity OB flag is refuted, and the counter decrements when the player kills
With the pilot finally shooting, the experiment completed both halves. Sample A at counter 12 over 120 entities gave 2 offsets where exactly 12 entities share a value; the counter then went 12 -> 11 and NEITHER survived. So within +-0x400 of an entity's position triple there is no 4-byte word whose shared-value population tracks REMAINING OB. The limits are recorded as part of the result, because they bound it: the test asks which entities share an EXACT 32-bit value, so a single bit ORed into a word that also carries health or a timer would never show up - a bit-level version of the same differential is the follow-on. Anything outside the window, or on entities that entities2 cannot see (it types by position CHANGING, so stationary objectives are invisible), is untested too, and the populations differed a lot between samples - 120 against 194. Separately: REMAINING OB went 12 -> 11, the first decrement of this whole investigation, while pilot.py logged 411 fire=1 samples and the HUD reached YOU KILLED WARPLANES 0003. Stated carefully - it does NOT show the counter counts kills, since an earlier run had the hostile population fall by a third with no movement; it shows some kills close something the counter tracks. Two robustness fixes: ob_flag retries an empty entity sample (one void run was caused by exactly that), and Pad releases everything on interpreter exit - a file-backed pad PERSISTS after its writer dies, so a tool killed mid-press would leave a button held and the game would walk through menus on its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
11
docs/re/captures/ob-flag-refuted-stage02.json
Normal file
11
docs/re/captures/ob-flag-refuted-stage02.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"nA": 12,
|
||||
"nB": 11,
|
||||
"hud_a": 12,
|
||||
"hud_b": null,
|
||||
"entities_a": 120,
|
||||
"entities_b": 194,
|
||||
"class_matches": [],
|
||||
"candidates_a": 2,
|
||||
"survivors": []
|
||||
}
|
||||
BIN
docs/re/captures/stage02-you-killed-warplanes-0003.png
Normal file
BIN
docs/re/captures/stage02-you-killed-warplanes-0003.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@@ -145,3 +145,66 @@ drawn from runs that assumed the pilot was shooting.
|
||||
This run did **not** freeze in ~25 minutes. Of the seven Stage 02 runs that have
|
||||
reached flight, **three froze** — so it is common but not the majority, and a run
|
||||
that survives the first minute or two seems to keep going.
|
||||
|
||||
---
|
||||
|
||||
## 🔴 2026-08-23 (final) — the per-entity flag hypothesis is REFUTED, on a transition it was not selected by
|
||||
|
||||
With the pilot actually shooting (`pilot-never-fires.md`), the experiment this
|
||||
file was written for finally completed both halves:
|
||||
|
||||
```
|
||||
HUD=12 RAM=12
|
||||
[A] counter=12 entities=120 96 turrets, 16 attackers, 7 wingmen, 1 player
|
||||
[A] classes whose head-count equals the counter: NONE
|
||||
[A] offsets where exactly 12 entities agree: 2
|
||||
pos+0x0238 = 00000006 {12 × e007_ADAN_Turret}
|
||||
pos+0x0250 = 239d6732 {12 × e010_ADAN_Attacker_S}
|
||||
counter 12 -> 11
|
||||
[B] counter=11 entities=194
|
||||
[B] of 2 candidates, 0 still hold exactly 11
|
||||
```
|
||||
([`captures/ob-flag-refuted-stage02.json`](captures/ob-flag-refuted-stage02.json))
|
||||
|
||||
**Both candidates die on the verification.** So: within ±0x400 of an entity's
|
||||
position triple there is **no 4-byte word whose shared-value population tracks
|
||||
`REMAINING OB`**. The simplest reading of "objectives are marked in the entity
|
||||
object" is dead.
|
||||
|
||||
### What that does and does not rule out — the limits are the result too
|
||||
|
||||
* 🔴 **Ruled out:** a per-entity word, in that window, holding a common value on
|
||||
exactly the objective entities.
|
||||
* ❔ **Not ruled out — a flag packed into a word that also varies.** The test asks
|
||||
which entities share an *exact* 32-bit value. A single bit ORed into a word
|
||||
that also carries health, a timer or a state machine would never produce a
|
||||
shared value, and this method cannot see it. A bit-level version of the same
|
||||
differential is the obvious follow-on.
|
||||
* ❔ **Not ruled out — anything outside the window**, or on entities the
|
||||
enumeration cannot see: `entities2.typed` types entities by their position
|
||||
*changing*, so a stationary objective is invisible to it.
|
||||
* ⚠️ **The populations differ a lot between samples** — 120 entities at A, 194 at
|
||||
B — so a flag on entities that spawned between the two would not have been
|
||||
tested either.
|
||||
|
||||
## ✅ And the counter DECREMENTS when the player kills
|
||||
|
||||
`REMAINING OB` went **12 → 11** during this run — the first decrement seen in
|
||||
this whole investigation — while `pilot.py` logged **411** `fire=1` samples and
|
||||
the HUD's own tally reached **`YOU KILLED WARPLANES 0003`**
|
||||
([`captures/stage02-you-killed-warplanes-0003.png`](captures/stage02-you-killed-warplanes-0003.png)).
|
||||
|
||||
That is worth stating carefully. It does **not** show that the counter counts
|
||||
kills — the earlier fifteen-minute run had the hostile population fall by a third
|
||||
with no movement at all. What it shows is that *some* kills close *something*
|
||||
the counter tracks, which is exactly the behaviour an objective counter should
|
||||
have, and it is the first time the player's own guns have been part of the
|
||||
experiment rather than a bystander.
|
||||
|
||||
## One more robustness fix, from a void run
|
||||
|
||||
`ob_flag.py` now **retries an empty sample**. `entities2.moving()` types entities
|
||||
by position changing, so a sample that lands on a load or loses the race with
|
||||
another scanner returns nothing — and a zero-entity sample silently yields zero
|
||||
candidates and a run that proves nothing. Seen exactly that way once, with the
|
||||
following sample finding 81 entities moments later.
|
||||
|
||||
@@ -11,6 +11,7 @@ then per frame a timestamp, the input vector, and every instance's raw window.
|
||||
|
||||
Usage: flight_probe.py <out.bin> [seconds]
|
||||
"""
|
||||
import atexit
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
@@ -59,6 +60,11 @@ class Pad:
|
||||
"LT": 0.0, "RT": 0.0}
|
||||
self.buttons = set()
|
||||
self._write()
|
||||
# A file-backed pad has a failure mode the FIFO did not: the state
|
||||
# PERSISTS after the writer dies. A tool killed mid-press leaves the
|
||||
# button held forever, and the game then walks through menus on its own
|
||||
# with nobody touching it. Release everything on exit.
|
||||
atexit.register(self.reset)
|
||||
|
||||
def _write(self):
|
||||
parts = []
|
||||
|
||||
@@ -110,7 +110,20 @@ def main():
|
||||
"ob_hunt.py before trusting anything below", flush=True)
|
||||
return 2
|
||||
|
||||
nA, entsA, gA = sample(w, defs, out, "A")
|
||||
# RETRY an empty sample. entities2.moving() types entities by their position
|
||||
# CHANGING between two reads, so a sample that lands on a load, a stall, or
|
||||
# simply loses the race with another scanner comes back with nothing — and a
|
||||
# zero-entity sample silently produces zero candidates and a void run. Seen
|
||||
# once, with sample B finding 81 entities moments later.
|
||||
for _ in range(6):
|
||||
nA, entsA, gA = sample(w, defs, out, "A")
|
||||
if len(entsA) >= 10:
|
||||
break
|
||||
print("[A] too few entities — retrying", flush=True)
|
||||
time.sleep(5)
|
||||
if len(entsA) < 10:
|
||||
print("[A] never got a usable entity sample", flush=True)
|
||||
return 4
|
||||
hist = Counter(nm for _, nm in entsA)
|
||||
print(f"[A] class histogram vs counter {nA}:", flush=True)
|
||||
for nm, k in hist.most_common(12):
|
||||
|
||||
Reference in New Issue
Block a user