Files
SirRoboGarage/common_libs/tests/run_melee_bitbrain_ab.sh
SirStone da4a971ca9 MELEE A/B: BitBrain vs shipped Pattern rack — repo's first melee measurement (null)
Adds common_libs/tests/measure_melee_bitbrain_ab.nim (+ .sh driver, .py analyzer,
committed per-run fixtures) and docs/melee_bitbrain_ab.md.

Experiment: 4-bot Free-For-All (ModularBot + WaveSurfer + PatternMover +
RandomMover), 4 arms x 16 runs x 7 rounds, frozen ModularBot from git archive
HEAD (commit 0f5cfe3, binary 11bba27), shipped tfil movement in every run.
Arms differ only in the gun rack: pattern (shipped), bb_round, bb_ret, bb_learn.

Result: NOT DETECTABLE. Score (server round score = damage + survival bonus)
differs by -63..+33 pts (perm p=0.16-0.71) against an MDE of 151 (~5.1%).
Every arm finishes rank 1. Round wins hint BitBrain's way (112/112 and 111/112
vs 109/112) but p=0.225 (MW 0.080), half the 0.40-win MDE.

Liveness proven: rack boot lines flip (rack active melee = PATTERN / BITBRAIN),
every run faced 3 distinct targets and ~66-69 target changes, and the bb arms
logged one [bb-reset] reason=target_change per switch. The melee premise was
exercised; the fast adaptation bought no measurable score edge at this sample.
2026-09-26 00:35:44 +02:00

65 lines
2.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# run_melee_bitbrain_ab.sh — drive the melee A/B (see measure_melee_bitbrain_ab.nim).
#
# Builds ONE frozen ModularBot (from `git archive HEAD`), then runs one process
# per arm in parallel; each process owns its own Tank Royale server on an
# ephemeral port. Output: <outdir>/<arm>/run<N>.json (+ .bot.stdout.log).
#
# common_libs/tests/run_melee_bitbrain_ab.sh [outdir]
#
# Env: MELEE_RUNS (default 16), MELEE_ROUNDS (default 7), MELEE_ARMS (space list).
# Kills its own process groups on EXIT/INT/TERM (setsid per arm).
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
OUTDIR="${1:-/tmp/melee_bitbrain_ab}"
RUNS="${MELEE_RUNS:-16}"
ROUNDS="${MELEE_ROUNDS:-7}"
ARMS_STR="${MELEE_ARMS:-pattern bb_round bb_ret bb_learn}"
BIN="$HERE/measure_melee_bitbrain_ab"
[[ -x "$BIN" ]] || { echo "ERROR: build the runner first: nim c --path:common_libs $HERE/measure_melee_bitbrain_ab.nim" >&2; exit 1; }
mkdir -p "$OUTDIR"
echo "[driver] building frozen ModularBot…"
"$BIN" --outdir "$OUTDIR" --build-only
read -r -a ARMS <<< "$ARMS_STR"
PIDS=()
cleanup() {
local rc=$?
trap - EXIT INT TERM
for p in "${PIDS[@]:-}"; do
[[ -n "$p" ]] || continue
kill -TERM -- "-$p" 2>/dev/null || true
done
sleep 1
for p in "${PIDS[@]:-}"; do
[[ -n "$p" ]] || continue
kill -KILL -- "-$p" 2>/dev/null || true
done
# backstop: anything from this run's process group that outlived the runner
pkill -f "[m]easure_melee_bitbrain_ab.*$OUTDIR" 2>/dev/null || true
exit "$rc"
}
trap cleanup EXIT INT TERM
for arm in "${ARMS[@]}"; do
echo "[driver] launching arm $arm (runs=$RUNS rounds=$ROUNDS)"
setsid "$BIN" --outdir "$OUTDIR" --arm "$arm" \
--run-start 1 --run-count "$RUNS" --rounds "$ROUNDS" \
> "$OUTDIR/$arm.driver.log" 2>&1 &
PIDS+=("$!")
sleep 3 # stagger so concurrent servers do not race for the same free port
done
echo "[driver] all arms launched; waiting…"
FAIL=0
for p in "${PIDS[@]}"; do
wait "$p" || FAIL=$((FAIL + 1))
done
PIDS=()
echo "[driver] done (failed arm processes: $FAIL)"
exit "$FAIL"