CasinoGames/Blackjack/Models/GameSettings.swift

274 lines
8.5 KiB
Swift

//
// 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<BlackjackSettingsData>()
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<BlackjackSettingsData>
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)
}
}