#!/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: //run.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"