← poker-tutor
#!/usr/bin/env bash
# Cuts the App Store screenshots, so they can be remade after a change rather than being a folder
# of stale PNGs nobody dares regenerate.
#
# It seeds a scripted player's history first: the honest first-run screens are all dashes and empty
# states, which is right for a person and useless as an advertisement.
#
# Six screens are opened by name through POKERTUTOR_SCREEN. The other two — the felt with a
# decision on it, and the coach's review — need a game behind them, so they are asked for as scenes
# through POKERTUTOR_DEMO: the app plays a fixed-seed game against itself until the scene is on the
# table (see Sources/PokerUI/Demo.swift). A test plays both scenes headlessly, for both products,
# so a seed that stops producing them fails the suite before it produces a blank felt here.
#
# Two products come out of this project — Poker Tutor (tournaments) and Poker Tutor Cash — and
# each has its own scheme, bundle id, history folder and set of shots. The product is the first
# argument; the cash product's felt is shot at a 50/100 fixed-limit table and its review at a
# 50/100 no-limit one.
#
# Scripts/make-screenshots.sh # tournament, iPhone 17 Pro Max, App/screenshots
# Scripts/make-screenshots.sh cash # Poker Tutor Cash → App/screenshots-cash
# Scripts/make-screenshots.sh cash "iPhone 17 Pro Max" /tmp/shots
set -euo pipefail
PRODUCT="${1:-tournament}"
DEVICE="${2:-iPhone 17 Pro Max}"
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
case "$PRODUCT" in
tournament)
SCHEME=PokerTutorPhone; APP=PokerTutor.app; BID=com.thedigitaladlab.pokertutor
FOLDER=PokerTutor; OUT="${3:-$ROOT/App/screenshots}"; FORMAT_FLAGS=""; SEED=3
;;
cash)
SCHEME=PokerTutorCashPhone; APP=PokerTutorCash.app; BID=com.thedigitaladlab.pokertutor.cash
FOLDER=PokerTutorCash; OUT="${3:-$ROOT/App/screenshots-cash}"
# Seed 9: a sound player who happened to run a little good over 150 hands, so the ladder's
# win-rate line reads as an advertisement rather than as a bad evening (seed 3 ran −52/100).
FORMAT_FLAGS="--format cash --stakes 50/100 --structure noLimit"; SEED=9
;;
*) echo "product must be tournament or cash"; exit 1 ;;
esac
UDID=$(xcrun simctl list devices available | grep -F "$DEVICE (" | head -1 | grep -oE '[0-9A-F-]{36}')
[ -n "$UDID" ] || { echo "no simulator called $DEVICE"; exit 1; }
mkdir -p "$OUT"
xcrun simctl boot "$UDID" 2>/dev/null || true
open -a Simulator
sleep 6
swift build -c release --package-path "$ROOT" >/dev/null
xcodebuild -project "$ROOT/App/PokerTutorPhone.xcodeproj" -scheme "$SCHEME" \
-destination "id=$UDID" -derivedDataPath "$ROOT/.screenshots-build" build >/dev/null
xcrun simctl install "$UDID" "$ROOT/.screenshots-build/Build/Products/Debug-iphonesimulator/$APP"
# A scripted player with a mild habit, so the scorecard has something honest on it. The cash
# history is several sittings, each cashed out, so Home's "last session" line is real.
# shellcheck disable=SC2086
"$ROOT/.build/release/pokerlab" demo-history --out /tmp/pt-demo.jsonl --hands 150 --player control --seed "$SEED" $FORMAT_FLAGS >/dev/null
DATA=$(xcrun simctl get_app_container "$UDID" "$BID" data)
mkdir -p "$DATA/Library/Application Support/$FOLDER"
cp /tmp/pt-demo.jsonl "$DATA/Library/Application Support/$FOLDER/hands.jsonl"
# The screen is handed over as an environment variable at launch. Neither `defaults write` nor
# writing the container plist by hand survives: the simulator caches an app's preferences and
# discards a write made from outside, which looks precisely like the app ignoring the setting.
shoot() { # shoot <screen-rawvalue> <filename>
xcrun simctl terminate "$UDID" "$BID" >/dev/null 2>&1 || true
sleep 1
SIMCTL_CHILD_POKERTUTOR_SCREEN="$1" xcrun simctl launch "$UDID" "$BID" >/dev/null
sleep 4
xcrun simctl io "$UDID" screenshot "$OUT/$2.png" >/dev/null
echo " $2.png"
}
# The two played scenes. The search for a good flop takes the app a moment longer to open.
scene() { # scene <table|review> <filename>
xcrun simctl terminate "$UDID" "$BID" >/dev/null 2>&1 || true
sleep 1
SIMCTL_CHILD_POKERTUTOR_DEMO="$1" xcrun simctl launch "$UDID" "$BID" >/dev/null
sleep 12
xcrun simctl io "$UDID" screenshot "$OUT/$2.png" >/dev/null
echo " $2.png"
}
# One launch that nobody photographs. The first launch after another app was on screen carries
# that app's name in the status bar as a way back ("◀ Poker Tutor Cash"), which is not something
# a store shot should show; relaunching the same app clears it.
xcrun simctl terminate "$UDID" "$BID" >/dev/null 2>&1 || true
SIMCTL_CHILD_POKERTUTOR_SCREEN=Home xcrun simctl launch "$UDID" "$BID" >/dev/null
sleep 3
echo "writing $PRODUCT shots to $OUT"
scene table 0-table
shoot Home 1-home
shoot Drills 2-drills
shoot "My game" 3-my-game
shoot Hands 4-hands
shoot Ladder 5-ladder
shoot Settings 6-more
scene review 7-review
echo "done."