Signed-off-by: Matt Bruce <matt.bruce1@toyota.com>

This commit is contained in:
Matt Bruce 2025-12-28 21:43:53 -06:00
parent 9cc7352c60
commit 55fd30d77e
4 changed files with 1598 additions and 15 deletions

View File

@ -24,28 +24,52 @@ struct BaccaratTests {
// Test with a King (value 0) and an 8 (value 8)
hand.addCard(Card(suit: .spades, rank: .king))
hand.addCard(Card(suit: .hearts, rank: .eight))
#expect(hand.value == 8)
#expect(hand.value == 8, "King (0) + Eight (8) should equal 8")
// Test with two cards that sum to more than 10
hand.clear()
hand.addCard(Card(suit: .spades, rank: .seven))
hand.addCard(Card(suit: .hearts, rank: .five))
#expect(hand.value == 2) // 7 + 5 = 12, 12 % 10 = 2
#expect(hand.value == 2, "Seven (7) + Five (5) = 12, 12 % 10 should equal 2")
// Test natural 9
hand.clear()
hand.addCard(Card(suit: .clubs, rank: .four))
hand.addCard(Card(suit: .diamonds, rank: .five))
#expect(hand.value == 9)
#expect(hand.isNatural == true)
#expect(hand.value == 9, "Four (4) + Five (5) should equal 9")
#expect(hand.isNatural == true, "Hand with 2 cards and value 9 is a natural")
// Test with three cards
hand.clear()
hand.addCard(Card(suit: .spades, rank: .four))
hand.addCard(Card(suit: .hearts, rank: .three))
hand.addCard(Card(suit: .clubs, rank: .two))
#expect(hand.value == 9) // 4 + 3 + 2 = 9
#expect(hand.isNatural == false) // Not a natural (3 cards)
#expect(hand.value == 9, "Four (4) + Three (3) + Two (2) should equal 9")
#expect(hand.isNatural == false, "Hand with 3 cards is not a natural")
// Test face cards (all should be 0)
hand.clear()
hand.addCard(Card(suit: .spades, rank: .king))
hand.addCard(Card(suit: .hearts, rank: .queen))
#expect(hand.value == 0, "King (0) + Queen (0) should equal 0")
// Test 10 card (should be 0 in baccarat)
hand.clear()
hand.addCard(Card(suit: .diamonds, rank: .ten))
hand.addCard(Card(suit: .clubs, rank: .nine))
#expect(hand.value == 9, "Ten (0) + Nine (9) should equal 9")
// Test Ace (should be 1)
hand.clear()
hand.addCard(Card(suit: .hearts, rank: .ace))
hand.addCard(Card(suit: .spades, rank: .ace))
#expect(hand.value == 2, "Ace (1) + Ace (1) should equal 2")
// Test modulo 10 with higher values
hand.clear()
hand.addCard(Card(suit: .diamonds, rank: .nine))
hand.addCard(Card(suit: .clubs, rank: .nine))
#expect(hand.value == 8, "Nine (9) + Nine (9) = 18, 18 % 10 should equal 8")
}
@Test func engineHandValues() async throws {
@ -65,12 +89,12 @@ struct BaccaratTests {
#expect(engine.bankerHand.value <= 9)
}
@Test func gameStateHandValues() async throws {
@Test @MainActor func gameStateHandValues() async throws {
let state = GameState()
// Initially should be 0
#expect(state.playerHandValue == 0)
#expect(state.bankerHandValue == 0)
#expect(state.playerHandValue == 0, "Player hand value should be 0 when no cards dealt")
#expect(state.bankerHandValue == 0, "Banker hand value should be 0 when no cards dealt")
// Place a bet
state.placeBet(type: .player, amount: 100)
@ -79,14 +103,26 @@ struct BaccaratTests {
await state.deal()
// After dealing, both hands should have values between 0-9
#expect(state.playerHandValue >= 0)
#expect(state.playerHandValue <= 9)
#expect(state.bankerHandValue >= 0)
#expect(state.bankerHandValue <= 9)
#expect(state.playerHandValue >= 0, "Player hand value should be >= 0")
#expect(state.playerHandValue <= 9, "Player hand value should be <= 9")
#expect(state.bankerHandValue >= 0, "Banker hand value should be >= 0")
#expect(state.bankerHandValue <= 9, "Banker hand value should be <= 9")
// Verify visible cards match engine cards
#expect(state.visiblePlayerCards.count >= 2)
#expect(state.visibleBankerCards.count >= 2)
#expect(state.visiblePlayerCards.count >= 2, "Player should have at least 2 cards")
#expect(state.visibleBankerCards.count >= 2, "Banker should have at least 2 cards")
// Verify cards are face up (needed for badges to show)
#expect(state.playerCardsFaceUp.count >= 2, "Player should have face-up state for cards")
#expect(state.bankerCardsFaceUp.count >= 2, "Banker should have face-up state for cards")
#expect(state.playerCardsFaceUp.contains(true), "At least one player card should be face up")
#expect(state.bankerCardsFaceUp.contains(true), "At least one banker card should be face up")
// Verify the visible hand values match what the engine calculates
let manualPlayerValue = state.visiblePlayerCards.reduce(0) { $0 + $1.baccaratValue } % 10
let manualBankerValue = state.visibleBankerCards.reduce(0) { $0 + $1.baccaratValue } % 10
#expect(state.playerHandValue == manualPlayerValue, "Visible cards should match calculated value for player")
#expect(state.bankerHandValue == manualBankerValue, "Visible cards should match calculated value for banker")
}
}

View File

@ -0,0 +1,629 @@
# Game Center Integration - Baccarat
This document outlines the Game Center achievements for Baccarat and the game-specific integration steps.
## Prerequisites
✅ Complete `CasinoKit/GAME_CENTER_PLAN.md` implementation first (shared infrastructure)
## Achievement Design Philosophy
Focus on **gameplay milestones**, **pattern recognition**, and **understanding Baccarat strategy**, not pure luck or bankroll metrics.
Achievements celebrate:
- 🎲 Understanding Baccarat rules and betting
- 📊 Pattern recognition (road maps)
- 💎 High-stakes play (earned through gameplay)
- 🎯 Mastery of side bets
## Baccarat Achievements
### 🎓 Learning & First Steps Category
#### 1. **First Hand**
- **ID:** `com.yourdomain.baccarat.first_hand`
- **Description:** Play your first hand of Baccarat
- **Progress:** Complete on first hand
- **Points:** 5
- **Icon:** `play.circle.fill`
- **Trigger:** After first `finishRound()` completes
#### 2. **Player Believer**
- **ID:** `com.yourdomain.baccarat.player_wins_10`
- **Description:** Win 10 times betting on Player
- **Progress:** Incremental (0-10)
- **Points:** 10
- **Icon:** `person.fill`
- **Trigger:** When Player bet wins
#### 3. **Banker's Trust**
- **ID:** `com.yourdomain.baccarat.banker_wins_25`
- **Description:** Win 25 times betting on Banker
- **Progress:** Incremental (0-25)
- **Points:** 15
- **Icon:** `building.columns.fill`
- **Trigger:** When Banker bet wins
#### 4. **Tie Hunter**
- **ID:** `com.yourdomain.baccarat.tie_wins_5`
- **Description:** Win 5 times betting on Tie
- **Progress:** Incremental (0-5)
- **Points:** 15
- **Icon:** "equal.circle.fill"
- **Trigger:** When Tie bet wins
- **Note:** Higher points due to rarity (14.4% house edge)
### 🎲 Natural & Special Hands Category
#### 5. **Natural Talent**
- **ID:** `com.yourdomain.baccarat.naturals_10`
- **Description:** Get 10 naturals (8 or 9 on initial two cards)
- **Progress:** Incremental (0-10)
- **Points:** 10
- **Icon:** `star.fill`
- **Trigger:** When either Player or Banker gets natural 8 or 9
#### 6. **Natural Legend**
- **ID:** `com.yourdomain.baccarat.naturals_50`
- **Description:** Get 50 naturals
- **Progress:** Incremental (0-50)
- **Points:** 25
- **Icon:** `star.circle.fill`
- **Trigger:** When either Player or Banker gets natural (extended)
#### 7. **Perfect Pair**
- **ID:** `com.yourdomain.baccarat.pair_bets_10`
- **Description:** Win Player Pair or Banker Pair side bet 10 times
- **Progress:** Incremental (0-10)
- **Points:** 15
- **Icon:** `suit.heart.fill`
- **Trigger:** When either pair bet wins
#### 8. **Dragon Master**
- **ID:** `com.yourdomain.baccarat.dragon_bonus_10`
- **Description:** Win Dragon Bonus side bet 10 times
- **Progress:** Incremental (0-10)
- **Points:** 15
- **Icon:** `flame.fill`
- **Trigger:** When Dragon Bonus bet wins
#### 9. **Dragon Legend**
- **ID:** `com.yourdomain.baccarat.dragon_bonus_win_by_9`
- **Description:** Win Dragon Bonus with a 9-point margin (30:1 payout)
- **Progress:** Complete when achieved
- **Points:** 25
- **Icon:** `flame.circle.fill`
- **Trigger:** When Dragon Bonus pays 30:1
- **Note:** Very rare achievement
### 💪 Persistence & Mastery Category
#### 10. **Beginner**
- **ID:** `com.yourdomain.baccarat.rounds_50`
- **Description:** Play 50 total rounds
- **Progress:** Incremental (0-50)
- **Points:** 10
- **Icon:** `person.fill`
- **Trigger:** After each round completes
#### 11. **Enthusiast**
- **ID:** `com.yourdomain.baccarat.rounds_250`
- **Description:** Play 250 total rounds
- **Progress:** Incremental (0-250)
- **Points:** 25
- **Icon:** `person.2.fill`
- **Trigger:** After each round completes
#### 12. **Expert**
- **ID:** `com.yourdomain.baccarat.rounds_500`
- **Description:** Play 500 total rounds
- **Progress:** Incremental (0-500)
- **Points:** 50
- **Icon:** `person.3.fill`
- **Trigger:** After each round completes
#### 13. **Comeback Kid**
- **ID:** `com.yourdomain.baccarat.comeback`
- **Description:** Recover from under $100 to $10,000 in a single session
- **Progress:** Complete when achieved
- **Points:** 25
- **Icon:** `arrow.up.right.circle.fill`
- **Trigger:** When balance reaches $10k after being below $100 in same session
- **Implementation Note:** Track session low point
### 📊 Pattern Recognition & Strategy Category
#### 14. **Streak Spotter**
- **ID:** `com.yourdomain.baccarat.spot_streaks_5`
- **Description:** Correctly predict and bet on a streak 5 times (3+ same outcome in a row)
- **Progress:** Incremental (0-5)
- **Points:** 20
- **Icon:** `chart.line.uptrend.xyaxis`
- **Trigger:** When player bets same side 3+ times in a row and all win
- **Implementation Note:** Track consecutive wins on same bet type
#### 15. **Road Map Reader**
- **ID:** `com.yourdomain.baccarat.view_road_map_25`
- **Description:** View the road map history 25 times
- **Progress:** Incremental (0-25)
- **Points:** 10
- **Icon:** `map.fill`
- **Trigger:** Track when road map view is expanded/scrolled
- **Implementation Note:** Encourages engagement with pattern tracking feature
### 💎 High Stakes Category
#### 16. **High Roller**
- **ID:** `com.yourdomain.baccarat.bet_50000`
- **Description:** Place a single bet of $50,000 or more
- **Progress:** Complete when achieved
- **Points:** 20
- **Icon:** `dollarsign.circle.fill`
- **Trigger:** When any bet equals or exceeds $50,000
- **Implementation Note:** Must be earned through gameplay (requires VIP table limits)
#### 17. **Big Win**
- **ID:** `com.yourdomain.baccarat.single_win_100000`
- **Description:** Win $100,000 or more in a single hand
- **Progress:** Complete when achieved
- **Points:** 30
- **Icon:** `banknote.fill`
- **Trigger:** When net profit from a single round ≥ $100,000
- **Implementation Note:** Possible with Dragon Bonus on large bet
### 🏆 Elite Category
#### 18. **Side Bet Specialist**
- **ID:** `com.yourdomain.baccarat.side_bets_total_25`
- **Description:** Win any side bet 25 times total
- **Progress:** Incremental (0-25)
- **Points:** 25
- **Icon:** `plus.circle.fill`
- **Trigger:** Any time a side bet wins (Pairs or Dragon Bonus)
#### 19. **James Bond** (Hidden Achievement)
- **ID:** `com.yourdomain.baccarat.james_bond`
- **Description:** Play 100 consecutive hands betting only on Banker
- **Progress:** Incremental (0-100, resets if different bet placed)
- **Points:** 25
- **Icon:** `tuxedo.fill` (or `theatermasks.fill`)
- **Trigger:** Track consecutive Banker-only bets
- **Implementation Note:** Resets to 0 if player bets on Player or Tie
- **Fun fact:** James Bond's favorite game and typical bet
#### 20. **Table Explorer** (Hidden Achievement)
- **ID:** `com.yourdomain.baccarat.all_table_limits`
- **Description:** Play at least 10 rounds at each table limit level
- **Progress:** Incremental (0-50, 10 per table × 5 tables)
- **Points:** 15
- **Icon:** `chart.bar.fill`
- **Trigger:** Track rounds played at each table limit (Casual, Low, Medium, High, VIP)
## Implementation Steps
### 1. Define Achievement Enum
**File:** `Baccarat/Baccarat/Models/BaccaratAchievement.swift` (new file)
```swift
import CasinoKit
import GameKit
enum BaccaratAchievement: String, AchievementDefinition {
case firstHand = "first_hand"
case playerWins10 = "player_wins_10"
case bankerWins25 = "banker_wins_25"
case tieWins5 = "tie_wins_5"
case naturals10 = "naturals_10"
case naturals50 = "naturals_50"
case pairBets10 = "pair_bets_10"
case dragonBonus10 = "dragon_bonus_10"
case dragonBonusWinBy9 = "dragon_bonus_win_by_9"
case rounds50 = "rounds_50"
case rounds250 = "rounds_250"
case rounds500 = "rounds_500"
case comeback = "comeback"
case spotStreaks5 = "spot_streaks_5"
case viewRoadMap25 = "view_road_map_25"
case bet50000 = "bet_50000"
case singleWin100000 = "single_win_100000"
case sideBetsTotal25 = "side_bets_total_25"
case jamesBond = "james_bond"
case allTableLimits = "all_table_limits"
var identifier: String {
"com.yourdomain.baccarat.\(rawValue)"
}
var title: String {
switch self {
case .firstHand: return String(localized: "First Hand")
case .playerWins10: return String(localized: "Player Believer")
case .bankerWins25: return String(localized: "Banker's Trust")
case .tieWins5: return String(localized: "Tie Hunter")
case .naturals10: return String(localized: "Natural Talent")
case .naturals50: return String(localized: "Natural Legend")
case .pairBets10: return String(localized: "Perfect Pair")
case .dragonBonus10: return String(localized: "Dragon Master")
case .dragonBonusWinBy9: return String(localized: "Dragon Legend")
case .rounds50: return String(localized: "Beginner")
case .rounds250: return String(localized: "Enthusiast")
case .rounds500: return String(localized: "Expert")
case .comeback: return String(localized: "Comeback Kid")
case .spotStreaks5: return String(localized: "Streak Spotter")
case .viewRoadMap25: return String(localized: "Road Map Reader")
case .bet50000: return String(localized: "High Roller")
case .singleWin100000: return String(localized: "Big Win")
case .sideBetsTotal25: return String(localized: "Side Bet Specialist")
case .jamesBond: return String(localized: "James Bond")
case .allTableLimits: return String(localized: "Table Explorer")
}
}
var description: String {
// Full descriptions for each achievement
}
var maxProgress: Int {
switch self {
case .firstHand, .comeback, .dragonBonusWinBy9, .bet50000, .singleWin100000: return 1
case .tieWins5, .spotStreaks5: return 5
case .playerWins10, .naturals10, .pairBets10, .dragonBonus10: return 10
case .bankerWins25, .sideBetsTotal25, .viewRoadMap25: return 25
case .naturals50, .rounds50, .allTableLimits: return 50
case .jamesBond: return 100
case .rounds250: return 250
case .rounds500: return 500
}
}
var isIncremental: Bool {
switch self {
case .firstHand, .comeback, .dragonBonusWinBy9, .bet50000, .singleWin100000:
return false
default:
return true
}
}
var iconName: String {
switch self {
case .firstHand: return "play.circle.fill"
case .playerWins10: return "person.fill"
case .bankerWins25: return "building.columns.fill"
case .tieWins5: return "equal.circle.fill"
case .naturals10: return "star.fill"
case .naturals50: return "star.circle.fill"
case .pairBets10: return "suit.heart.fill"
case .dragonBonus10: return "flame.fill"
case .dragonBonusWinBy9: return "flame.circle.fill"
case .rounds50: return "person.fill"
case .rounds250: return "person.2.fill"
case .rounds500: return "person.3.fill"
case .comeback: return "arrow.up.right.circle.fill"
case .spotStreaks5: return "chart.line.uptrend.xyaxis"
case .viewRoadMap25: return "map.fill"
case .bet50000: return "dollarsign.circle.fill"
case .singleWin100000: return "banknote.fill"
case .sideBetsTotal25: return "plus.circle.fill"
case .jamesBond: return "theatermasks.fill"
case .allTableLimits: return "chart.bar.fill"
}
}
}
```
### 2. Add Achievement Tracker to GameState
**File:** `Baccarat/Baccarat/Engine/GameState.swift`
```swift
import CasinoKit
@MainActor
@Observable
class GameState {
// ... existing properties ...
// NEW: Achievement tracking
private(set) var achievementTracker: AchievementTracker<BaccaratAchievement>
// Track session-specific metrics
private var sessionLowBalance: Int = 0
private var consecutiveBankerBets = 0
private var consecutiveSameBetType: BetType? = nil
private var consecutiveSameBetWins = 0
private var roundsPerTableLimit: [String: Int] = [:]
init() {
// ... existing init ...
self.achievementTracker = AchievementTracker<BaccaratAchievement>()
self.sessionLowBalance = balance
}
// NEW: Achievement checking methods
func checkAchievements(after action: GameAction) {
// Called after each game action
}
private func updateSessionTracking() {
if balance < sessionLowBalance {
sessionLowBalance = balance
}
// Check comeback achievement
if sessionLowBalance < 100 && balance >= 10_000 {
achievementTracker.complete(.comeback)
}
}
}
```
### 3. Add Achievement Triggers
Add achievement checks throughout game logic:
**In `BaccaratEngine.swift` or `GameState.swift`:**
```swift
// After round completes
func finishRound() {
// ... existing logic ...
// First hand achievement
if totalRoundsPlayed == 1 {
achievementTracker.complete(.firstHand)
}
// Persistence achievements
achievementTracker.increment(.rounds50)
achievementTracker.increment(.rounds250)
achievementTracker.increment(.rounds500)
// Track table limits
let limitKey = settings.tableLimit.displayName
roundsPerTableLimit[limitKey, default: 0] += 1
if roundsPerTableLimit.values.filter({ $0 >= 10 }).count == 5 {
achievementTracker.complete(.allTableLimits)
}
updateSessionTracking()
}
// When placing bets
func placeBet(type: BetType, amount: Int) {
// ... existing logic ...
// High roller achievement
if amount >= 50_000 {
achievementTracker.complete(.bet50000)
}
// James Bond tracking (consecutive Banker bets)
if type == .banker {
consecutiveBankerBets += 1
if consecutiveBankerBets >= 100 {
achievementTracker.complete(.jamesBond)
}
} else {
consecutiveBankerBets = 0
}
// Streak tracking
if type == consecutiveSameBetType {
// Continue tracking
} else {
consecutiveSameBetType = type
consecutiveSameBetWins = 0
}
}
// After determining winner
func evaluateRound() {
// ... existing logic ...
// Natural achievements
if playerHand.isNatural || bankerHand.isNatural {
achievementTracker.increment(.naturals10)
achievementTracker.increment(.naturals50)
}
// Check main bet wins
if playerBetWon {
achievementTracker.increment(.playerWins10)
if consecutiveSameBetType == .player {
consecutiveSameBetWins += 1
if consecutiveSameBetWins >= 3 {
achievementTracker.increment(.spotStreaks5)
}
}
}
if bankerBetWon {
achievementTracker.increment(.bankerWins25)
if consecutiveSameBetType == .banker {
consecutiveSameBetWins += 1
if consecutiveSameBetWins >= 3 {
achievementTracker.increment(.spotStreaks5)
}
}
}
if tieBetWon {
achievementTracker.increment(.tieWins5)
}
// Side bet achievements
if playerPairWon || bankerPairWon {
achievementTracker.increment(.pairBets10)
achievementTracker.increment(.sideBetsTotal25)
}
if dragonBonusWon {
achievementTracker.increment(.dragonBonus10)
achievementTracker.increment(.sideBetsTotal25)
// Check for 9-point margin win
if dragonBonusMargin == 9 {
achievementTracker.complete(.dragonBonusWinBy9)
}
}
// Big win achievement
let netProfit = calculateNetProfit()
if netProfit >= 100_000 {
achievementTracker.complete(.singleWin100000)
}
}
// When road map is viewed
func trackRoadMapView() {
achievementTracker.increment(.viewRoadMap25)
}
```
### 4. Add Road Map View Tracking
**File:** `Baccarat/Baccarat/Views/Table/RoadMapView.swift` (or wherever road map is displayed)
```swift
.onAppear {
state.trackRoadMapView()
}
```
### 5. Add Game Center to Settings
**File:** `Baccarat/Baccarat/Views/Sheets/SettingsView.swift`
```swift
import CasinoKit
struct SettingsView: View {
// ... existing code ...
var body: some View {
SheetContainerView(title: String(localized: "Settings")) {
// ... existing sections ...
// NEW: Game Center section
GameCenterSettingsSection()
} onDone: {
dismiss()
}
}
}
```
### 6. Optional: Add Game Center Access Point
**File:** `Baccarat/Baccarat/Views/Game/GameTableView.swift`
```swift
TopBarView(
balance: state.balance,
// ... existing parameters ...
leadingButtons: [
TopBarButton(
icon: "gamecontroller.fill",
accessibilityLabel: "Game Center"
) {
GameCenterManager.shared.showGameCenterDashboard()
}
]
)
```
### 7. Initialize Game Center on App Launch
**File:** `Baccarat/Baccarat/BaccaratApp.swift`
```swift
import CasinoKit
@main
struct BaccaratApp: App {
init() {
// Authenticate with Game Center silently
Task {
await GameCenterManager.shared.authenticate()
}
}
// ... rest of app ...
}
```
### 8. Add Localized Strings
**File:** `Baccarat/Baccarat/Resources/Localizable.xcstrings`
Add all achievement titles, descriptions, and Game Center UI strings.
## App Store Connect Configuration
### For Each Achievement:
1. Log into App Store Connect
2. Select Baccarat app
3. Go to Game Center → Achievements
4. Click "+" to add achievement
5. Configure:
- **Reference Name:** (internal only)
- **Achievement ID:** Must match enum identifier
- **Point Value:** As specified above
- **Hidden:** Set to "Yes" for James Bond and Table Explorer
- **Achievable More Than Once:** No for all
- **Localization:** Add EN, ES-MX, FR-CA
- **Images:** Upload 512x512 and 1024x1024 versions
## Testing Checklist
- [ ] All achievements can be triggered in game
- [ ] Incremental achievements show progress
- [ ] Hidden achievements don't appear until unlocked
- [ ] James Bond achievement resets on non-Banker bet
- [ ] Streak tracking works correctly (3+ consecutive wins)
- [ ] Road map view tracking increments
- [ ] Table limit tracking works across all 5 limits
- [ ] Big win achievement triggers at $100k profit
- [ ] Dragon Bonus 9-point margin detected correctly
- [ ] Comeback achievement tracks session low properly
## Edge Cases to Handle
1. **Player resets game mid-session** - Session achievements (comeback) should reset
2. **Multiple simultaneous bets** - Track all winning bet types for achievements
3. **Tie pushes main bet** - Don't count as "win" for streak tracking
4. **Switching table limits** - Properly track rounds per limit
5. **Offline play** - Queue achievements, submit when online
## Future Enhancements
- **Achievement notifications** - Use CasinoKit's AchievementToast
- **Progress UI** - Show achievement progress in statistics view
- **Pattern hints** - Suggest when player is on a streak (Road Map Reader tie-in)
---
## Summary
- ✅ 20 total achievements
- ✅ Focus on gameplay understanding, pattern recognition, and milestones
- ✅ Two hidden achievements (James Bond, Table Explorer)
- ✅ Incremental progress for most achievements
- ✅ Unique Baccarat-specific achievements (road maps, Dragon Bonus, streaks)
- ✅ Integration with existing GameState and road map system
- ✅ No code duplication (uses CasinoKit infrastructure)
**Estimated Integration Time:** 2-3 hours after CasinoKit infrastructure is complete.
---
*See `CasinoKit/GAME_CENTER_PLAN.md` for shared infrastructure implementation details.*

View File

@ -0,0 +1,554 @@
# Game Center Integration - Blackjack
This document outlines the Game Center achievements for Blackjack and the game-specific integration steps.
## Prerequisites
✅ Complete `CasinoKit/GAME_CENTER_PLAN.md` implementation first (shared infrastructure)
## Achievement Design Philosophy
Focus on **learning milestones** and **skill demonstration**, not bankroll/luck-based metrics.
Achievements celebrate:
- 📚 Learning basic strategy
- 🎓 Card counting practice
- 🎯 Gameplay milestones
- 💪 Persistence and mastery
## Blackjack Achievements
### 🎓 Learning & Strategy Category
#### 1. **First Hand**
- **ID:** `com.yourdomain.blackjack.first_hand`
- **Description:** Play your first hand of Blackjack
- **Progress:** Complete on first hand
- **Points:** 5
- **Icon:** `play.circle.fill`
- **Trigger:** After first `finishRound()` completes
#### 2. **Strategy Student**
- **ID:** `com.yourdomain.blackjack.strategy_student`
- **Description:** Play 50 hands following perfect basic strategy
- **Progress:** Incremental (0-50)
- **Points:** 10
- **Icon:** `book.fill`
- **Trigger:** Each hand where player follows all hint recommendations
- **Implementation Note:** Track in `GameState` when hint matches action taken
#### 3. **Strategy Master**
- **ID:** `com.yourdomain.blackjack.strategy_master`
- **Description:** Play 100 hands following perfect basic strategy
- **Progress:** Incremental (0-100)
- **Points:** 25
- **Icon:** `graduationcap.fill`
- **Trigger:** Same as Strategy Student, extended goal
#### 4. **Card Counter**
- **ID:** `com.yourdomain.blackjack.card_counter`
- **Description:** Play 25 hands using the Hi-Lo counting system
- **Progress:** Incremental (0-25)
- **Points:** 15
- **Icon:** `brain.head.profile`
- **Trigger:** When `settings.showCount` is enabled and hand completes
- **Implementation Note:** Must have counting enabled in settings
#### 5. **True Count Master**
- **ID:** `com.yourdomain.blackjack.true_count_master`
- **Description:** Make 10 correct betting decisions based on true count
- **Progress:** Incremental (0-10)
- **Points:** 20
- **Icon:** `chart.line.uptrend.xyaxis`
- **Trigger:** When player increases bet with positive true count (+2 or higher)
- **Implementation Note:** Track bet increases that align with count recommendations
### 🎯 Gameplay Milestones Category
#### 6. **Natural Talent**
- **ID:** `com.yourdomain.blackjack.natural_10`
- **Description:** Get 10 Blackjacks (natural 21)
- **Progress:** Incremental (0-10)
- **Points:** 10
- **Icon:** `star.fill`
- **Trigger:** When player hand is natural blackjack
#### 7. **Natural Legend**
- **ID:** `com.yourdomain.blackjack.natural_50`
- **Description:** Get 50 Blackjacks
- **Progress:** Incremental (0-50)
- **Points:** 25
- **Icon:** `star.circle.fill`
- **Trigger:** When player hand is natural blackjack (extended)
#### 8. **Split Decision**
- **ID:** `com.yourdomain.blackjack.split_wins_10`
- **Description:** Win with split hands 10 times
- **Progress:** Incremental (0-10)
- **Points:** 10
- **Icon:** `rectangle.split.2x1.fill`
- **Trigger:** When a split hand wins against dealer
#### 9. **Double Down Dynamo**
- **ID:** `com.yourdomain.blackjack.double_wins_15`
- **Description:** Win 15 hands after doubling down
- **Progress:** Incremental (0-15)
- **Points:** 15
- **Icon:** `arrow.down.circle.fill`
- **Trigger:** When player wins a hand after doubling down
#### 10. **Insurance Agent**
- **ID:** `com.yourdomain.blackjack.insurance_wins_5`
- **Description:** Win insurance bet 5 times
- **Progress:** Incremental (0-5)
- **Points:** 10
- **Icon:** `shield.checkered`
- **Trigger:** When dealer has blackjack and player took insurance
### 💪 Persistence & Mastery Category
#### 11. **Rookie**
- **ID:** `com.yourdomain.blackjack.hands_100`
- **Description:** Play 100 total hands
- **Progress:** Incremental (0-100)
- **Points:** 10
- **Icon:** `person.fill`
- **Trigger:** After each hand completes
#### 12. **Regular**
- **ID:** `com.yourdomain.blackjack.hands_500`
- **Description:** Play 500 total hands
- **Progress:** Incremental (0-500)
- **Points:** 25
- **Icon:** `person.2.fill`
- **Trigger:** After each hand completes
#### 13. **Veteran**
- **ID:** `com.yourdomain.blackjack.hands_1000`
- **Description:** Play 1,000 total hands
- **Progress:** Incremental (0-1000)
- **Points:** 50
- **Icon:** `person.3.fill`
- **Trigger:** After each hand completes
#### 14. **Comeback Kid**
- **ID:** `com.yourdomain.blackjack.comeback`
- **Description:** Recover from under $100 to $10,000 in a single session
- **Progress:** Complete when achieved
- **Points:** 25
- **Icon:** `arrow.up.right.circle.fill`
- **Trigger:** When balance reaches $10k after being below $100 in same session
- **Implementation Note:** Track session low point
#### 15. **Perfect Pair Player**
- **ID:** `com.yourdomain.blackjack.perfect_pairs_5`
- **Description:** Win Perfect Pairs side bet 5 times
- **Progress:** Incremental (0-5)
- **Points:** 10
- **Icon:** `suit.heart.fill`
- **Trigger:** When Perfect Pairs side bet wins
#### 16. **21+3 Champion**
- **ID:** `com.yourdomain.blackjack.twentyone_plus_three_5`
- **Description:** Win 21+3 side bet 5 times
- **Progress:** Incremental (0-5)
- **Points:** 10
- **Icon:** `suit.diamond.fill`
- **Trigger:** When 21+3 side bet wins
### 🏆 Elite Category
#### 17. **Side Bet Specialist**
- **ID:** `com.yourdomain.blackjack.side_bet_wins_25`
- **Description:** Win any side bet 25 times total
- **Progress:** Incremental (0-25)
- **Points:** 25
- **Icon:** `plus.circle.fill`
- **Trigger:** Any time a side bet wins (Perfect Pairs or 21+3)
#### 18. **Rule Breaker** (Hidden Achievement)
- **ID:** `com.yourdomain.blackjack.all_rule_variations`
- **Description:** Play at least 10 hands with each rule variation
- **Progress:** Incremental (0-40, 10 per variation × 4 variations)
- **Points:** 15
- **Icon:** `gearshape.2.fill`
- **Trigger:** Track which rule sets have been played
- **Implementation Note:** Vegas Strip, Atlantic City, European, Custom (10 hands each)
## Implementation Steps
### 1. Define Achievement Enum
**File:** `Blackjack/Blackjack/Models/BlackjackAchievement.swift` (new file)
```swift
import CasinoKit
import GameKit
enum BlackjackAchievement: String, AchievementDefinition {
case firstHand = "first_hand"
case strategyStudent = "strategy_student"
case strategyMaster = "strategy_master"
case cardCounter = "card_counter"
case trueCountMaster = "true_count_master"
case natural10 = "natural_10"
case natural50 = "natural_50"
case splitWins10 = "split_wins_10"
case doubleWins15 = "double_wins_15"
case insuranceWins5 = "insurance_wins_5"
case hands100 = "hands_100"
case hands500 = "hands_500"
case hands1000 = "hands_1000"
case comeback = "comeback"
case perfectPairs5 = "perfect_pairs_5"
case twentyOnePlusThree5 = "twentyone_plus_three_5"
case sideBetWins25 = "side_bet_wins_25"
case allRuleVariations = "all_rule_variations"
var identifier: String {
"com.yourdomain.blackjack.\(rawValue)"
}
var title: String {
switch self {
case .firstHand: return String(localized: "First Hand")
case .strategyStudent: return String(localized: "Strategy Student")
case .strategyMaster: return String(localized: "Strategy Master")
case .cardCounter: return String(localized: "Card Counter")
case .trueCountMaster: return String(localized: "True Count Master")
case .natural10: return String(localized: "Natural Talent")
case .natural50: return String(localized: "Natural Legend")
case .splitWins10: return String(localized: "Split Decision")
case .doubleWins15: return String(localized: "Double Down Dynamo")
case .insuranceWins5: return String(localized: "Insurance Agent")
case .hands100: return String(localized: "Rookie")
case .hands500: return String(localized: "Regular")
case .hands1000: return String(localized: "Veteran")
case .comeback: return String(localized: "Comeback Kid")
case .perfectPairs5: return String(localized: "Perfect Pair Player")
case .twentyOnePlusThree5: return String(localized: "21+3 Champion")
case .sideBetWins25: return String(localized: "Side Bet Specialist")
case .allRuleVariations: return String(localized: "Rule Breaker")
}
}
var description: String {
// Full descriptions for each achievement
}
var maxProgress: Int {
switch self {
case .firstHand, .comeback: return 1
case .insuranceWins5, .perfectPairs5, .twentyOnePlusThree5: return 5
case .splitWins10, .natural10: return 10
case .doubleWins15: return 15
case .cardCounter, .sideBetWins25, .strategyMaster: return 25
case .strategyStudent, .natural50: return 50
case .hands100: return 100
case .hands500: return 500
case .hands1000: return 1000
case .trueCountMaster: return 10
case .allRuleVariations: return 40
}
}
var isIncremental: Bool {
self != .firstHand && self != .comeback
}
var iconName: String {
switch self {
case .firstHand: return "play.circle.fill"
case .strategyStudent: return "book.fill"
case .strategyMaster: return "graduationcap.fill"
case .cardCounter: return "brain.head.profile"
case .trueCountMaster: return "chart.line.uptrend.xyaxis"
case .natural10: return "star.fill"
case .natural50: return "star.circle.fill"
case .splitWins10: return "rectangle.split.2x1.fill"
case .doubleWins15: return "arrow.down.circle.fill"
case .insuranceWins5: return "shield.checkered"
case .hands100: return "person.fill"
case .hands500: return "person.2.fill"
case .hands1000: return "person.3.fill"
case .comeback: return "arrow.up.right.circle.fill"
case .perfectPairs5: return "suit.heart.fill"
case .twentyOnePlusThree5: return "suit.diamond.fill"
case .sideBetWins25: return "plus.circle.fill"
case .allRuleVariations: return "gearshape.2.fill"
}
}
}
```
### 2. Add Achievement Tracker to GameState
**File:** `Blackjack/Blackjack/Engine/GameState.swift`
```swift
import CasinoKit
@MainActor
@Observable
class GameState {
// ... existing properties ...
// NEW: Achievement tracking
private(set) var achievementTracker: AchievementTracker<BlackjackAchievement>
// Track session-specific metrics for achievements
private var sessionLowBalance: Int = 0
private var handsPlayedThisSession = 0
private var lastActionWasStrategyOptimal = false
init() {
// ... existing init ...
self.achievementTracker = AchievementTracker<BlackjackAchievement>()
self.sessionLowBalance = balance
}
// NEW: Achievement checking methods
func checkAchievements(after action: GameAction) {
// Called after each game action
// Check relevant achievements based on action type
}
private func updateSessionTracking() {
if balance < sessionLowBalance {
sessionLowBalance = balance
}
// Check comeback achievement
if sessionLowBalance < 100 && balance >= 10_000 {
achievementTracker.complete(.comeback)
}
}
}
```
### 3. Add Achievement Triggers
Add achievement checks throughout game logic:
**In `BlackjackEngine.swift` or `GameState.swift`:**
```swift
// After dealing initial cards
if playerHand.isBlackjack {
achievementTracker.increment(.natural10)
achievementTracker.increment(.natural50)
}
// After completing a hand
func finishRound() {
// ... existing logic ...
// First hand achievement
if totalHandsPlayed == 1 {
achievementTracker.complete(.firstHand)
}
// Persistence achievements
achievementTracker.increment(.hands100)
achievementTracker.increment(.hands500)
achievementTracker.increment(.hands1000)
// Check if strategy was followed
if lastActionWasStrategyOptimal {
achievementTracker.increment(.strategyStudent)
achievementTracker.increment(.strategyMaster)
}
// Card counting achievements
if settings.showCount {
achievementTracker.increment(.cardCounter)
// Check if bet was adjusted based on count
if betWasIncreasedWithPositiveCount {
achievementTracker.increment(.trueCountMaster)
}
}
updateSessionTracking()
}
// After winning with split
func checkSplitWin() {
if handWasSplit && playerWon {
achievementTracker.increment(.splitWins10)
}
}
// After winning with double down
func checkDoubleWin() {
if playerDoubledDown && playerWon {
achievementTracker.increment(.doubleWins15)
}
}
// After insurance win
func checkInsuranceWin() {
if insuranceBetWon {
achievementTracker.increment(.insuranceWins5)
}
}
// After side bet wins
func checkSideBetWins() {
if perfectPairsBetWon {
achievementTracker.increment(.perfectPairs5)
achievementTracker.increment(.sideBetWins25)
}
if twentyOnePlusThreeBetWon {
achievementTracker.increment(.twentyOnePlusThree5)
achievementTracker.increment(.sideBetWins25)
}
}
```
### 4. Add Game Center to Settings
**File:** `Blackjack/Blackjack/Views/Sheets/SettingsView.swift`
```swift
import CasinoKit
struct SettingsView: View {
// ... existing code ...
var body: some View {
SheetContainerView(title: String(localized: "Settings")) {
// ... existing sections ...
// NEW: Game Center section
GameCenterSettingsSection()
} onDone: {
dismiss()
}
}
}
```
### 5. Optional: Add Game Center Access Point
**File:** `Blackjack/Blackjack/Views/Game/GameTableView.swift`
Add to top bar or as floating overlay:
```swift
TopBarView(
balance: state.balance,
// ... existing parameters ...
leadingButtons: [
TopBarButton(
icon: "gamecontroller.fill",
accessibilityLabel: "Game Center"
) {
GameCenterManager.shared.showGameCenterDashboard()
}
]
)
```
### 6. Initialize Game Center on App Launch
**File:** `Blackjack/Blackjack/BlackjackApp.swift`
```swift
import CasinoKit
@main
struct BlackjackApp: App {
init() {
// Authenticate with Game Center silently
Task {
await GameCenterManager.shared.authenticate()
}
}
// ... rest of app ...
}
```
### 7. Add Localized Strings
**File:** `Blackjack/Blackjack/Resources/Localizable.xcstrings`
Add all achievement titles, descriptions, and Game Center UI strings.
## App Store Connect Configuration
### For Each Achievement:
1. Log into App Store Connect
2. Select Blackjack app
3. Go to Game Center → Achievements
4. Click "+" to add achievement
5. Configure:
- **Reference Name:** (internal only, e.g., "Strategy Student")
- **Achievement ID:** Must match enum identifier (e.g., `com.yourdomain.blackjack.strategy_student`)
- **Point Value:** As specified above
- **Hidden:** Set to "Yes" for hidden achievements (Rule Breaker)
- **Achievable More Than Once:** No for all
- **Pre-iOS 14 Gamers:** Leave unchecked
- **Localization:** Add EN, ES-MX, FR-CA with titles/descriptions
- **Images:** Upload 512x512 and 1024x1024 versions
### Achievement Icon Assets
Create icons matching the SF Symbols specified in the achievement enum. Use your app's color scheme (golds, blues, casino theme).
## Testing Checklist
- [ ] All achievements can be triggered in game
- [ ] Incremental achievements show progress correctly
- [ ] Achievements appear in Game Center dashboard
- [ ] Achievements work offline (queue and submit later)
- [ ] Achievement tracking persists across app launches
- [ ] Localization works for all three languages
- [ ] VoiceOver announces achievement unlocks
- [ ] Settings section shows correct Game Center status
- [ ] Strategy-based achievements verify correct play
- [ ] Card counting achievements only trigger when enabled
## Edge Cases to Handle
1. **Player resets game mid-session** - Session achievements (comeback) should reset
2. **Player changes rule variations** - Track separately for "Rule Breaker"
3. **Player disables hints** - Strategy achievements still trackable via engine logic
4. **Offline play** - Queue achievements, submit when online
5. **Multiple devices** - Game Center syncs achievement progress automatically
## Analytics to Track (Optional)
Consider adding local analytics to understand achievement engagement:
- Which achievements are earned most often?
- Average time to complete each achievement
- Percentage of players who enable card counting (for targeted education)
## Future Enhancements
- **Achievement notifications** - Show toast when unlocked (use CasinoKit's AchievementToast)
- **Progress tracking UI** - Show achievement progress in stats view
- **Suggested next achievement** - Guide players toward specific goals
---
## Summary
- ✅ 18 total achievements
- ✅ Focus on learning and skill, not luck/bankroll
- ✅ Incremental progress for most achievements
- ✅ Hidden achievement for exploring all rule variations
- ✅ Integration with existing GameState and statistics
- ✅ No code duplication (uses CasinoKit infrastructure)
**Estimated Integration Time:** 2-3 hours after CasinoKit infrastructure is complete.
---
*See `CasinoKit/GAME_CENTER_PLAN.md` for shared infrastructure implementation details.*

View File

@ -0,0 +1,364 @@
# Game Center Integration - CasinoKit (Shared Infrastructure)
This document outlines the shared Game Center infrastructure that will be used by all casino games in the workspace.
## Overview
Game Center will be integrated for **achievements only** (no leaderboards) to avoid cheating concerns with client-side casino games. All shared Game Center logic will live in CasinoKit to avoid code duplication.
## Shared Components to Add
### 1. GameCenterManager (Core Service)
**Location:** `CasinoKit/Sources/CasinoKit/GameCenter/GameCenterManager.swift`
**Purpose:** Centralized manager for all Game Center operations.
**Features:**
- Authentication with Game Center
- Achievement submission
- Achievement progress tracking
- Error handling and retry logic
- Availability checking
- Privacy-friendly (doesn't force sign-in)
**Key Methods:**
```swift
@MainActor
@Observable
class GameCenterManager {
static let shared = GameCenterManager()
var isAuthenticated: Bool = false
var isAvailable: Bool = true
var localPlayer: GKLocalPlayer?
// Authentication
func authenticate()
// Achievement management
func submitAchievement(_ identifier: String, percentComplete: Double)
func incrementAchievement(_ identifier: String, by value: Double)
func resetAchievements() // For testing only
func loadAchievements() -> [GKAchievement]
// UI presentation
func showGameCenterDashboard()
}
```
**Implementation Notes:**
- Use async/await APIs (iOS 14+)
- Silent authentication (no blocking UI)
- Gracefully handle Game Center being disabled
- Cache authentication state
- Support offline mode (queue submissions for later)
### 2. Achievement Configuration Protocol
**Location:** `CasinoKit/Sources/CasinoKit/GameCenter/AchievementDefinition.swift`
**Purpose:** Type-safe achievement definitions per game.
```swift
protocol AchievementDefinition {
var identifier: String { get }
var title: String { get }
var description: String { get }
var maxProgress: Int { get }
var iconName: String { get }
var isIncremental: Bool { get }
}
// Example usage in game:
enum BlackjackAchievement: String, AchievementDefinition {
case strategyStudent = "blackjack_strategy_50"
case cardCounter = "blackjack_card_counting_25"
// ...
var identifier: String {
"com.yourdomain.blackjack.\(rawValue)"
}
var title: String {
switch self {
case .strategyStudent: return String(localized: "Strategy Student")
case .cardCounter: return String(localized: "Card Counter")
}
}
// ... other properties
}
```
### 3. Game Center Access Point View
**Location:** `CasinoKit/Sources/CasinoKit/Views/GameCenter/GameCenterAccessPoint.swift`
**Purpose:** Standard UI component for showing Game Center status.
**Features:**
- Shows authentication state
- Displays achievement progress
- Links to Game Center dashboard
- Optional placement (top bar or settings)
- Respects user privacy preferences
```swift
struct GameCenterAccessPoint: View {
@State private var manager = GameCenterManager.shared
var body: some View {
// Small floating button or status indicator
// Shows GC icon when authenticated
// Tapping opens achievement list
}
}
```
### 4. Achievement Tracking Helper
**Location:** `CasinoKit/Sources/CasinoKit/GameCenter/AchievementTracker.swift`
**Purpose:** Helper for games to track achievement progress locally before submitting.
```swift
@MainActor
@Observable
class AchievementTracker<Achievement: AchievementDefinition> {
private var progress: [String: Int] = [:]
func increment(_ achievement: Achievement, by value: Int = 1)
func getProgress(_ achievement: Achievement) -> Int
func checkAndSubmit(_ achievement: Achievement)
func reset() // For new game sessions
}
```
**Why This Helps:**
- Games can track progress locally (fast)
- Batch submissions to Game Center (efficient)
- Handles the "report once at 100%" logic for non-incremental achievements
### 5. Settings Integration
**Location:** `CasinoKit/Sources/CasinoKit/Views/Settings/GameCenterSettingsSection.swift`
**Purpose:** Standard settings UI for Game Center.
```swift
struct GameCenterSettingsSection: View {
@State private var manager = GameCenterManager.shared
var body: some View {
SheetSection(title: "GAME CENTER", icon: "gamecontroller.fill") {
if manager.isAvailable {
HStack {
Text("Status")
Spacer()
Text(manager.isAuthenticated ? "Connected" : "Not Connected")
.foregroundStyle(.secondary)
}
if manager.isAuthenticated {
Button("View Achievements") {
manager.showGameCenterDashboard()
}
Button("Sign Out") {
// Note: Can't actually sign out from app,
// just stop authenticating
}
} else {
Button("Connect to Game Center") {
Task { await manager.authenticate() }
}
}
} else {
Text("Game Center is not available")
.foregroundStyle(.secondary)
}
}
}
}
```
### 6. Achievement Toast Notification
**Location:** `CasinoKit/Sources/CasinoKit/Views/GameCenter/AchievementToast.swift`
**Purpose:** Show celebratory notification when achievement is earned (optional enhancement).
**Features:**
- Brief animation when achievement unlocks
- Shows achievement icon and title
- Auto-dismisses after 3 seconds
- Doesn't block gameplay
- Similar to iOS system notifications
```swift
struct AchievementToast: View {
let achievement: String
let title: String
@State private var isShowing = false
var body: some View {
// Slide-in notification from top
// Shows achievement icon + title
// Fades out after delay
}
}
```
## File Structure
```
CasinoKit/Sources/CasinoKit/
├── GameCenter/
│ ├── GameCenterManager.swift # Core manager (authentication, submission)
│ ├── AchievementDefinition.swift # Protocol for type-safe achievements
│ ├── AchievementTracker.swift # Local progress tracking helper
│ └── GameCenterError.swift # Custom error types
├── Views/
│ └── GameCenter/
│ ├── GameCenterAccessPoint.swift # Status indicator/button
│ ├── GameCenterSettingsSection.swift # Settings UI component
│ └── AchievementToast.swift # Achievement unlock notification
└── Resources/
└── Localizable.xcstrings # Add GC-related strings
```
## Localization Strings to Add
Add to `CasinoKit/Resources/Localizable.xcstrings`:
```
Game Center
Connected
Not Connected
Sign Out
View Achievements
Game Center is not available
Achievement Unlocked!
Connect to Game Center
```
## App Store Connect Configuration
**Note:** Each game will need its own achievements configured in App Store Connect.
### Achievement Naming Convention
Use consistent identifier format:
```
com.yourdomain.{game}.{achievement_key}
Examples:
com.yourdomain.blackjack.strategy_student
com.yourdomain.blackjack.card_counter
com.yourdomain.baccarat.dragon_master
com.yourdomain.baccarat.natural_high
```
### Achievement Assets
Each achievement needs:
- 512x512px icon (1x)
- 1024x1024px icon (2x)
**Design Guidelines:**
- Use SF Symbols where appropriate (consistent with app design)
- Match app color scheme
- Clear, recognizable icons
- Consider accessibility (high contrast)
## Implementation Order
1. **GameCenterManager** - Core authentication and submission logic
2. **AchievementDefinition protocol** - Type system
3. **AchievementTracker** - Helper for games to use
4. **GameCenterSettingsSection** - UI integration
5. **GameCenterAccessPoint** - Optional status indicator
6. **AchievementToast** - Optional enhancement
## Testing Strategy
### Local Testing
- Test with Game Center sandbox account
- Verify authentication flow
- Test achievement submission
- Test offline behavior
- Test achievement progress tracking
### TestFlight Testing
- Required for full Game Center integration testing
- Verify achievements appear correctly
- Test achievement notifications
- Verify localization
### Debug Features to Add
```swift
#if DEBUG
extension GameCenterManager {
func resetAllAchievements() {
// Only available in debug builds
}
func logAchievementStatus() {
// Print all achievement progress
}
}
#endif
```
## Privacy Considerations
- **No forced sign-in** - Game Center is entirely optional
- **Graceful degradation** - App works fully without Game Center
- **No data collection** - Only submit achievement progress, nothing else
- **User control** - Easy to see status and disconnect
## Performance Considerations
- **Authenticate once** - On app launch, silent background auth
- **Batch submissions** - Don't submit every increment immediately
- **Cache state** - Remember authentication status
- **Async operations** - Never block UI on Game Center calls
- **Offline queue** - Store failed submissions, retry later
## Future Enhancements (Optional)
- **Challenge Mode** - Separate game mode with fixed rules and leaderboards
- **Friend Comparison** - Show achievement progress vs friends
- **Weekly Challenges** - Time-limited achievement variants
- **Leaderboards** - Only if Challenge Mode is added with anti-cheat
## Dependencies
- **GameKit framework** - Apple's Game Center SDK
- **No third-party dependencies** - Pure Apple APIs
## Estimated Effort
- **CasinoKit infrastructure**: 4-6 hours
- **Per-game integration**: 2-3 hours each
- **App Store Connect setup**: 1-2 hours
- **Testing & polish**: 2-3 hours
**Total: ~12-16 hours** for complete implementation across both games.
---
## Next Steps
1. Review this plan
2. See game-specific plans in `Blackjack/GAME_CENTER_PLAN.md` and `Baccarat/GAME_CENTER_PLAN.md`
3. Configure achievements in App Store Connect
4. Implement CasinoKit infrastructure first
5. Integrate into games second
6. Test with TestFlight
---
*This shared infrastructure approach ensures consistency across all casino games while avoiding code duplication.*