re: refute the OB entity-flag on the FULL enumeration -- the stationary-entity hole is closed

Every earlier refutation in this file carried the caveat that entities2.typed
types entities by their position CHANGING, so a stationary objective is invisible
to it.  This session's definition-pointer enumeration does not have that limit, so
the sweep was re-run against it (ob_flag_all.py, guarded route, stage asserted,
HUD cropped beside each sample):

  A: HUD 004, 147 entities -> 152 candidates
  B: HUD 012, 133 entities ->  15 candidates
  intersection: 1

The lone survivor pos+0x0250 = 239d6732 is the same offset AND identical value
this file already characterised as a per-group word.  Membership test: all 12
holders are UN_e010_ADAN_Attacker_S, 12 of 16 live attackers.  It is a squad
parameter, and it survived only because that population equalled the counter at
both samples.

Also reconfirms "not a class head-count" on 147 entities including capital ships.

Trap recorded: the first sweep reported 298 entities and a class with head-count
exactly 4 -- a perfect-looking hit that was pure artifact.  Deduping by ADDRESS
leaves the measured exact 2x duplication (pairs 0x1000 apart, byte-identical
positions) intact and doubles every population.  Dedup on the position VALUE.
This commit is contained in:
Sylpheed RE agent
2026-08-27 00:40:53 +00:00
parent a22ef5f96f
commit b0081fd81a
2 changed files with 116 additions and 0 deletions

View File

@@ -309,3 +309,63 @@ filter used, but the confirming screenshot at that instant did not agree.
**What is still not ruled out** is unchanged from the word-level pass: anything
outside the window, and anything on entities `entities2.typed` cannot see (it
types by position *changing*, so stationary objectives are invisible).
---
## 🔴 2026-08-27 — the last hole is closed: refuted on the FULL entity enumeration too
Every refutation above carried the same caveat: *"anything on entities
`entities2.typed` cannot see — it types by position **changing**, so a stationary
objective is invisible to it."* That hole is now closed, because this session
established an enumeration that does not have it — search for the **definition
pointer** and take `position = hit - 0x130`
([`two-entity-enumerations.md`](two-entity-enumerations.md)), which finds every
entity moving or not, capital ships and the `ACROPOLIS` included.
Re-run with that enumeration ([`ob_flag_all.py`](../../tools/re-capture/ob_flag_all.py)),
guarded route, stage asserted, HUD cropped beside every sample:
| pass | HUD | entities | candidates |
|---|---|---|---|
| A | `004` | **147** | 152 |
| B | `012` | 133 | 15 |
| **intersection** | | | **1** |
The lone survivor is **`pos+0x0250 = 239d6732`** — and it is **the same offset and
the identical value** this file already characterised as a per-group word. Put to
the membership test rather than admired:
```
holders of pos+0x250 == 239d6732 : 12
12 UN_e010_ADAN_Attacker_S (of 16 live attackers)
```
**All twelve are attackers.** It is a squad parameter or pointer shared by part of
the `e010` population, and it survived only because that population happened to
equal the counter at both samples. Not an objective flag.
### What this adds
* 🔴 The **"objectives are marked in the entity object"** family is now refuted on
an enumeration that **includes stationary entities** — the limit every earlier
pass had to state is gone, not merely narrowed.
* 🔴 **"Not a class head-count" is reconfirmed on 147 entities including capital
ships**: no class had a population of 4 at A or 12 at B.
* ⚠️ **A trap worth having.** The first run of this sweep reported **298**
entities and a class with head-count exactly 4 (`UN_f105_TCAF_Cruiser`) — an
apparently perfect hit. It was an artifact: deduplicating by *address* leaves
this session's measured **exact 2× duplication** (pairs `0x1000` apart holding
byte-identical positions) intact, so every entity is counted twice and every
population doubles. Deduplicating by **position value** gives 147 and the
spurious class hit disappears. Any sweep of this shape must dedup on the
position triple, not the address.
### Still open
* ❔ Anything **outside** the `-0x400 … +0xC00` window — unchanged.
* ❔ Sample A's holder membership was not re-checked (the world had moved on), so
"4 attackers at A" is inferred from B's membership plus this file's earlier
identification of the same word, not measured.
* ❔ What the counter *does* count. Still unexplained: it read `004` with ~118
hostiles live, rose `004 → 012` as waves spawned, held through a third of the
hostile population dying, and decremented once when the player killed.

View File

@@ -0,0 +1,56 @@
"""Flag sweep over the DEFINITION-POINTER enumeration (sees stationary entities)."""
import sys, os, struct, collections, json
sys.path.insert(0, '/tmp/rc')
import gmem, gworld, entities2 as E
LO, HI = -0x400, 0xC00
def entities(w):
"""Every entity via its definition pointer: position = hit - 0x130."""
defs = E.definitions(w)
lo, hi = gmem.va_to_off(E.ENT_VA_LO), gmem.va_to_off(E.ENT_VA_HI)
out = []
for pat, nm in defs.items():
for a, b in gmem.extents(w.fd, w.size):
a, b = max(a, lo), min(b, hi)
if b <= a: continue
blob = os.pread(w.fd, b - a, a); i = 0
while True:
j = blob.find(pat, i)
if j < 0: break
off = a + j
if off % 4 == 0: out.append((off - 0x130, nm))
i = j + 4
return out
def sweep(w, ents, n):
"""(offset, value) pairs shared by exactly n entities."""
tally = collections.defaultdict(collections.Counter)
for pos, nm in ents:
buf = os.pread(w.fd, HI - LO, pos + LO)
if len(buf) < HI - LO: continue
for k in range(0, HI - LO, 4):
tally[LO + k][struct.unpack('>I', buf[k:k+4])[0]] += 1
return {(o, v) for o, c in tally.items() for v, k in c.items() if k == n}
if __name__ == '__main__':
n = int(sys.argv[1]); tag = sys.argv[2]
w = gworld.World()
ents = entities(w)
# Dedup by POSITION VALUE, not by address: this session measured an exact
# 2x duplication (pairs 0x1000 apart holding byte-identical positions), so
# deduping on the address leaves every entity counted twice and the
# "exactly N agree" test can never match.
ded = {}
for pos, nm in ents:
b = os.pread(w.fd, 12, pos)
if len(b) < 12: continue
key = (nm, struct.unpack('>fff', b))
ded.setdefault(key, pos)
ents = [(pos, nm) for (nm, _), pos in ded.items()]
print('entities (definition-pointer enumeration):', len(ents))
c = collections.Counter(nm for _, nm in ents)
print('classes whose head-count equals %d: %s' % (n, [k for k, v in c.items() if v == n] or 'NONE'))
cand = sweep(w, ents, n)
print('offsets where exactly %d entities agree: %d' % (n, len(cand)))
json.dump([[o, v] for o, v in sorted(cand)], open('/tmp/flag_%s.json' % tag, 'w'))