#!/usr/bin/env bash # #162 4-NODE RETEST -- the K/D invariants under bystanders, which the 2-node # bench structurally cannot see (every kill involved both nodes there). # A (shooter, autofire missiles) kills B (walker, respawns); C and D are # BYSTANDERS parked at far dropzones. All 2-node invariants hold, plus: # I6 BYSTANDER CONVERGENCE: C and D's replicant mirrors (SBMIRROR) reach # A.kills == kill count and B.deaths == death count -- the #45 record # is the only way bystanders learn tallies. # I7 ALL-NODE AGREEMENT: no node ever mirrors a tally HIGHER than the # authoritative counts (no phantom kills surviving on any pod). # I8 DELIVERY AUDIT: DMG receipts on the victim are inst=M only -- any # inst=R delivery is the audit's unmapped replicant path going live. 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 kd4_a.log kd4_b.log kd4_c.log kd4_d.log kd4_r.log mkdir -p /c/git/bt411/scratchpad/night15/mlbak && mv matchlog_*.txt /c/git/bt411/scratchpad/night15/mlbak/ 2>/dev/null bt_expert_egg MP.EGG KD4.EGG sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/" KD4.EGG python - <<'EOF' import io, re txt = io.open('KD4.EGG', encoding='latin-1').read() # clone the 1602 pilot page for 1702/1802 (names C/D, far dropzones) m = re.search(r"\[127\.0\.0\.1:1602\](?:\n[^\[\n][^\n]*)*", txt) page = m.group(0) for addr, name, dz in (("127.0.0.1:1702", "Bystc", "one"), ("127.0.0.1:1802", "Bystd", "one")): p = page.replace("[127.0.0.1:1602]", "[%s]" % addr) p = re.sub(r"name=\S+", "name=%s" % name, p) p = re.sub(r"dropzone=\S+", "dropzone=%s" % dz, p) txt += "\n" + p + "\n" txt = txt.replace("pilot=127.0.0.1:1602", "pilot=127.0.0.1:1602\npilot=127.0.0.1:1702\npilot=127.0.0.1:1802") io.open('KD4.EGG', 'w', encoding='latin-1').write(txt) import sys pages = txt.count("advancedDamage=1") print("pilot pages with advancedDamage=1:", pages) sys.exit(0 if pages >= 4 else 1) EOF [ $? -eq 0 ] || { echo "FAIL: egg extension broke advancedDamage stamping"; exit 1; } # D + C: bystanders -- present, parked, watching (mirrors only) ( export BT_SCORE_LOG=1 BT_DEATH_LOG=1 BT_MATCHLOG=1 bt_launch kd4_d.log KD4.EGG 0xC0 -net 1801 ) sleep 2 ( export BT_SCORE_LOG=1 BT_DEATH_LOG=1 BT_MATCHLOG=1 bt_launch kd4_c.log KD4.EGG 0x30 -net 1701 ) sleep 2 # B: the victim -- walks at A, no weapons held ( export BT_GOTO=enemy BT_GOTO_STOP=80 BT_SCORE_LOG=1 BT_DEATH_LOG=1 BT_MATCHLOG=1 bt_launch kd4_b.log KD4.EGG 0x0C -net 1601 ) sleep 2 # A: the shooter ( export BT_GOTO=enemy BT_GOTO_STOP=100 BT_AUTOFIRE=1 BT_AF_MISSILE=1 BT_AF_PERIOD=4 export BT_SCORE_LOG=1 BT_DEATH_LOG=1 BT_MATCHLOG=1 bt_launch kd4_a.log KD4.EGG 0x03 -net 1501 ) sleep 5 python ../tools/btconsole.py KD4.EGG 127.0.0.1:1501 127.0.0.1:1601 127.0.0.1:1701 127.0.0.1:1801 > kd4_r.log 2>&1 & R=$!; sleep 360; kill $R 2>/dev/null; sleep 2 bt_kill_ours; sleep 2; taskkill //F //IM btl4.exe >/dev/null 2>&1 python - <<'PY' import glob, io, re, sys deaths = pdead = type2 = swall = applying = skipped = dmg_r = 0 mirrors = {} # (log, player) -> max kills/deaths mirrored for path in glob.glob("matchlog_*.txt"): for line in io.open(path, encoding="latin-1", errors="replace"): if line.startswith("DEATH ") and "inst=M" in line: deaths += 1 elif line.startswith("PLAYER_DEAD"): pdead += 1 elif line.startswith("SCORE") and "type=2" in line: type2 += 1 elif line.startswith("DMG") and "inst=R" in line: dmg_r += 1 elif line.startswith("SBMIRROR"): m = re.search(r"player=(\d+:\d+) kills=(\d+) deaths=(\d+)", line) if m: key = (path, m.group(1)) k, d = int(m.group(2)), int(m.group(3)) pk, pd = mirrors.get(key, (0, 0)) mirrors[key] = (max(pk, k), max(pd, d)) for path in ("kd4_a.log", "kd4_b.log", "kd4_c.log", "kd4_d.log"): txt = io.open(path, encoding="latin-1", errors="replace").read() swall += txt.count("SWALLOWED") applying += txt.count("-> APPLYING") skipped += txt.count("-> SKIPPED") print("=== #162 4-NODE K/D INVARIANTS ===") print("real deaths / PLAYER_DEAD / type2 / APPLYING:", deaths, pdead, type2, applying) print("SWALLOWED:", swall, " DMG inst=R:", dmg_r) maxk = {}; maxd = {} for (path, player), (k, d) in sorted(mirrors.items()): print(" mirror %-28s %-6s kills<=%d deaths<=%d" % (path[-16:], player, k, d)) maxk[player] = max(maxk.get(player, 0), k) maxd[player] = max(maxd.get(player, 0), d) fails = [] if deaths == 0: fails.append("NO DEATHS -- rerun") if pdead != deaths: fails.append("I1 FAIL: PLAYER_DEAD %d != deaths %d" % (pdead, deaths)) if applying != deaths: fails.append("I2 FAIL: APPLYING %d != deaths %d" % (applying, deaths)) if type2 != deaths: fails.append("I3 FAIL: type2 %d != deaths %d" % (type2, deaths)) if swall: fails.append("I4 FAIL: %d SWALLOWED" % swall) # authoritative per-player tallies from the receipts themselves: # deaths: max PLAYER_DEAD tally per player; kills: max kills= on its own SCORE rows auth_d = {}; auth_k = {} for path in glob.glob("matchlog_*.txt"): for line in io.open(path, encoding="latin-1", errors="replace"): m = re.search(r"PLAYER_DEAD .*player=(\d+:\d+) .*tally=(\d+)", line) if m: auth_d[m.group(1)] = max(auth_d.get(m.group(1), 0), int(m.group(2))) m = re.search(r"SCORE .*player=(\d+:\d+) type=2 .*kills=(\d+)", line) if m: auth_k[m.group(1)] = max(auth_k.get(m.group(1), 0), int(m.group(2))) print("authoritative kills:", auth_k, " deaths:", auth_d) # I6: for every player with authority, SOME bystander mirror reaches it for pl, want in auth_k.items(): got = max((k for (p, q), (k, d) in mirrors.items() if q == pl), default=0) if want and got != want: fails.append("I6 FAIL: %s kills mirrored %d != %d" % (pl, got, want)) for pl, want in auth_d.items(): got = max((d for (p, q), (k, d) in mirrors.items() if q == pl), default=0) if want and got != want: fails.append("I6 FAIL: %s deaths mirrored %d != %d" % (pl, got, want)) # I7: no mirror exceeds authority for (p, pl), (k, d) in mirrors.items(): if k > auth_k.get(pl, 0): fails.append("I7 FAIL: %s kills mirror %d > auth %d" % (pl, k, auth_k.get(pl, 0))) if d > auth_d.get(pl, 0): fails.append("I7 FAIL: %s deaths mirror %d > auth %d" % (pl, d, auth_d.get(pl, 0))) if dmg_r: fails.append("I8 FLAG: %d DMG inst=R deliveries" % dmg_r) print("VERDICT:", "PASS -- all 4-node invariants hold" if not fails else "; ".join(sorted(set(fails)))) sys.exit(0 if not fails else 1) PY