Skip to content

tgwizard/adam-says

Repository files navigation

Adam Says

A "Simon says" color memory game for Google Pixel Watch (Wear OS).

The watch flashes a sequence of colored quadrants; tap them back in order. Each round adds one more. Wrong tap ends the game.

Built with Kotlin + Jetpack Compose for Wear OS.

Project status

Working end-to-end. Installed and playable on both the Wear OS emulator and a real Pixel Watch. No remote configured — the repo is local only.

  • minSdk 30 (Wear OS 3+; covers Pixel Watch 1 & 2)
  • compileSdk 36 (extension 1)
  • Kotlin 2.2.x, AGP 9.x, Gradle 9.3.1

Prerequisites

  • Android Studio (Koala or newer) installed at /Applications/Android Studio.app
  • Wear OS system image (API 34+) installed via SDK Manager
  • A Wear OS AVD named something like "Wear_OS_XL_Round" with a round 454×454 profile
  • No standalone JDK needed — the bundled JDK at /Applications/Android Studio.app/Contents/jbr/Contents/Home is used

Running in Android Studio

Open the project in Android Studio, pick the Wear AVD in the target dropdown, press ▶. That's it.

Running from the terminal

The gradle wrapper requires JAVA_HOME set to Android Studio's bundled JDK:

export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
export PATH="$JAVA_HOME/bin:$PATH:$HOME/Library/Android/sdk/platform-tools"

Build:

./gradlew :app:assembleDebug

Install on emulator (must be running):

./gradlew :app:installDebug
adb shell am start -n com.tgwizard.adamsays/.presentation.MainActivity

Run the unit tests:

./gradlew :app:testDebugUnitTest

Running on a real Pixel Watch

The Pixel Watch has no USB port — connect via ADB over Wi-Fi.

One-time setup

  1. On the watch: Settings → System → About → Versions, tap Build number seven times to enable developer options.
  2. Settings → Developer options → ADB debugging: ON.
  3. Settings → Developer options → Wireless debugging: ON.
  4. Make sure the watch is on the same Wi-Fi network as the Mac.

Every time

Wear OS rotates the wireless debugging port after reboots / disconnects.

  1. Open Settings → Developer options → Wireless debugging on the watch.

  2. Note the IP:port at the top of that screen — that's the ADB port.

  3. If ADB has never paired with this watch before (or you revoked trust), tap Pair new device — it shows a pairing IP:port and a 6-digit code. Run:

    adb pair <pairing-ip>:<pairing-port>
    # enter the 6-digit code when prompted
  4. Connect (using the ADB port from step 2, not the pairing port):

    adb connect <ip>:<adb-port>
  5. Install and launch:

    adb -s <ip>:<adb-port> install -r app/build/outputs/apk/debug/app-debug.apk
    adb -s <ip>:<adb-port> shell am start -n com.tgwizard.adamsays/.presentation.MainActivity

If adb connect returns Connection refused, the port rotated — re-check it on the watch.

Code layout

app/src/main/java/com/tgwizard/adamsays/
  presentation/MainActivity.kt     # hosts GameScreen
  game/
    Quadrant.kt                    # enum + tap-angle → quadrant pure function
    GameState.kt                   # GameState, GamePhase sealed interface, Timing
    GameViewModel.kt               # state machine; StateFlow<GameState>
  ui/
    GameColors.kt                  # dim/lit neon palette per quadrant
    WatchFaceCanvas.kt             # draws the 4 quadrants + hub + ball of fire
    GameScreen.kt                  # top-level composable; routes taps, drives animations
    StartScreen.kt                 # "ADAM SAYS / tap to start" screen
    GameOverScreen.kt              # "SCORE / N / tap to play again" screen

app/src/test/java/com/tgwizard/adamsays/
  MainDispatcherRule.kt            # JUnit rule installing TestDispatcher as Main
  game/QuadrantTest.kt             # 8 tests
  game/GameViewModelTest.kt        # 16 tests — full state machine coverage

State machine (in GameViewModel)

NotStarted ──onStartTap──► Idle ──delay(PRE_START_MS)──► Showing ──loop flashes──► AwaitingInput
                                                                                   │
                                                       ┌── wrong ──────────────────┤
                                                       │                           │
                                                       │                           ├── correct & final ──► RoundComplete
                                                       │                           │                           │
                                                       │                           │                      delay(ROUND_PAUSE_MS)
                                                       │                           │                           ▼
                                                       │                           └── correct & mid ──► (advance inputIndex, still AwaitingInput)
                                                       ▼
                                                   GameOver(score, restartable=false)
                                                       │
                                              delay(GAME_OVER_LOCK_MS)
                                                       ▼
                                                   GameOver(..., restartable=true)
                                                       │
                                           ┌───────────┴─────────────┐
                                           ▼                         ▼
                                       tap to restart      delay(auto_restart_MS)
                                           └──────► startNewGame ──┘

Testing notes

  • Unit tests drive the ViewModel's StateFlow with a StandardTestDispatcher
    • virtual time via advanceTimeBy (see MainDispatcherRule).
  • Tests bias time offsets by ±1ms around Timing constants to avoid fragility at exact virtual-time boundaries in kotlinx-coroutines-test.
  • No instrumented / Compose UI tests (kept minimal for MVP). Visuals are verified manually on the emulator.

Design docs

  • Spec: docs/superpowers/specs/2026-04-17-wear-os-simon-game-design.md
  • Implementation plan: docs/superpowers/plans/2026-04-17-wear-os-simon-game.md (tracks the original TDD walkthrough — useful for understanding the code's evolution; may lag behind current state.)

License

MIT — see LICENSE.

Ideas not yet implemented

  • Persist high score across launches (DataStore)
  • Difficulty ramp (flashes speed up as the sequence grows)
  • Sound effects (game is haptic-only now)
  • Settings screen
  • Watch tile / complication for one-tap launch

About

Game for Pixel Watch / Wear OS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages