72 lines
3.3 KiB
Bash
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
|