#!/usr/bin/env bash # Batch Forge simulation for one deck variant. # # tools/run_forge.sh [games] [seed] [variant] # # deck_dir is the deck's own directory, e.g. decks/reaper-king-no-more. # variant defaults to "no-mikaeus". Writes into /decks//sim/: # .log full Forge output (every game) # .txt human report # .json machine-readable results set -euo pipefail DECK_DIR="${1:?usage: run_forge.sh [games] [seed] [variant]}" GAMES="${2:-24}" SEED="${3:-20260907}" VARIANT="${4:-no-mikaeus}" FORGE="$HOME/forge" FORGE_DECKS="$HOME/.forge/decks/commander" TOOLS="$(cd "$(dirname "$0")" && pwd)" DECK_DIR="$(cd "$DECK_DIR" && pwd)" STAMP="$(date -u +%Y%m%dT%H%M%SZ)" VDIR="$DECK_DIR/decks/$VARIANT" OUT="$VDIR/sim/$STAMP" [ -f "$VDIR/decklist.txt" ] || { echo "no decklist: $VDIR/decklist.txt" >&2; exit 1; } mkdir -p "$VDIR/sim" OPPONENTS=( "Faceless Menace [C19] [2019].dck" "Merciless Rage [C19] [2019].dck" "Arcane Maelstrom [C20] [2020].dck" ) # .dck filename and [metadata] Name= are derived from the deck (slug / # config.yaml `name:`), never hardcoded -- ask to_dck.py what it wrote. DCK_NAME="$(python3 "$TOOLS/to_dck.py" --deck "$DECK_DIR" "$VDIR/decklist.txt" \ -o "$FORGE_DECKS/$(basename "$DECK_DIR").dck" | sed -n 's/^wrote \(.*\) (.*/\1/p')" DCK_NAME="$(basename "$DCK_NAME")" echo "[$VARIANT] $GAMES games, 4-player Commander pod, seed $SEED" # stdbuf keeps the log readable while the batch runs -- without it the pipe # buffers everything until Forge exits and progress is invisible. ( cd "$FORGE" && timeout 7200 stdbuf -oL -eL sh forge.sh sim \ -d "$DCK_NAME" "${OPPONENTS[@]}" \ -f Commander -n "$GAMES" -s "$SEED" ) 2>&1 \ | stdbuf -oL grep -vE "WARNING:|not assigned to any set|Upcoming set|^Phase:|^Warning:|Correcting zone" \ | tee "$OUT.log" \ | stdbuf -oL grep -E "^Game Result:" || true python3 "$TOOLS/forge_report.py" "$OUT.log" "$OUT.json" "$OUT.txt" \ "$GAMES" "$SEED" "$VDIR/decklist.txt"