6.4 KiB
Arcade85
A 5-in-1 iOS collection of faithful HD remakes of five classic TI-85 calculator
games. Native SwiftUI + SpriteKit, one shared shell, each game a
Scene (SpriteKit) + Sim (pure Swift engine) pair.
This file is the operational runbook for an agent building/deploying Arcade85 on the
Mac Studio without the human in the loop. Read it top to bottom before touching
signing, git, or the build. It follows the same pattern as ~/dev/KyleCode/AGENTS.md
(same Gitea, same ~/dev/.env token, same signing identity).
1. Repo layout
Arcade85/
Arcade85.xcodeproj/ # hand-maintained (NOT xcodegen) — edit via Xcode/pbxproj
Arcade85/
Arcade85App.swift # @main
Shell/ # app chrome: RootView, GameHostView, GameCatalog,
# Theme, Haptics, HighScoreStore
Games/
SceneArt.swift # shared procedural texture/sprite helpers
DopeWars/ DopeWarsScene.swift + DopeWarsSim.swift
Lander/ LanderScene.swift + LanderSim.swift
Phoenix/ PhoenixScene.swift + PhoenixSim.swift + PhoenixData.swift
ZTetris/ TetrisScene.swift + TetrisSim.swift
ZMercury/ MercuryScene.swift + MercurySim.swift + MercuryData.swift
Galaxian/ (ported, dormant — see GameCatalog note)
ComingSoonScene.swift
README.md
Convention: every game is a *Scene.swift (SpriteKit, all UI/input) paired with a
*Sim.swift (pure Swift state machine — no UIKit/SpriteKit imports). The Sim is the
engine and is where mechanics live; the Scene only renders and routes touches. Keep
them separate. Dope Wars' DopeWarsSim is the reference for a well-factored Sim
(seeded RNG, a separate extRng stream for non-classic extensions, #if DEBUG debug
hooks, a messages: [String] queue the Scene drains one-per-tap).
The original TI-85 sources this was remade from live in sibling folders under
~/dev/TI/ (lander/, zetris/, phx4.0ti85/, zmercury-ti85/, galaxian-ti85/).
They are not part of this repo.
2. Signing
- Team:
TG8X9PP8VT(Kyle Albanese). Identity: "Apple Development: Kyle Albanese (5SRT78F8BG)". Bundle ID:com.albanese.arcade85. - Automatic signing (
CODE_SIGN_STYLE = Automatic),GENERATE_INFOPLIST_FILE = YES(no Info.plist file exists — don't add one). - The current pbxproj does not pin
DEVELOPMENT_TEAM; the identity in the login keychain is used. If a device build ever fails to sign, setDEVELOPMENT_TEAM = TG8X9PP8VTon the target (don't switch to manual profiles).
Environment prerequisites (why this works)
Commands run as the user's own launchd job in gui/501 (Aqua session), so the
login keychain is readable by xcodebuild/codesign. Verify before a device build
(do NOT change anything if it fails — stop and tell the user):
launchctl managername # must print: Aqua
security find-identity -v -p codesigning /Users/fung/Library/Keychains/login.keychain-db
# must list: "Apple Development: Kyle Albanese (5SRT78F8BG)"
Hard rules: never revoke/recreate the Apple Development cert, don't touch keychain
settings, keep automatic signing, never commit *.p8 or *.env secrets.
3. Build
Run from /Users/fung/dev/TI/Arcade85.
Simulator (no signing — default for quick checks)
xcodebuild -project Arcade85.xcodeproj -scheme Arcade85 \
-destination 'generic/platform=iOS Simulator' -configuration Debug \
-derivedDataPath build/dd build CODE_SIGNING_ALLOWED=NO
Success: ** BUILD SUCCEEDED **. (Verified 2026-09-16.)
Device (real iPhone, auto-signed)
xcodebuild -project Arcade85.xcodeproj -scheme Arcade85 \
-destination 'generic/platform=iOS' -derivedDataPath build/dd \
-allowProvisioningUpdates build
Install/launch a paired device (list with xcrun devicectl list devices):
APP=build/dd/Build/Products/Debug-iphoneos/Arcade85.app
xcrun devicectl device install app --device <UDID> "$APP"
xcrun devicectl device process launch --device <UDID> com.albanese.arcade85
There are no automated tests in this project. A passing build is the check. For gameplay, run in the simulator and play, or use the Dope Wars debug env vars below.
Dope Wars debug env vars (DEBUG builds only)
DopeWarsScene.didMove(to:) reads ProcessInfo env vars for screenshot/demo poses:
ARCADE_AUTOPLAY=1 (auto-start a run), ARCADE_DEMO=1 (deterministic day-12 demo),
ARCADE_DEMO_VIEW=buy|chase|tier3|over|ride|keypad|sharkchase|endless. The
DopeWarsSim also exposes debug* mutators (wallet, debt, price, tier, held, forced
RNG rolls) gated behind #if DEBUG.
4. Git & Gitea
Remote: Gitea https://git.kwalb.com/kyle/Arcade85 (branch main). No git
credentials in the keychain — use the API token from ~/dev/.env inline, and let the
credential prompt fail to store (harmless). The repo is created on first push:
TOKEN=$(sed -n 's/^GITEA_API=//p' ~/dev/.env)
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"Arcade85","private":false,"default_branch":"main"}' \
https://git.kwalb.com/api/v1/user/repos
# then:
git push "https://kyle:$TOKEN@git.kwalb.com/kyle/Arcade85.git" main
API: curl -H "Authorization: token $TOKEN" https://git.kwalb.com/api/v1/repos/kyle/Arcade85/…
Only commit/push when asked. Stage only intended files. Never stage *.p8 or
*.env.
.env — do not source it
~/dev/.env holds GITEA_URL, GITEA_API (token), GITEA_USERNAME, GITEA_EMAIL
plus other service keys. Do not source it — a later line is a bare
https://claude.ai/magic-link… URL (not key=value) which breaks source in zsh.
Read values with sed:
TOKEN=$(sed -n 's/^GITEA_API=//p' ~/dev/.env)
5. .gitignore (already in place — don't remove)
build/, DerivedData/, *.xcuserstate, xcuserdata/, *.moved-aside, *.hmap,
*.ipa, *.dSYM.zip, *.dSYM, .DS_Store, .build/, .swiftpm/.
Add *.p8 and *.env to this ignore list if/when they could appear here (none do
today, but the rule is: never commit signing keys or secret files).
6. Adding a game
- Create
Games/<Name>/<Name>Scene.swift+<Name>Sim.swift(mirror Dope Wars). - Add both files to the Xcode target in
Arcade85.xcodeproj/project.pbxproj(hand-maintained — there is noproject.yml/xcodegen step; edit the pbxproj or use Xcode's "Add Files to Arcade85"). - Register the slot in
Shell/GameCatalog.swift. - Build (simulator) to verify.