A React Native card game score tracker where the lowest score wins. Players accumulate points each round — the first to reach the score limit is eliminated, and the player with the fewest points wins.
- Set 2–8 players with custom names and a score limit
- Track scores round by round with color-coded progress bars
- Edit any player's total score inline at any time
- Automatic game-over detection when any player hits the limit
- Final rankings with winner / eliminated callouts
- Play again with the same players or start a new game from scratch
The app is a three-screen React Native application built on Expo with Expo Router (file-based routing). There is no backend — all state is persisted locally on the device via AsyncStorage.
flowchart TD
User --> Setup["Setup (index.jsx)"]
Setup -->|"router.replace /game"| Game["Game (game.jsx)"]
Game -->|"router.replace /results"| Results["Results (results.jsx)"]
Results -->|"play again"| Game
Results -->|"new game"| Setup
Game -->|"reset"| Setup
Setup -->|"write gameState"| AS[("AsyncStorage")]
Game -->|"read / write gameState"| AS
Results -->|"read / write gameState"| AS
flowchart TD
Layout["_layout.jsx (Stack + StatusBar)"]
Layout --> Setup["SetupScreen"]
Layout --> Game["GameScreen"]
Layout --> Results["ResultsScreen"]
Game --> Modal["Edit Score Modal"]
Setup --> AS[("AsyncStorage")]
Game --> AS
Results --> AS
sequenceDiagram
User->>Setup: Configure players and score limit
Setup->>AsyncStorage: Write initial gameState
Setup->>Game: router.replace("/game")
Game->>AsyncStorage: Read gameState on focus
User->>Game: Enter round scores and submit
Game->>Game: Accumulate scores
alt Any player score >= limitScore
Game->>AsyncStorage: Write gameState (finished=true)
Game->>Results: router.replace("/results")
Results->>AsyncStorage: Read gameState on focus
else Limit not reached
Game->>AsyncStorage: Write updated gameState
Game->>Game: Increment round, clear inputs
end
flowchart TD
Enter["User enters round scores"] --> Check{"At least one\nnon-empty input?"}
Check -->|"No"| Alert["Show alert — no scores entered"]
Check -->|"Yes"| Add["Add round values to player totals\n(empty input = 0)"]
Add --> Limit{"Any player\n>= limitScore?"}
Limit -->|"Yes"| Finish["Save finished=true → navigate to Results"]
Limit -->|"No"| Save["Save updated game → clear inputs\nIncrement round counter"]
There is no API — state is a single JSON object persisted under the AsyncStorage key "gameState":
{
players: [{ id: number, name: string, score: number }],
limitScore: number, // first player to reach this is eliminated
round: number, // current round (starts at 1, incremented after each submit)
finished: boolean // true once a player hits the limit
}gameState is written by Setup, read and mutated by Game, and read and optionally reset by Results. Deleting the key clears the game and returns to Setup.
npm start # Start Expo dev server (Expo Go via QR code)
npm run android # Launch on Android emulator / connected device
npm run ios # Launch on iOS simulator
npm run build:android # Cloud APK build via EASNo test runner is configured.
| File | Purpose |
|---|---|
app.json |
App name, icon/splash assets, Android package com.scoretracker.app, Expo owner ybt24, EAS project ID |
eas.json |
Two build profiles: preview and production, both output .apk (not .aab) |
babel.config.js |
Babel with babel-preset-expo |
Prerequisites: Node.js, an Expo account (for cloud builds).
npm install
npx expo start # scan QR code with Expo Go on Android/iOSFor a real APK build:
npm install -g eas-cli
eas login
eas build --platform android --profile previewThe cloud build takes 5–10 minutes and produces a downloadable .apk.
To install the APK on Android: enable Install unknown apps in device settings, then open the downloaded file.
No automated tests exist. Manual testing via Expo Go covers the primary flow. Behavior in Expo Go may differ slightly from a production APK build.
- No game history — only the current game is stored; previous games are not retained.
- No negative scores — the edit modal rejects values below 0.
- Single device — no multiplayer sync or remote state; all players share one phone.
- No undo — submitting a round immediately persists; only the edit modal can correct a wrong total.
- Persistent game history / session log
- Undo last round
- Custom player avatars or colors
- Score export (share as text/image)
- iOS
.ipaproduction build