Files
BT411/scratchpad/night15/kd_bench.sh
T

72 lines
3.3 KiB
Bash

#!/usr/bin/env bash
# #162: the K/D-chain invariants bench -- HARD PASS/FAIL, no blind greps.
# 2-node: A autofires missiles into B (multi-round volleys = the duplicate
# trigger window); B respawns each death. Invariants asserted from receipts:
# I1: victim PLAYER_DEAD count == victim DEATH-event count (one per death)
# I2: every death applies the -500 cost ([deathcost] APPLYING, count match)
# I3: shooter SCORE type=2 count == kill count (one credit per kill)
# I4: no SWALLOWED warnings (the dedup tripwire stays silent)
# I5: victim running total drops ~500 per death (arithmetic witness)
. /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 kd_a.log kd_b.log kd_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 KD.EGG
sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/" KD.EGG
grep -q "advancedDamage=1" KD.EGG || { echo "FAIL: bench egg lacks advancedDamage=1"; exit 1; }
( export BT_GOTO=enemy BT_GOTO_STOP=80 BT_SCORE_LOG=1 BT_DEATH_LOG=1 BT_MATCHLOG=1
bt_launch kd_b.log KD.EGG 0x0C -net 1601 )
sleep 2
( 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 kd_a.log KD.EGG 0x03 -net 1501 )
sleep 5
python ../tools/btconsole.py KD.EGG 127.0.0.1:1501 127.0.0.1:1601 > kd_r.log 2>&1 &
R=$!; sleep 300; 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 = swall = applying = skipped = type2 = 0
drops = []
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
for path in ("kd_a.log", "kd_b.log"):
txt = io.open(path, encoding="latin-1", errors="replace").read()
swall += txt.count("SWALLOWED")
applying += txt.count("-> APPLYING")
skipped += txt.count("-> SKIPPED")
for m in re.finditer(r"\[deathcost\].*scoreBefore=([\d.\-]+)\s+-> APPLYING", txt):
drops.append(float(m.group(1)))
print("=== #162 K/D INVARIANTS ===")
print("real deaths (DEATH inst=M):", deaths)
print("PLAYER_DEAD increments :", pdead)
print("deathcost APPLYING/SKIP :", applying, "/", skipped)
print("SCORE type=2 (kill arms) :", type2)
print("SWALLOWED warnings :", swall)
fails = []
if deaths == 0:
fails.append("NO DEATHS -- bench produced no kills; rerun")
if pdead != deaths:
fails.append("I1 FAIL: PLAYER_DEAD %d != deaths %d" % (pdead, deaths))
if applying != deaths:
fails.append("I2 FAIL: deathcost APPLYING %d != deaths %d" % (applying, deaths))
if type2 != deaths:
fails.append("I3 FAIL: kill credits %d != deaths %d" % (type2, deaths))
if swall != 0:
fails.append("I4 FAIL: %d duplicate deaths SWALLOWED (dedup tripwire fired)" % swall)
print("VERDICT:", "PASS -- all invariants hold" if not fails else "; ".join(fails))
sys.exit(0 if not fails else 1)
PY