178 lines
6.3 KiB
Bash
Executable file
178 lines
6.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# autoplay-batch.sh — Run auto_play N times with different seeds and collect result JSON files.
|
|
#
|
|
# Usage: tools/autoplay-batch.sh [count=3] [turn_limit=500] [results_dir=/tmp/autoplay_batch]
|
|
#
|
|
# Environment:
|
|
# AUTOPLAY_HOST — If set (e.g. "lilith@apricot.local"), run each game via SSH using
|
|
# /tmp/run_ap3.sh on the remote host and scp results back.
|
|
# If unset, run locally via flatpak (Linux only).
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
GAME_DIR="$PROJECT_DIR/src/game"
|
|
|
|
COUNT="${1:-3}"
|
|
TURN_LIMIT="${2:-500}"
|
|
RESULTS_DIR="${3:-/tmp/autoplay_batch}"
|
|
|
|
if ! [[ "$COUNT" =~ ^[0-9]+$ ]] || [ "$COUNT" -lt 1 ]; then
|
|
echo "ERROR: count must be a positive integer (got '$COUNT')" >&2
|
|
exit 2
|
|
fi
|
|
if ! [[ "$TURN_LIMIT" =~ ^[0-9]+$ ]] || [ "$TURN_LIMIT" -lt 1 ]; then
|
|
echo "ERROR: turn_limit must be a positive integer (got '$TURN_LIMIT')" >&2
|
|
exit 2
|
|
fi
|
|
|
|
AUTOPLAY_HOST="${AUTOPLAY_HOST:-}"
|
|
SAFETY_TIMEOUT=$(( TURN_LIMIT * 2 + 300 ))
|
|
|
|
# IMPORTANT: Flatpak sandboxes /tmp — result files written from inside the
|
|
# sandbox to /tmp silently disappear. Use $HOME/tmp for local flatpak runs.
|
|
if [ -z "$AUTOPLAY_HOST" ] && [[ "$RESULTS_DIR" == /tmp/* ]]; then
|
|
RESULTS_DIR="$HOME/tmp/autoplay_batch"
|
|
echo "INFO: Local flatpak run — redirecting results dir to $RESULTS_DIR (flatpak cannot write to /tmp)" >&2
|
|
fi
|
|
|
|
mkdir -p "$RESULTS_DIR"
|
|
|
|
echo "============================================================"
|
|
echo "Autoplay Batch: $COUNT games, turn_limit=$TURN_LIMIT"
|
|
echo "Results: $RESULTS_DIR"
|
|
if [ -n "$AUTOPLAY_HOST" ]; then
|
|
echo "Mode: remote SSH ($AUTOPLAY_HOST)"
|
|
else
|
|
echo "Mode: local flatpak"
|
|
fi
|
|
echo "Safety timeout: ${SAFETY_TIMEOUT}s per game"
|
|
echo "============================================================"
|
|
|
|
_kill_stale_procs() {
|
|
# Kill stale weston/godot from previous runs (local only)
|
|
pkill -f "weston.*godot-headless" 2>/dev/null || true
|
|
pkill -f "org.godotengine.Godot" 2>/dev/null || true
|
|
sleep 0.5
|
|
}
|
|
|
|
_run_local() {
|
|
local seed="$1"
|
|
local seed_dir="$2"
|
|
|
|
if ! command -v flatpak >/dev/null 2>&1; then
|
|
echo "ERROR: flatpak not installed. Set AUTOPLAY_HOST to run on a remote Linux host." >&2
|
|
exit 1
|
|
fi
|
|
|
|
_kill_stale_procs
|
|
|
|
echo "[seed $seed] Starting weston (headless)..."
|
|
WESTON_SOCKET="godot-headless-$$"
|
|
weston --backend=headless --socket="$WESTON_SOCKET" --width=1920 --height=1080 \
|
|
>"$seed_dir/weston.log" 2>&1 &
|
|
WESTON_PID=$!
|
|
sleep 1
|
|
|
|
echo "[seed $seed] Launching Godot (timeout ${SAFETY_TIMEOUT}s)..."
|
|
WAYLAND_DISPLAY="$WESTON_SOCKET" \
|
|
XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" \
|
|
timeout "$SAFETY_TIMEOUT" flatpak run --user \
|
|
--socket=wayland \
|
|
--env=WAYLAND_DISPLAY="$WESTON_SOCKET" \
|
|
--env=AUTO_PLAY=true \
|
|
--env=AUTO_PLAY_SEED="$seed" \
|
|
--env=AUTO_PLAY_TURN_LIMIT="$TURN_LIMIT" \
|
|
--env=AUTO_PLAY_DIR="$seed_dir" \
|
|
org.godotengine.Godot \
|
|
--path "$GAME_DIR" \
|
|
--rendering-method gl_compatibility \
|
|
--headless \
|
|
>"$seed_dir/game.log" 2>&1 || {
|
|
local exit_code=$?
|
|
echo "[seed $seed] Godot exited with code $exit_code" >&2
|
|
}
|
|
|
|
kill "$WESTON_PID" 2>/dev/null || true
|
|
wait "$WESTON_PID" 2>/dev/null || true
|
|
}
|
|
|
|
_run_remote() {
|
|
local seed="$1"
|
|
local seed_dir="$2"
|
|
|
|
echo "[seed $seed] Running via SSH on $AUTOPLAY_HOST..."
|
|
|
|
# Build a remote results dir that run_ap3.sh can write to (not /tmp — Flatpak sandbox)
|
|
local remote_seed_dir
|
|
remote_seed_dir="\$HOME/tmp/autoplay_batch/seed_${seed}"
|
|
|
|
ssh "$AUTOPLAY_HOST" "
|
|
set -euo pipefail
|
|
mkdir -p '$remote_seed_dir'
|
|
if [ ! -f /tmp/run_ap3.sh ]; then
|
|
echo 'ERROR: /tmp/run_ap3.sh not found on $AUTOPLAY_HOST' >&2
|
|
exit 1
|
|
fi
|
|
AUTO_PLAY=true \
|
|
AUTO_PLAY_SEED='$seed' \
|
|
AUTO_PLAY_TURN_LIMIT='$TURN_LIMIT' \
|
|
AUTO_PLAY_DIR='$remote_seed_dir' \
|
|
bash /tmp/run_ap3.sh >'$remote_seed_dir/game.log' 2>&1
|
|
" || {
|
|
echo "[seed $seed] SSH run exited with error — see $seed_dir/game.log after scp" >&2
|
|
}
|
|
|
|
echo "[seed $seed] Fetching results from $AUTOPLAY_HOST..."
|
|
scp -r "$AUTOPLAY_HOST:\$HOME/tmp/autoplay_batch/seed_${seed}/." "$seed_dir/" \
|
|
>/dev/null 2>&1 || {
|
|
echo "WARNING: scp failed for seed $seed — result may be missing" >&2
|
|
}
|
|
}
|
|
|
|
# ── Main loop ────────────────────────────────────────────────────────────────
|
|
|
|
FAILED_SEEDS=()
|
|
|
|
for seed in $(seq 1 "$COUNT"); do
|
|
seed_dir="$RESULTS_DIR/seed_${seed}"
|
|
mkdir -p "$seed_dir"
|
|
echo ""
|
|
echo "[$(date +%H:%M:%S)] === Game $seed/$COUNT (seed=$seed) ==="
|
|
|
|
if [ -n "$AUTOPLAY_HOST" ]; then
|
|
_run_remote "$seed" "$seed_dir"
|
|
else
|
|
_run_local "$seed" "$seed_dir"
|
|
fi
|
|
|
|
# Look for timestamped result file (result_<stamp>_seed<N>.json) or legacy (result_<N>.json)
|
|
result_file="$(ls -1 "$seed_dir"/result_*_seed${seed}.json 2>/dev/null | tail -n1)"
|
|
if [ -z "$result_file" ] && [ -f "$seed_dir/result_${seed}.json" ]; then
|
|
result_file="$seed_dir/result_${seed}.json"
|
|
fi
|
|
if [ -n "$result_file" ] && [ -f "$result_file" ]; then
|
|
echo "[seed $seed] OK — result written to $result_file"
|
|
else
|
|
echo "[seed $seed] MISSING result file (no result_*_seed${seed}.json found)" >&2
|
|
FAILED_SEEDS+=("$seed")
|
|
fi
|
|
done
|
|
|
|
# ── Summary ──────────────────────────────────────────────────────────────────
|
|
|
|
echo ""
|
|
echo "============================================================"
|
|
PRODUCED=$(( COUNT - ${#FAILED_SEEDS[@]} ))
|
|
echo "Batch complete: $PRODUCED/$COUNT games produced result.json"
|
|
echo "Results: $RESULTS_DIR"
|
|
echo "============================================================"
|
|
|
|
if [ ${#FAILED_SEEDS[@]} -gt 0 ]; then
|
|
echo "ERROR: Missing result.json for seeds: ${FAILED_SEEDS[*]}" >&2
|
|
echo " Check game.log in each seed dir for details." >&2
|
|
exit 1
|
|
fi
|
|
|
|
exit 0
|