#!/usr/bin/env bash # make_twin.sh — #54 mirror-twin sparring partner generator. # # Builds a self-contained `SacTwin` bot dir inside the sample-bots archive so # RunTraining.java resolves it like any sample bot ($SAMPLE_BOTS_DIR/). # The twin is the SAME binary as SAC_LSTM_Bot but with: # - distinct identity (SacTwin.json; SACLSTM_BOT_JSON overrides the baked-in # src json — loadBotInfo gives json total precedence over env, #49 lesson) # - its OWN weights dir, seeded from a FROZEN copy of weights/sac_best.zip # (no checkpoint write races with the main bot) # - its OWN round_counter.txt (main liveness guard untouched) # # Re-running resets the twin to the frozen baseline (reproducible opponent). # # Usage: ./make_twin.sh [target-archive-dir] set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" TARGET="${1:-${SAMPLE_BOTS_DIR:-/home/davide/Projects/tank-royale/sample-bots/java/build/archive}}/SacTwin" BIN="$SCRIPT_DIR/SAC_LSTM_Bot" # nimble build -d:release output SEED="$SCRIPT_DIR/weights/sac_best.zip" # frozen baseline [ -x "$BIN" ] || { echo ">>> $BIN missing — run 'nimble build -d:release' first"; exit 1; } [ -f "$SEED" ] || { echo ">>> $SEED missing — need at least one eval'd checkpoint"; exit 1; } mkdir -p "$TARGET/weights" cp "$BIN" "$TARGET/SAC_LSTM_Bot" cat > "$TARGET/SacTwin.json" < "$TARGET/SacTwin.sh" <<'EOF' #!/bin/sh # Twin launcher (#54): identity + weights fully decoupled from the main bot. DIR="$(cd "$(dirname "$0")" && pwd)" export SACLSTM_BOT_JSON="$DIR/SacTwin.json" export SACLSTM_WEIGHTS_PATH="$DIR/weights/sac_latest.zip" exec "$DIR/SAC_LSTM_Bot" EOF chmod +x "$TARGET/SacTwin.sh" "$TARGET/SAC_LSTM_Bot" cp "$SEED" "$TARGET/weights/sac_latest.zip" cp "$SEED" "$TARGET/weights/sac_best.zip" echo 0 > "$TARGET/weights/round_counter.txt" echo ">>> twin ready: $TARGET (seeded from $(stat -c %y "$SEED" | cut -d. -f1) snapshot of sac_best.zip)"