// // GameSettings.swift // Blackjack // // User-configurable game settings including Blackjack rule variations. // import Foundation import SwiftUI import CasinoKit /// Blackjack rule variation presets. enum BlackjackStyle: String, CaseIterable, Identifiable { case vegas = "vegas" // Vegas Strip rules case atlantic = "atlantic" // Atlantic City rules case european = "european" // European no-hole-card case custom = "custom" var id: String { rawValue } var displayName: String { switch self { case .vegas: return String(localized: "Vegas Strip") case .atlantic: return String(localized: "Atlantic City") case .european: return String(localized: "European") case .custom: return String(localized: "Custom") } } var description: String { switch self { case .vegas: return String(localized: "Dealer stands on soft 17, double after split, 3:2 blackjack") case .atlantic: return String(localized: "Dealer stands on soft 17, late surrender, 8 decks") case .european: return String(localized: "No hole card, dealer stands on soft 17, no surrender") case .custom: return String(localized: "Customize all rules") } } } /// Number of decks in the shoe. enum DeckCount: Int, CaseIterable, Identifiable { case one = 1 case two = 2 case four = 4 case six = 6 case eight = 8 var id: Int { rawValue } var displayName: String { switch self { case .one: return "1 Deck" case .two: return "2 Decks" case .four: return "4 Decks" case .six: return "6 Decks" case .eight: return "8 Decks" } } var description: String { switch self { case .one: return String(localized: "Single deck, higher variance") case .two: return String(localized: "Lower house edge") case .four: return String(localized: "Common shoe game") case .six: return String(localized: "Standard casino") case .eight: return String(localized: "Maximum penetration") } } } /// Observable settings class for Blackjack configuration. @Observable @MainActor final class GameSettings { // MARK: - Game Style /// The preset rule variation. var gameStyle: BlackjackStyle = .vegas { didSet { applyStylePreset() save() } } // MARK: - Rule Options /// Number of decks in the shoe. var deckCount: DeckCount = .six { didSet { save() } } /// Whether dealer hits on soft 17. var dealerHitsSoft17: Bool = false { didSet { save() } } /// Whether player can double after split. var doubleAfterSplit: Bool = true { didSet { save() } } /// Whether player can re-split aces. var resplitAces: Bool = false { didSet { save() } } /// Whether late surrender is allowed. var lateSurrender: Bool = true { didSet { save() } } /// Whether European no-hole-card rule is used (dealer gets second card after player acts). var noHoleCard: Bool = false { didSet { save() } } /// Whether insurance is offered. var insuranceAllowed: Bool = true { didSet { save() } } /// Blackjack payout ratio (1.5 = 3:2, 1.2 = 6:5) var blackjackPayout: Double = 1.5 { didSet { save() } } // MARK: - Betting Limits /// The table limits preset. var tableLimits: TableLimits = .low { didSet { save() } } /// Minimum bet amount. var minBet: Int { tableLimits.minBet } /// Maximum bet amount. var maxBet: Int { tableLimits.maxBet } // MARK: - Starting Balance /// The starting balance for new games. var startingBalance: Int = 10_000 { didSet { save() } } // MARK: - Animation Settings /// Whether to show dealing animations. var showAnimations: Bool = true { didSet { save() } } /// Speed of card dealing (1.0 = normal) var dealingSpeed: Double = 1.0 { didSet { save() } } // MARK: - Display Settings /// Whether to show the cards remaining indicator. var showCardsRemaining: Bool = true { didSet { save() } } /// Whether to show hand history. var showHistory: Bool = true { didSet { save() } } /// Whether to show dealer hints (suggested action). var showHints: Bool = true { didSet { save() } } /// Whether to show the running card count (Hi-Lo system). var showCardCount: Bool = false { didSet { save() } } // MARK: - Sound Settings /// Whether sound effects are enabled. var soundEnabled: Bool = true { didSet { save() } } /// Whether haptic feedback is enabled. var hapticsEnabled: Bool = true { didSet { save() } } /// Volume level for sound effects. var soundVolume: Float = 1.0 { didSet { save() } } // MARK: - Initialization init() { self.persistence = CloudSyncManager() load() applyStylePreset() } // MARK: - Style Presets private func applyStylePreset() { guard gameStyle != .custom else { return } switch gameStyle { case .vegas: deckCount = .six dealerHitsSoft17 = false doubleAfterSplit = true resplitAces = false lateSurrender = false noHoleCard = false // American: dealer gets hole card upfront blackjackPayout = 1.5 case .atlantic: deckCount = .eight dealerHitsSoft17 = false doubleAfterSplit = true resplitAces = true lateSurrender = true noHoleCard = false // American: dealer gets hole card upfront blackjackPayout = 1.5 case .european: deckCount = .six dealerHitsSoft17 = false doubleAfterSplit = true resplitAces = false lateSurrender = false noHoleCard = true // European: dealer gets second card after player acts blackjackPayout = 1.5 case .custom: break } } // MARK: - Persistence private let persistence: CloudSyncManager var iCloudAvailable: Bool { FileManager.default.ubiquityIdentityToken != nil } func load() { let data = persistence.load() if let style = BlackjackStyle(rawValue: data.gameStyle) { self.gameStyle = style } if let count = DeckCount(rawValue: data.deckCount) { self.deckCount = count } if let limits = TableLimits(rawValue: data.tableLimits) { self.tableLimits = limits } self.startingBalance = data.startingBalance self.dealerHitsSoft17 = data.dealerHitsSoft17 self.doubleAfterSplit = data.doubleAfterSplit self.resplitAces = data.resplitAces self.lateSurrender = data.lateSurrender self.noHoleCard = data.noHoleCard self.blackjackPayout = data.blackjackPayout self.insuranceAllowed = data.insuranceAllowed self.showAnimations = data.showAnimations self.dealingSpeed = data.dealingSpeed self.showCardsRemaining = data.showCardsRemaining self.showHistory = data.showHistory self.showHints = data.showHints self.showCardCount = data.showCardCount self.soundEnabled = data.soundEnabled self.hapticsEnabled = data.hapticsEnabled self.soundVolume = data.soundVolume } func save() { let data = BlackjackSettingsData( lastModified: Date(), gameStyle: gameStyle.rawValue, deckCount: deckCount.rawValue, tableLimits: tableLimits.rawValue, startingBalance: startingBalance, dealerHitsSoft17: dealerHitsSoft17, doubleAfterSplit: doubleAfterSplit, resplitAces: resplitAces, lateSurrender: lateSurrender, noHoleCard: noHoleCard, blackjackPayout: blackjackPayout, insuranceAllowed: insuranceAllowed, showAnimations: showAnimations, dealingSpeed: dealingSpeed, showCardsRemaining: showCardsRemaining, showHistory: showHistory, showHints: showHints, showCardCount: showCardCount, soundEnabled: soundEnabled, hapticsEnabled: hapticsEnabled, soundVolume: soundVolume ) persistence.save(data) } }