lgbot: auto recording while two humans fight (lgbot_record 2 / QL_RECORD), analyzer pairs any opponents

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
esefandClaude Opus 5.5 committed 2026-10-15 18:37:40 +03:00
1 parent 3535940331
commit acf625d72b
8 files changed
+133 -51

No files matched your search

+31 -24
View File
@@ -2,7 +2,8 @@
"""Compare how you and lgbot move, from a recording (set lgbot_record 1, play, set lgbot_record 0).
python3 tools/lgbot_rec.py [home/lgbot-rec.csv]
Only fight time counts: both alive, within 1000 units and about the same floor. Sideways/toward are
relative to the opponent (you vs the bot, the bot vs the nearest human). Prints numbers only.
relative to the nearest opponent (human or bot), so human-vs-human recordings (lgbot_record 2) work
too: "humans" is every human together. Prints numbers only.
"""
import math, sys
@@ -40,25 +41,31 @@ def parse(lines):
def fight_frames(session):
"""-> {"human": [...], "bot": [...]}, frames (t, x, y, z, vx, vy, vz, ox, oy, state) in time order."""
out = {"human": [], "bot": []}
"""-> {who: [...]} ("bot", "h1", ...), frames (t, x, y, z, vx, vy, vz, ox, oy, state) in time order.
Each alive player is measured against the nearest alive player in fight range (human or bot)."""
out = {}
for t in sorted(session):
rows = session[t]
bot = rows.get("bot")
if not bot or not bot[0]:
continue
best = None
for who, r in rows.items():
if who == "bot" or not r[0]:
continue
d = math.hypot(r[2] - bot[2], r[3] - bot[3])
if best is None or d < best[0]:
best = (d, r)
if best is None or best[0] > FIGHT_DIST or abs(best[1][4] - bot[4]) > FIGHT_DZ:
continue
h = best[1]
out["human"].append((t,) + h[2:8] + (bot[2], bot[3], ""))
out["bot"].append((t,) + bot[2:8] + (h[2], h[3], bot[8]))
alive = [(who, r) for who, r in session[t].items() if r[0]]
for who, me in alive:
best = None
for other, r in alive:
if other == who or abs(r[4] - me[4]) > FIGHT_DZ:
continue
d = math.hypot(r[2] - me[2], r[3] - me[3])
if d <= FIGHT_DIST and (best is None or d < best[0]):
best = (d, r)
if best is not None:
out.setdefault(who, []).append(
(t,) + me[2:8] + (best[1][2], best[1][3], me[8]))
return out
def by_kind(per_player):
"""{who: frames} -> {"human": frames, "bot": frames}: one player after another, so time jumps
back at each join and the gap rule keeps players (and sessions) apart."""
out = {"human": [], "bot": []}
for who in sorted(per_player):
out["bot" if who == "bot" else "human"].extend(per_player[who])
return out
@@ -143,7 +150,7 @@ ROWS = [("seconds", "fight time s"), ("hold_ms", "strafe hold ms p10/p50/p90"),
def report(human, bot):
lines = ["%-28s %-22s %s" % ("", "you", "lgbot")]
lines = ["%-28s %-22s %s" % ("", "humans", "lgbot")]
for k, label in ROWS:
lines.append("%-28s %-22s %s" % (label, fmt(human.get(k)), fmt(bot.get(k))))
return "\n".join(lines)
@@ -153,11 +160,11 @@ def main(argv):
path = argv[1] if len(argv) > 1 else "home/lgbot-rec.csv"
with open(path) as f:
sessions = parse(f)
both = {"human": [], "bot": []}
per_player = {}
for s in sessions:
fr = fight_frames(s)
for k in both:
both[k].extend(fr[k]) # t restarts at 0 each session: the gap rule keeps them apart
for who, frames in fight_frames(s).items():
per_player.setdefault(who, []).extend(frames) # t restarts each session: a gap
both = by_kind(per_player)
print("%d session(s)" % len(sessions))
print(report(analyze(both["human"]), analyze(both["bot"])))
return 0