select_puzzles.sh (3914B)
1 #!/usr/bin/env bash 2 set -euo pipefail 3 4 COUNT="${1:-10}" 5 DEFAULT_CANDIDATE_DIR="Generated" 6 PICKMAKE_EXECUTABLE=".build/release/Pickmake" 7 CLUEMAKE_EXECUTABLE=".build/release/Cluemake" 8 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 9 CROSSMAKE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" 10 CALLER_DIR="$(pwd)" 11 OUTPUT_DIR="${CROSSMAKE_DIR}/Picked" 12 CLEAN=false 13 CLUE_AGENT="${CROSSMAKE_CLUE_AGENT:-codex}" 14 CLUE_TIMEOUT="${CROSSMAKE_CLUE_TIMEOUT:-300}" 15 CLUE_ATTEMPTS="${CROSSMAKE_CLUE_ATTEMPTS:-3}" 16 CLUE_PUZZLES=true 17 18 if ! [[ "$COUNT" =~ ^[0-9]+$ ]] || ((10#$COUNT < 1)); then 19 echo "Usage: $0 [positive-puzzle-count] [-o output-dir] [--clean] [--clue-agent codex|claude] [--clue-timeout seconds] [--skip-clues] [candidate-dir-or-xd-file ...]" >&2 20 exit 1 21 fi 22 23 resolve_path() { 24 if [[ "$1" = /* ]]; then 25 echo "$1" 26 else 27 echo "${CALLER_DIR}/$1" 28 fi 29 } 30 31 shift || true 32 inputs=() 33 while (($# > 0)); do 34 case "$1" in 35 -o|--into) 36 if (($# < 2)); then 37 echo "$1 requires a directory argument" >&2 38 exit 1 39 fi 40 OUTPUT_DIR="$(resolve_path "$2")" 41 shift 2 42 ;; 43 --clean) 44 CLEAN=true 45 shift 46 ;; 47 --clue-agent) 48 if (($# < 2)); then 49 echo "$1 requires codex or claude" >&2 50 exit 1 51 fi 52 CLUE_AGENT="$2" 53 shift 2 54 ;; 55 --clue-timeout) 56 if (($# < 2)); then 57 echo "$1 requires a timeout in seconds" >&2 58 exit 1 59 fi 60 CLUE_TIMEOUT="$2" 61 shift 2 62 ;; 63 --skip-clues) 64 CLUE_PUZZLES=false 65 shift 66 ;; 67 *) 68 inputs+=("$(resolve_path "$1")") 69 shift 70 ;; 71 esac 72 done 73 74 if [[ "$CLUE_AGENT" != "codex" && "$CLUE_AGENT" != "claude" ]]; then 75 echo "Clue agent must be codex or claude" >&2 76 exit 1 77 fi 78 79 if ! [[ "$CLUE_TIMEOUT" =~ ^[0-9]+([.][0-9]+)?$ ]]; then 80 echo "Clue timeout must be a positive number" >&2 81 exit 1 82 fi 83 84 if ! [[ "$CLUE_ATTEMPTS" =~ ^[0-9]+$ ]] || ((10#$CLUE_ATTEMPTS < 1)); then 85 echo "CROSSMAKE_CLUE_ATTEMPTS must be a positive integer" >&2 86 exit 1 87 fi 88 89 if ((${#inputs[@]} == 0)); then 90 inputs+=("${CROSSMAKE_DIR}/${DEFAULT_CANDIDATE_DIR}") 91 fi 92 93 cd "$CROSSMAKE_DIR" 94 95 echo "Building Pickmake and Cluemake in release mode" >&2 96 swift build -c release --product Pickmake --product Cluemake 97 98 if [[ ! -x "$PICKMAKE_EXECUTABLE" ]]; then 99 echo "Pickmake executable not found after build: $PICKMAKE_EXECUTABLE" >&2 100 exit 1 101 fi 102 103 if [[ "$CLUE_PUZZLES" == true && ! -x "$CLUEMAKE_EXECUTABLE" ]]; then 104 echo "Cluemake executable not found after build: $CLUEMAKE_EXECUTABLE" >&2 105 exit 1 106 fi 107 108 selected_paths="$("$PICKMAKE_EXECUTABLE" --count "$COUNT" --verbose "${inputs[@]}")" 109 110 if [[ -z "$selected_paths" ]]; then 111 echo "No puzzles selected; nothing copied." >&2 112 exit 1 113 fi 114 115 mkdir -p "$OUTPUT_DIR" 116 117 if [[ "$CLEAN" == true ]]; then 118 rm -f "$OUTPUT_DIR"/*.xd 119 echo "Cleaned existing .xd files from ${OUTPUT_DIR}." >&2 120 fi 121 122 copied_count=0 123 while IFS= read -r selected_path; do 124 [[ -n "$selected_path" ]] || continue 125 output_path="${OUTPUT_DIR}/${selected_path##*/}" 126 cp "$selected_path" "$output_path" 127 echo "Copied ${selected_path} -> ${output_path}" 128 if [[ "$CLUE_PUZZLES" == true ]]; then 129 echo "Generating clues for ${output_path} with ${CLUE_AGENT}" >&2 130 clue_attempt=1 131 until "$CLUEMAKE_EXECUTABLE" --agent "$CLUE_AGENT" --timeout "$CLUE_TIMEOUT" "$output_path"; do 132 if ((clue_attempt >= 10#$CLUE_ATTEMPTS)); then 133 echo "Failed to generate clues for ${output_path} after ${CLUE_ATTEMPTS} attempt(s)." >&2 134 exit 1 135 fi 136 clue_attempt=$((clue_attempt + 1)) 137 echo "Retrying clues for ${output_path} (${clue_attempt}/${CLUE_ATTEMPTS})" >&2 138 done 139 fi 140 copied_count=$((copied_count + 1)) 141 done <<<"$selected_paths" 142 143 if [[ "$CLUE_PUZZLES" == true ]]; then 144 echo "Copied and clued ${copied_count} puzzle(s) into ${OUTPUT_DIR}." >&2 145 else 146 echo "Copied ${copied_count} puzzle(s) into ${OUTPUT_DIR}." >&2 147 fi