A real-time UNO card game built with Phoenix LiveView. Play against 3 AI opponents with smooth card animations, drag-and-drop hand management, and standard UNO rules.
- 1 human player vs 3 AI opponents (CPU West, CPU North, CPU East)
- Full UNO rules: Skip, Reverse, Draw Two, Wild, Wild Draw Four
- CSS-only card rendering (no images)
- Drag-and-drop card reordering with FLIP animations
- Card draw/play animations between actual positions with 3D card flip reveals
- Dynamic direction arrow showing current turn flow
- Target player indicator
- UNO button with penalty system
- Color chooser for wild cards
- In-game rules modal (rendered from Markdown)
- Player name persistence via localStorage
- Deployable to fly.io
lib/uno_royale/engine/ -- Pure Elixir game engine (no Phoenix deps)
card.ex -- Card struct & helpers
deck.ex -- 108-card deck creation, shuffle, draw
player.ex -- Player struct
game_state.ex -- Game state with turn management
rules.ex -- Play validation
game.ex -- Core state machine
ai.ex -- AI card selection strategy
lib/uno_royale/
game_server.ex -- GenServer wrapping engine + AI scheduling
game_bridge.ex -- Popcorn WASM bridge (same engine, offline iOS)
wasm_application.ex -- Minimal supervisor for WASM target
application.ex -- Supervisor tree
lib/uno_royale_web/
live/game_live.ex -- LiveView (lobby + game board)
components/
game_components.ex -- Card, hand, pile function components
assets/
js/app.js -- Phoenix LiveView client hooks + animations
css/app.css -- Phoenix web styling
web/ -- Shared standalone/iOS web UI source
index.html -- Shared entry point
js/ -- Shared renderer, interactions, animations
css/game.css -- Shared game styles
wasm/
dist/ -- Generated Popcorn runtime + AVM bundle
ios/ -- iOS app (SwiftUI + WKWebView)
project.yml -- XcodeGen spec
UnoRoyale/ -- Swift sources + bundled web resources
web/ -- iOS-specific web bridge/runtime shims
scripts/
build_ios.sh -- Full build pipeline (WASM → iOS resources)
The game engine is fully decoupled from Phoenix -- pure functions that take state in and return state out. The GenServer manages AI turn scheduling via Process.send_after and broadcasts state updates via PubSub.
ios/UnoRoyale/Resources/www is a generated staging bundle. Shared UI source lives in web/, and iOS-only web shims live in ios/web/.
- Elixir 1.17.3 / Erlang OTP 26.0.2 (managed via mise)
- Xcode 15+ with iOS SDK (for iOS build only)
- xcodegen (
brew install xcodegen, for iOS build only)
mix setup
mix phx.serverVisit localhost:4000.
mix test test/uno_royale/engine/A pre-commit hook runs engine tests automatically and requires test changes when engine files are modified.
The game engine compiles to WebAssembly via Popcorn (AtomVM → WASM) and runs fully offline in a WKWebView-based iOS app. Single-player vs 3 AI opponents, no network required.
# Build WASM bundle + assemble iOS resources + generate Xcode project
./scripts/build_ios.sh# 1. Build for simulator
xcodebuild -project ios/UnoRoyale.xcodeproj \
-scheme UnoRoyale \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro' \
build
# 2. Boot a simulator
xcrun simctl boot "iPhone 17 Pro"
open -a Simulator
# 3. Install and launch
xcrun simctl install booted \
~/Library/Developer/Xcode/DerivedData/UnoRoyale-*/Build/Products/Debug-iphonesimulator/UnoRoyale.app
xcrun simctl launch booted com.unoroyale.appOr just open ios/UnoRoyale.xcodeproj in Xcode and hit Run.
Open ios/UnoRoyale.xcodeproj in Xcode, select your device, and run. You may need to set a development team in Signing & Capabilities.
Configured for fly.io:
fly launch
fly secrets set SECRET_KEY_BASE=$(mix phx.gen.secret)
fly deploySee RULES.md for the full game rules.