tools+plan(#182): resolve-capture histogram classifier + verbatim player-physics rebuild plan

Slice 0 of the #182 verbatim rebuild. The classifier reproduces the design
baseline off acdream-crowd-resolve.jsonl (2883 move-intent resolves:
52.8% OK / 25.1% partial / 22.1% stuck / 107 airborne-stuck) — the A/B
'before' the rebuild measures against (retail target ~78% OK, 0 airborne-stuck).

The plan refines the design spec's §7: the airborne-stuck bleed is the
frames_stationary_fall counter (validate_transition increments; handle_all_collisions
zeros velocity at fsf>1), NOT the cached_velocity field (a separate reporting value).
Slices reorder accordingly; calc_friction (retail 0.25 vs acdream 0.0) is an
orthogonal L.3c divergence kept out of scope.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-07 13:37:15 +02:00
parent 80881ed6f1
commit 8bb8b20411
2 changed files with 1002 additions and 0 deletions

View file

@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""Classify ACDREAM_CAPTURE_RESOLVE JSONL into OK / partial / stuck / airborne-stuck.
The A/B measurement instrument for the #182 verbatim player-physics rebuild
(docs/superpowers/plans/2026-07-07-player-physics-update-verbatim-rebuild.md).
Each JSONL record (PhysicsResolveCapture.ResolveCaptureRecord) has:
input.{currentPos,targetPos,cellId,...}, bodyBefore/bodyAfter (PhysicsBodySnapshot
incl. velocity, slidingNormal, transientState), result.{position,cellId,isOnGround,
collisionNormalValid,collisionNormal}. Vector3 = {x,y,z}.
Buckets (move-intent records only, i.e. targetPos != currentPos):
OK reached target: dist(result.position, targetPos) <= EPS_REACH
partial advanced short: moved > EPS_MOVE and not OK (retail SLID)
stuck reverted: moved <= EPS_MOVE (retail COLLIDED-revert)
airborne-stuck subset of stuck: bodyBefore airborne w/ jump velocity into a
near-horizontal collision normal (the falling-animation wedge)
Retail target (tools/cdb/retail-crowd-jump3.cdb):
~78% OK, 12.7% COLLIDED (~stuck), 8.8% SLID (~partial), 0 airborne-stuck.
Usage: py tools/analyze_resolve_capture.py [capture.jsonl]
"""
import sys
import json
import math
EPS_REACH = 0.02 # 2 cm — "reached target"
EPS_MOVE = 0.01 # 1 cm — "advanced at all"
def dist(a, b):
return math.sqrt((a["x"] - b["x"]) ** 2 + (a["y"] - b["y"]) ** 2 + (a["z"] - b["z"]) ** 2)
def classify(rec):
i = rec["input"]
if dist(i["targetPos"], i["currentPos"]) <= EPS_MOVE:
return None # zero-motion rest tick — not a move-intent record
r = rec["result"]
moved = dist(r["position"], i["currentPos"])
reached = dist(r["position"], i["targetPos"]) <= EPS_REACH
if reached:
return "ok"
if moved > EPS_MOVE:
return "partial"
# reverted / stuck. airborne-stuck = a stuck frame while AIRBORNE (OnWalkable bit clear):
# the "stuck in the falling animation" wedge. The near-horizontal creature normal is the
# cause and the jump velocity the symptom, but the sliding normal flips frame-to-frame so
# neither is reliable every frame — the airborne (not-grounded) revert is the stable marker.
bb = rec.get("bodyBefore") or {}
on_walkable = bb.get("transientState", 0) & 0x2 # TransientStateFlags.OnWalkable
if not on_walkable:
return "airborne-stuck"
return "stuck"
def main(path):
counts = {"ok": 0, "partial": 0, "stuck": 0, "airborne-stuck": 0}
total_move = 0
total_records = 0
with open(path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
try:
rec = json.loads(line)
except json.JSONDecodeError:
continue
total_records += 1
c = classify(rec)
if c is None:
continue
total_move += 1
if c == "airborne-stuck":
counts["airborne-stuck"] += 1
counts["stuck"] += 1 # airborne-stuck is a subset of stuck for the % columns
else:
counts[c] += 1
print(f"total records: {total_records}")
if total_move == 0:
print("no move-intent records")
return
print(f"move-intent resolves: {total_move}")
for k in ("ok", "partial", "stuck"):
print(f" {k:16s} {counts[k]:6d} {100.0 * counts[k] / total_move:5.1f}%")
print(f" {'airborne-stuck':16s} {counts['airborne-stuck']:6d} (frames; subset of stuck)")
print("retail target: ok ~78% partial ~9% stuck ~13% airborne-stuck 0")
if __name__ == "__main__":
main(sys.argv[1] if len(sys.argv) > 1 else "acdream-crowd-resolve.jsonl")