From ed1dabfe180cc8de0a56c9732fe0b7c6f44c84b1 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 31 Dec 2025 14:02:52 -0600 Subject: [PATCH] Signed-off-by: Matt Bruce --- Baccarat/Baccarat/Engine/GameState.swift | 16 +++++ .../Baccarat/Views/Game/GameTableView.swift | 4 +- Blackjack/Blackjack/Engine/GameState.swift | 13 +--- Blackjack/Blackjack/Models/GameSettings.swift | 8 --- .../Blackjack/Resources/Localizable.xcstrings | 3 + .../Blackjack/Storage/BlackjackGameData.swift | 2 - .../Blackjack/Views/Sheets/SettingsView.swift | 10 --- .../Views/Table/BettingZoneView.swift | 64 ++++++++----------- .../Views/Table/BlackjackTableView.swift | 2 +- .../CasinoKit/Models/CasinoGameState.swift | 11 ++++ 10 files changed, 63 insertions(+), 70 deletions(-) diff --git a/Baccarat/Baccarat/Engine/GameState.swift b/Baccarat/Baccarat/Engine/GameState.swift index f77142d..d1b58cb 100644 --- a/Baccarat/Baccarat/Engine/GameState.swift +++ b/Baccarat/Baccarat/Engine/GameState.swift @@ -591,6 +591,22 @@ final class GameState: CasinoGameState { return currentAmount + amount <= maxBet } + /// The minimum bet level across all bet types that can accept more chips. + /// Used by chip selector to determine if chips should be enabled. + /// Returns the smallest bet so chips stay enabled if ANY bet type can accept more. + var minBetForChipSelector: Int { + // All bet types are always available in Baccarat + let allBetTypes: [BetType] = [ + .player, .banker, .tie, + .playerPair, .bankerPair, + .dragonBonusPlayer, .dragonBonusBanker + ] + + // Return the minimum bet amount across all bet types + // so chips stay enabled if any bet type can accept more + return allBetTypes.map { betAmount(for: $0) }.min() ?? 0 + } + // MARK: - Betting Actions /// Places a bet of the specified amount on the given bet type. diff --git a/Baccarat/Baccarat/Views/Game/GameTableView.swift b/Baccarat/Baccarat/Views/Game/GameTableView.swift index aab8ab6..cd3b2ca 100644 --- a/Baccarat/Baccarat/Views/Game/GameTableView.swift +++ b/Baccarat/Baccarat/Views/Game/GameTableView.swift @@ -353,7 +353,7 @@ struct GameTableView: View { ChipSelectorView( selectedChip: $selectedChip, balance: state.balance, - currentBet: state.totalBetAmount, + currentBet: state.minBetForChipSelector, maxBet: state.maxBet ) .transition(.opacity.combined(with: .move(edge: .bottom))) @@ -464,7 +464,7 @@ struct GameTableView: View { ChipSelectorView( selectedChip: $selectedChip, balance: state.balance, - currentBet: state.totalBetAmount, + currentBet: state.minBetForChipSelector, maxBet: state.maxBet ) .transition(.opacity.combined(with: .move(edge: .bottom))) diff --git a/Blackjack/Blackjack/Engine/GameState.swift b/Blackjack/Blackjack/Engine/GameState.swift index 9df2d86..4560686 100644 --- a/Blackjack/Blackjack/Engine/GameState.swift +++ b/Blackjack/Blackjack/Engine/GameState.swift @@ -177,7 +177,6 @@ final class GameState: CasinoGameState { /// Whether a specific side bet can accept more chips of the given amount. /// Matches Baccarat's canAddToBet pattern. func canAddToSideBet(type: SideBetType, amount: Int) -> Bool { - guard settings.sideBetsEnabled else { return false } let currentAmount: Int switch type { case .perfectPairs: @@ -204,16 +203,12 @@ final class GameState: CasinoGameState { return false } - /// The minimum bet level across all active bet types. + /// The minimum bet level across all bet types. /// Used by chip selector to determine if chips should be enabled. /// Returns the smallest bet so chips stay enabled if ANY bet type can accept more. var minBetForChipSelector: Int { - if settings.sideBetsEnabled { - // Return the minimum of all bet types so chips stay enabled if any can be increased - return min(currentBet, perfectPairsBet, twentyOnePlusThreeBet) - } else { - return currentBet - } + // Return the minimum of all bet types so chips stay enabled if any can be increased + min(currentBet, perfectPairsBet, twentyOnePlusThreeBet) } /// Whether the current hand can hit. @@ -983,8 +978,6 @@ final class GameState: CasinoGameState { /// Evaluates side bets based on the initial deal. private func evaluateSideBets() { - guard settings.sideBetsEnabled else { return } - let playerCards = playerHands[0].cards guard playerCards.count >= 2 else { return } diff --git a/Blackjack/Blackjack/Models/GameSettings.swift b/Blackjack/Blackjack/Models/GameSettings.swift index 7d8205a..91ed9d8 100644 --- a/Blackjack/Blackjack/Models/GameSettings.swift +++ b/Blackjack/Blackjack/Models/GameSettings.swift @@ -136,11 +136,6 @@ final class GameSettings: GameSettingsProtocol { /// Speed of card dealing (uses CasinoDesign.DealingSpeed constants) var dealingSpeed: Double = CasinoDesign.DealingSpeed.normal { didSet { save() } } - // MARK: - Side Bets - - /// Whether side bets (Perfect Pairs, 21+3) are enabled. - var sideBetsEnabled: Bool = false { didSet { save() } } - // MARK: - Display Settings /// Whether to show the cards remaining indicator. @@ -242,7 +237,6 @@ final class GameSettings: GameSettingsProtocol { self.blackjackPayout = data.blackjackPayout self.insuranceAllowed = data.insuranceAllowed self.neverAskInsurance = data.neverAskInsurance - self.sideBetsEnabled = data.sideBetsEnabled self.showAnimations = data.showAnimations self.dealingSpeed = data.dealingSpeed self.showCardsRemaining = data.showCardsRemaining @@ -269,7 +263,6 @@ final class GameSettings: GameSettingsProtocol { blackjackPayout: blackjackPayout, insuranceAllowed: insuranceAllowed, neverAskInsurance: neverAskInsurance, - sideBetsEnabled: sideBetsEnabled, showAnimations: showAnimations, dealingSpeed: dealingSpeed, showCardsRemaining: showCardsRemaining, @@ -297,7 +290,6 @@ final class GameSettings: GameSettingsProtocol { blackjackPayout = 1.5 insuranceAllowed = true neverAskInsurance = false - sideBetsEnabled = false showAnimations = true dealingSpeed = CasinoDesign.DealingSpeed.normal showCardsRemaining = true diff --git a/Blackjack/Blackjack/Resources/Localizable.xcstrings b/Blackjack/Blackjack/Resources/Localizable.xcstrings index 9629d08..53fb547 100644 --- a/Blackjack/Blackjack/Resources/Localizable.xcstrings +++ b/Blackjack/Blackjack/Resources/Localizable.xcstrings @@ -3250,6 +3250,7 @@ } }, "Enable Side Bets" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -5348,6 +5349,7 @@ } }, "Perfect Pairs (25:1) and 21+3 (100:1)" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -6329,6 +6331,7 @@ } }, "SIDE BETS" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { diff --git a/Blackjack/Blackjack/Storage/BlackjackGameData.swift b/Blackjack/Blackjack/Storage/BlackjackGameData.swift index 08706b4..4d2e891 100644 --- a/Blackjack/Blackjack/Storage/BlackjackGameData.swift +++ b/Blackjack/Blackjack/Storage/BlackjackGameData.swift @@ -62,7 +62,6 @@ struct BlackjackSettingsData: PersistableGameData { blackjackPayout: 1.5, insuranceAllowed: true, neverAskInsurance: false, - sideBetsEnabled: false, showAnimations: true, dealingSpeed: 1.0, showCardsRemaining: true, @@ -87,7 +86,6 @@ struct BlackjackSettingsData: PersistableGameData { var blackjackPayout: Double var insuranceAllowed: Bool var neverAskInsurance: Bool - var sideBetsEnabled: Bool var showAnimations: Bool var dealingSpeed: Double var showCardsRemaining: Bool diff --git a/Blackjack/Blackjack/Views/Sheets/SettingsView.swift b/Blackjack/Blackjack/Views/Sheets/SettingsView.swift index 92627f8..12b19fc 100644 --- a/Blackjack/Blackjack/Views/Sheets/SettingsView.swift +++ b/Blackjack/Blackjack/Views/Sheets/SettingsView.swift @@ -111,16 +111,6 @@ struct SettingsView: View { TableLimitsPicker(selection: $settings.tableLimits) } - // 3.5. Side Bets - SheetSection(title: String(localized: "SIDE BETS"), icon: "dollarsign.arrow.trianglehead.counterclockwise.rotate.90") { - SettingsToggle( - title: String(localized: "Enable Side Bets"), - subtitle: String(localized: "Perfect Pairs (25:1) and 21+3 (100:1)"), - isOn: $settings.sideBetsEnabled, - accentColor: accent - ) - } - // 4. Deck Settings SheetSection(title: String(localized: "DECK SETTINGS"), icon: "rectangle.portrait.on.rectangle.portrait") { DeckCountPicker(selection: $settings.deckCount) diff --git a/Blackjack/Blackjack/Views/Table/BettingZoneView.swift b/Blackjack/Blackjack/Views/Table/BettingZoneView.swift index be86ca1..127ce58 100644 --- a/Blackjack/Blackjack/Views/Table/BettingZoneView.swift +++ b/Blackjack/Blackjack/Views/Table/BettingZoneView.swift @@ -55,38 +55,32 @@ struct BettingZoneView: View { } var body: some View { - if state.settings.sideBetsEnabled { - // Horizontal layout: PP | Main Bet | 21+3 - HStack(spacing: Design.Spacing.small) { - // Perfect Pairs - SideBetZoneView( - betType: .perfectPairs, - betAmount: state.perfectPairsBet, - isEnabled: canAddPerfectPairs, - isAtMax: isPPAtMax, - onTap: { state.placeSideBet(type: .perfectPairs, amount: selectedChip.rawValue) } - ) - .frame(width: Design.Size.sideBetZoneWidth) - - // Main bet (center, takes remaining space) - mainBetZone - - // 21+3 - SideBetZoneView( - betType: .twentyOnePlusThree, - betAmount: state.twentyOnePlusThreeBet, - isEnabled: canAddTwentyOnePlusThree, - isAtMax: is21PlusThreeAtMax, - onTap: { state.placeSideBet(type: .twentyOnePlusThree, amount: selectedChip.rawValue) } - ) - .frame(width: Design.Size.sideBetZoneWidth) - } - .frame(height: zoneHeight) - } else { - // Simple layout: just main bet + // Horizontal layout: PP | Main Bet | 21+3 + HStack(spacing: Design.Spacing.small) { + // Perfect Pairs + SideBetZoneView( + betType: .perfectPairs, + betAmount: state.perfectPairsBet, + isEnabled: canAddPerfectPairs, + isAtMax: isPPAtMax, + onTap: { state.placeSideBet(type: .perfectPairs, amount: selectedChip.rawValue) } + ) + .frame(width: Design.Size.sideBetZoneWidth) + + // Main bet (center, takes remaining space) mainBetZone - .frame(height: zoneHeight) + + // 21+3 + SideBetZoneView( + betType: .twentyOnePlusThree, + betAmount: state.twentyOnePlusThreeBet, + isEnabled: canAddTwentyOnePlusThree, + isAtMax: is21PlusThreeAtMax, + onTap: { state.placeSideBet(type: .twentyOnePlusThree, amount: selectedChip.rawValue) } + ) + .frame(width: Design.Size.sideBetZoneWidth) } + .frame(height: zoneHeight) } private var mainBetZone: some View { @@ -155,14 +149,12 @@ extension Design.Size { } } -#Preview("With Side Bets") { +#Preview("Main Bet Only") { ZStack { Color.Table.felt.ignoresSafeArea() BettingZoneView( state: { - let settings = GameSettings() - settings.sideBetsEnabled = true - let state = GameState(settings: settings) + let state = GameState(settings: GameSettings()) state.placeBet(amount: 100) return state }(), @@ -177,9 +169,7 @@ extension Design.Size { Color.Table.felt.ignoresSafeArea() BettingZoneView( state: { - let settings = GameSettings() - settings.sideBetsEnabled = true - let state = GameState(settings: settings) + let state = GameState(settings: GameSettings()) state.placeBet(amount: 250) state.placeSideBet(type: .perfectPairs, amount: 25) state.placeSideBet(type: .twentyOnePlusThree, amount: 50) diff --git a/Blackjack/Blackjack/Views/Table/BlackjackTableView.swift b/Blackjack/Blackjack/Views/Table/BlackjackTableView.swift index f1186eb..6fb515b 100644 --- a/Blackjack/Blackjack/Views/Table/BlackjackTableView.swift +++ b/Blackjack/Blackjack/Views/Table/BlackjackTableView.swift @@ -135,7 +135,7 @@ struct BlackjackTableView: View { ) // Side bet toasts (positioned on left/right sides to not cover cards) - if state.settings.sideBetsEnabled && state.showSideBetToasts { + if state.showSideBetToasts { HStack { // PP on left if state.perfectPairsBet > 0, let ppResult = state.perfectPairsResult { diff --git a/CasinoKit/Sources/CasinoKit/Models/CasinoGameState.swift b/CasinoKit/Sources/CasinoKit/Models/CasinoGameState.swift index 5ec1b00..70bc411 100644 --- a/CasinoKit/Sources/CasinoKit/Models/CasinoGameState.swift +++ b/CasinoKit/Sources/CasinoKit/Models/CasinoGameState.swift @@ -36,6 +36,17 @@ public protocol CasinoGameState: SessionManagedGame { /// Aggregated statistics from all sessions. var aggregatedStats: AggregatedSessionStats { get } + + // MARK: - Chip Selection + + /// The minimum bet level across all bet types. + /// Used by ChipSelectorView to determine if chips should be enabled. + /// Returns the smallest bet so chips stay enabled if ANY bet type can accept more. + /// + /// Games with multiple bet types (main bet, side bets) should return the minimum + /// bet amount across all placeable bet types, ensuring the chip selector remains + /// responsive as long as at least one bet type has room for more chips. + var minBetForChipSelector: Int { get } } // MARK: - Default Implementations