Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
a7d550eef1
commit
13010a3131
@ -802,7 +802,7 @@ struct ActionButtonsView: View {
|
||||
.disabled(gameState.currentBets.isEmpty)
|
||||
} else {
|
||||
Button("Clear", systemImage: "xmark.circle", action: onClear)
|
||||
.labelStyle(.titleOnly)
|
||||
.labelStyle(.titleAndIcon)
|
||||
.font(.system(size: buttonFontSize, weight: .semibold))
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, Design.Spacing.xxLarge)
|
||||
@ -839,10 +839,10 @@ struct ActionButtonsView: View {
|
||||
.disabled(!gameState.canDeal)
|
||||
} else {
|
||||
Button("Deal", systemImage: "play.fill", action: onDeal)
|
||||
.labelStyle(.titleOnly)
|
||||
.labelStyle(.titleAndIcon)
|
||||
.font(.system(size: buttonFontSize, weight: .bold))
|
||||
.foregroundStyle(.black)
|
||||
.padding(.horizontal, Design.Spacing.xxxLarge + Design.Spacing.small)
|
||||
.padding(.horizontal, Design.Spacing.xxLarge)
|
||||
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
|
||||
.background(
|
||||
Capsule()
|
||||
@ -863,7 +863,7 @@ struct ActionButtonsView: View {
|
||||
@ViewBuilder
|
||||
private var newRoundButton: some View {
|
||||
if isAccessibilitySize {
|
||||
Button("New Round", systemImage: "arrow.right.circle", action: onNewRound)
|
||||
Button("New Round", systemImage: "arrow.clockwise", action: onNewRound)
|
||||
.labelStyle(.iconOnly)
|
||||
.font(.system(size: iconSize, weight: .bold))
|
||||
.foregroundStyle(.black)
|
||||
@ -880,11 +880,11 @@ struct ActionButtonsView: View {
|
||||
)
|
||||
.shadow(color: .yellow.opacity(Design.Opacity.light), radius: Design.Shadow.radiusMedium)
|
||||
} else {
|
||||
Button("New Round", systemImage: "arrow.right.circle", action: onNewRound)
|
||||
.labelStyle(.titleOnly)
|
||||
Button("New Round", systemImage: "arrow.clockwise", action: onNewRound)
|
||||
.labelStyle(.titleAndIcon)
|
||||
.font(.system(size: buttonFontSize, weight: .bold))
|
||||
.foregroundStyle(.black)
|
||||
.padding(.horizontal, Design.Spacing.xxxLarge + Design.Spacing.small)
|
||||
.padding(.horizontal, Design.Spacing.xxLarge)
|
||||
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
|
||||
.background(
|
||||
Capsule()
|
||||
|
||||
@ -17,12 +17,14 @@ final class BlackjackEngine {
|
||||
/// The card shoe.
|
||||
private(set) var shoe: Deck
|
||||
|
||||
/// Number of decks in the shoe.
|
||||
let deckCount: Int
|
||||
|
||||
/// Settings reference for rule variations.
|
||||
private let settings: GameSettings
|
||||
|
||||
/// Number of decks in the shoe (reads from current settings).
|
||||
var deckCount: Int {
|
||||
settings.deckCount.rawValue
|
||||
}
|
||||
|
||||
/// Cards remaining in shoe.
|
||||
var cardsRemaining: Int {
|
||||
shoe.cardsRemaining
|
||||
@ -38,16 +40,15 @@ final class BlackjackEngine {
|
||||
|
||||
init(settings: GameSettings) {
|
||||
self.settings = settings
|
||||
self.deckCount = settings.deckCount.rawValue
|
||||
self.shoe = Deck(deckCount: deckCount)
|
||||
self.shoe = Deck(deckCount: settings.deckCount.rawValue)
|
||||
shoe.shuffle()
|
||||
}
|
||||
|
||||
// MARK: - Shoe Management
|
||||
|
||||
/// Reshuffles the shoe.
|
||||
/// Reshuffles the shoe with the current deck count from settings.
|
||||
func reshuffle() {
|
||||
shoe = Deck(deckCount: deckCount)
|
||||
shoe = Deck(deckCount: settings.deckCount.rawValue)
|
||||
shoe.shuffle()
|
||||
}
|
||||
|
||||
|
||||
@ -168,6 +168,11 @@ final class GameState {
|
||||
sound.volume = settings.soundVolume
|
||||
}
|
||||
|
||||
/// Called when deck count setting changes - reshuffles with new deck count.
|
||||
func applyDeckCountChange() {
|
||||
engine.reshuffle()
|
||||
}
|
||||
|
||||
// MARK: - Persistence
|
||||
|
||||
/// Loads saved game data from iCloud or local storage.
|
||||
@ -268,8 +273,11 @@ final class GameState {
|
||||
|
||||
let delay = settings.showAnimations ? 0.3 * settings.dealingSpeed : 0
|
||||
|
||||
// Deal cards: player, dealer, player, dealer
|
||||
for i in 0..<4 {
|
||||
// European no-hole-card: deal 3 cards (player, dealer, player)
|
||||
// American style: deal 4 cards (player, dealer, player, dealer)
|
||||
let cardCount = settings.noHoleCard ? 3 : 4
|
||||
|
||||
for i in 0..<cardCount {
|
||||
if let card = engine.dealCard() {
|
||||
if i % 2 == 0 {
|
||||
playerHands[0].cards.append(card)
|
||||
@ -283,14 +291,24 @@ final class GameState {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for insurance offer
|
||||
if let upCard = dealerUpCard, engine.shouldOfferInsurance(dealerUpCard: upCard) {
|
||||
// Check for insurance offer (only in American style with hole card)
|
||||
if !settings.noHoleCard, let upCard = dealerUpCard, engine.shouldOfferInsurance(dealerUpCard: upCard) {
|
||||
currentPhase = .insurance
|
||||
return
|
||||
}
|
||||
|
||||
// Check for immediate blackjacks
|
||||
await checkForBlackjacks()
|
||||
// Check for immediate blackjacks (only in American style - European checks after player acts)
|
||||
if !settings.noHoleCard {
|
||||
await checkForBlackjacks()
|
||||
} else {
|
||||
// European: just go to player turn (blackjacks checked after player acts)
|
||||
if playerHands[0].isBlackjack {
|
||||
// Player blackjack - will be handled after dealer gets second card
|
||||
currentPhase = .playerTurn(handIndex: 0)
|
||||
} else {
|
||||
currentPhase = .playerTurn(handIndex: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for blackjacks and handles accordingly.
|
||||
@ -491,12 +509,42 @@ final class GameState {
|
||||
private func dealerTurn() async {
|
||||
currentPhase = .dealerTurn
|
||||
|
||||
// Reveal hole card
|
||||
sound.play(.cardFlip)
|
||||
|
||||
let delay = settings.showAnimations ? 0.5 * settings.dealingSpeed : 0
|
||||
if delay > 0 {
|
||||
try? await Task.sleep(for: .seconds(delay))
|
||||
|
||||
// European no-hole-card: deal the second card now
|
||||
if settings.noHoleCard && dealerHand.cards.count == 1 {
|
||||
if let card = engine.dealCard() {
|
||||
dealerHand.cards.append(card)
|
||||
sound.play(.cardDeal)
|
||||
|
||||
if delay > 0 {
|
||||
try? await Task.sleep(for: .seconds(delay))
|
||||
}
|
||||
}
|
||||
|
||||
// Check for dealer blackjack in European mode
|
||||
// Player loses everything (no early check in European)
|
||||
if dealerHand.isBlackjack {
|
||||
// Mark player hands as lost if they don't have blackjack
|
||||
for i in 0..<playerHands.count {
|
||||
if playerHands[i].result == nil {
|
||||
if playerHands[i].isBlackjack {
|
||||
playerHands[i].result = .push
|
||||
} else {
|
||||
playerHands[i].result = .lose
|
||||
}
|
||||
}
|
||||
}
|
||||
await completeRound()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// American style: reveal hole card
|
||||
sound.play(.cardFlip)
|
||||
|
||||
if delay > 0 {
|
||||
try? await Task.sleep(for: .seconds(delay))
|
||||
}
|
||||
}
|
||||
|
||||
// Dealer draws
|
||||
|
||||
@ -99,6 +99,9 @@ final class GameSettings {
|
||||
/// 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() } }
|
||||
|
||||
@ -171,6 +174,7 @@ final class GameSettings {
|
||||
doubleAfterSplit = true
|
||||
resplitAces = false
|
||||
lateSurrender = false
|
||||
noHoleCard = false // American: dealer gets hole card upfront
|
||||
blackjackPayout = 1.5
|
||||
|
||||
case .atlantic:
|
||||
@ -179,6 +183,7 @@ final class GameSettings {
|
||||
doubleAfterSplit = true
|
||||
resplitAces = true
|
||||
lateSurrender = true
|
||||
noHoleCard = false // American: dealer gets hole card upfront
|
||||
blackjackPayout = 1.5
|
||||
|
||||
case .european:
|
||||
@ -187,6 +192,7 @@ final class GameSettings {
|
||||
doubleAfterSplit = true
|
||||
resplitAces = false
|
||||
lateSurrender = false
|
||||
noHoleCard = true // European: dealer gets second card after player acts
|
||||
blackjackPayout = 1.5
|
||||
|
||||
case .custom:
|
||||
@ -220,6 +226,7 @@ final class GameSettings {
|
||||
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
|
||||
@ -243,6 +250,7 @@ final class GameSettings {
|
||||
doubleAfterSplit: doubleAfterSplit,
|
||||
resplitAces: resplitAces,
|
||||
lateSurrender: lateSurrender,
|
||||
noHoleCard: noHoleCard,
|
||||
blackjackPayout: blackjackPayout,
|
||||
insuranceAllowed: insuranceAllowed,
|
||||
showAnimations: showAnimations,
|
||||
|
||||
@ -38,10 +38,34 @@
|
||||
"• Use an online tool to generate all sizes" : {
|
||||
"comment" : "A step in the process of exporting app icons.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"1 Deck: Lowest house edge (~0.17%), rare to find." : {
|
||||
|
||||
},
|
||||
"2 Decks: Low house edge (~0.35%), common online." : {
|
||||
|
||||
},
|
||||
"2-10: Face value" : {
|
||||
"comment" : "Description of the card values for cards with values 2 through 10.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"4 Decks: Moderate house edge (~0.45%)." : {
|
||||
|
||||
},
|
||||
"6 decks shuffled together." : {
|
||||
|
||||
},
|
||||
"6 Decks: Standard in Vegas (~0.50%)." : {
|
||||
|
||||
},
|
||||
"6:5 Blackjack (avoid!): Increases house edge by ~1.4%." : {
|
||||
|
||||
},
|
||||
"8 decks shuffled together." : {
|
||||
|
||||
},
|
||||
"8 Decks: Standard in Atlantic City (~0.55%)." : {
|
||||
|
||||
},
|
||||
"A 'soft' hand has an Ace counting as 11." : {
|
||||
"comment" : "Explanation of how an Ace can be counted as either 1 or 11 in a hand.",
|
||||
@ -128,6 +152,9 @@
|
||||
"Alternative: Use an online tool" : {
|
||||
"comment" : "A section header that suggests using an online tool to generate app icons.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Always split Aces and 8s." : {
|
||||
|
||||
},
|
||||
"An Ace + 10-value card dealt initially is 'Blackjack'." : {
|
||||
"comment" : "Description of a blackjack hand.",
|
||||
@ -188,6 +215,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Basic Strategy" : {
|
||||
|
||||
},
|
||||
"Basic strategy suggestions" : {
|
||||
"localizations" : {
|
||||
@ -306,6 +336,9 @@
|
||||
"Blackjack pays 3:2 (1.5x your bet)." : {
|
||||
"comment" : "Description of the payout for blackjack in the Blackjack rules help view.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Blackjack pays 3:2." : {
|
||||
|
||||
},
|
||||
"Blackjack: 3:2" : {
|
||||
"comment" : "Payout description for a Blackjack win.",
|
||||
@ -648,6 +681,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dealer Hits Soft 17: Increases house edge by ~0.2%." : {
|
||||
|
||||
},
|
||||
"Dealer must hit on 16 or less." : {
|
||||
"comment" : "Description of the dealer's rule to hit if the hand value is 16 or less.",
|
||||
@ -700,6 +736,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dealer stands on all 17s (including soft 17)." : {
|
||||
|
||||
},
|
||||
"Dealer stands on all 17s." : {
|
||||
|
||||
},
|
||||
"Dealer stands on soft 17, double after split, 3:2 blackjack" : {
|
||||
"comment" : "Description of the \"Vegas Strip\" blackjack rule variation.",
|
||||
@ -724,6 +766,9 @@
|
||||
"Dealer: No cards" : {
|
||||
"comment" : "Accessibility label for the dealer hand when there are no cards visible.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Deck Count" : {
|
||||
|
||||
},
|
||||
"DECK SETTINGS" : {
|
||||
"localizations" : {
|
||||
@ -834,6 +879,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Double after split (DAS) allowed." : {
|
||||
|
||||
},
|
||||
"Double After Split (DAS): Reduces house edge by ~0.15%." : {
|
||||
|
||||
},
|
||||
"Double after split allowed." : {
|
||||
|
||||
},
|
||||
"Double Down" : {
|
||||
"localizations" : {
|
||||
@ -856,10 +910,25 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Double down allowed on any two cards." : {
|
||||
|
||||
},
|
||||
"Double down on any two cards." : {
|
||||
|
||||
},
|
||||
"Double Down: Double your bet, take one card, then stand" : {
|
||||
"comment" : "Action available in Blackjack when the player wants to double their bet, take one more card, and then stand.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Double on 9, 10, or 11 only (some venues)." : {
|
||||
|
||||
},
|
||||
"Double on 10 vs dealer 2-9." : {
|
||||
|
||||
},
|
||||
"Double on 11 vs dealer 2-10." : {
|
||||
|
||||
},
|
||||
"Double tap to add chips" : {
|
||||
"comment" : "A hint that appears when a user taps on the betting zone, instructing them to double-tap to add chips.",
|
||||
@ -886,6 +955,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Fewer decks favor the player slightly." : {
|
||||
|
||||
},
|
||||
"GAME STYLE" : {
|
||||
"localizations" : {
|
||||
@ -990,6 +1062,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Higher house edge due to no hole card." : {
|
||||
|
||||
},
|
||||
"Hint: %@" : {
|
||||
"localizations" : {
|
||||
@ -1034,6 +1109,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Hit on soft 17 or less." : {
|
||||
|
||||
},
|
||||
"Hit: Take another card" : {
|
||||
"comment" : "Action available in Blackjack: Hit (take another card).",
|
||||
@ -1202,6 +1280,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Late surrender available." : {
|
||||
|
||||
},
|
||||
"Late Surrender: Reduces house edge by ~0.07%." : {
|
||||
|
||||
},
|
||||
"Launch" : {
|
||||
"comment" : "A tab in the BrandingPreviewView that links to the launch screen preview.",
|
||||
@ -1328,6 +1412,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"More decks = harder to count cards." : {
|
||||
|
||||
},
|
||||
"Most popular style on the Las Vegas Strip." : {
|
||||
|
||||
},
|
||||
"Net" : {
|
||||
"localizations" : {
|
||||
@ -1350,6 +1440,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Never split 10s or 5s." : {
|
||||
|
||||
},
|
||||
"NEW GAME" : {
|
||||
"localizations" : {
|
||||
@ -1438,6 +1531,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"No hole card: dealer takes second card after player acts." : {
|
||||
|
||||
},
|
||||
"No surrender option." : {
|
||||
|
||||
},
|
||||
"Objective" : {
|
||||
"localizations" : {
|
||||
@ -1635,6 +1734,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Re-split aces allowed." : {
|
||||
|
||||
},
|
||||
"Re-split Aces: Reduces house edge by ~0.05%." : {
|
||||
|
||||
},
|
||||
"Roulette" : {
|
||||
"comment" : "The name of a roulette card.",
|
||||
@ -1665,6 +1770,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rule Variations" : {
|
||||
|
||||
},
|
||||
"RULES" : {
|
||||
"localizations" : {
|
||||
@ -1893,6 +2001,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Split up to 4 hands, but not aces." : {
|
||||
|
||||
},
|
||||
"Split: If you have two cards of the same value, split into two hands" : {
|
||||
"comment" : "Description of the 'Split' action in the game rules.",
|
||||
@ -1919,6 +2030,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Stand on 17+ always." : {
|
||||
|
||||
},
|
||||
"Stand: Keep your current hand" : {
|
||||
"comment" : "Action to keep your current hand in Blackjack.",
|
||||
@ -1927,6 +2041,9 @@
|
||||
"Standard casino" : {
|
||||
"comment" : "Description of a deck count option when the user selects 6 decks.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Standard rules on the East Coast." : {
|
||||
|
||||
},
|
||||
"Statistics" : {
|
||||
"localizations" : {
|
||||
@ -1993,6 +2110,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Surrender 16 vs dealer 9, 10, Ace." : {
|
||||
|
||||
},
|
||||
"Surrender after dealer checks for blackjack" : {
|
||||
"localizations" : {
|
||||
@ -2093,6 +2213,9 @@
|
||||
"These show how the same pattern works for other games" : {
|
||||
"comment" : "A description below the section of the view that previews icons for other games.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Traditional European casino style." : {
|
||||
|
||||
},
|
||||
"Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : {
|
||||
"comment" : "A description of an alternative method for generating app icons.",
|
||||
|
||||
@ -63,6 +63,7 @@ struct BlackjackSettingsData: PersistableGameData {
|
||||
doubleAfterSplit: true,
|
||||
resplitAces: false,
|
||||
lateSurrender: true,
|
||||
noHoleCard: false,
|
||||
blackjackPayout: 1.5,
|
||||
insuranceAllowed: true,
|
||||
showAnimations: true,
|
||||
@ -84,6 +85,7 @@ struct BlackjackSettingsData: PersistableGameData {
|
||||
var doubleAfterSplit: Bool
|
||||
var resplitAces: Bool
|
||||
var lateSurrender: Bool
|
||||
var noHoleCard: Bool
|
||||
var blackjackPayout: Double
|
||||
var insuranceAllowed: Bool
|
||||
var showAnimations: Bool
|
||||
|
||||
@ -120,8 +120,14 @@ struct DealerHandView: View {
|
||||
.font(.system(size: labelFontSize, weight: .bold, design: .rounded))
|
||||
.foregroundStyle(.white)
|
||||
|
||||
if !hand.cards.isEmpty && showHoleCard {
|
||||
ValueBadge(value: hand.value, color: Color.Hand.dealer)
|
||||
// Show value: always show if hole card visible, or show single card value in European mode
|
||||
if !hand.cards.isEmpty {
|
||||
if showHoleCard {
|
||||
ValueBadge(value: hand.value, color: Color.Hand.dealer)
|
||||
} else if hand.cards.count == 1 {
|
||||
// European mode: show single visible card value
|
||||
ValueBadge(value: hand.cards[0].blackjackValue, color: Color.Hand.dealer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,6 +146,12 @@ struct DealerHandView: View {
|
||||
)
|
||||
.zIndex(Double(index))
|
||||
}
|
||||
|
||||
// Show placeholder for second card in European mode (no hole card)
|
||||
if hand.cards.count == 1 && !showHoleCard {
|
||||
CardPlaceholderView(width: cardWidth)
|
||||
.opacity(Design.Opacity.medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -81,6 +81,85 @@ struct RulesHelpView: View {
|
||||
String(localized: "Push: Bet returned"),
|
||||
String(localized: "Surrender: Half bet returned")
|
||||
]
|
||||
),
|
||||
RulePage(
|
||||
title: String(localized: "Vegas Strip"),
|
||||
icon: "sparkles",
|
||||
content: [
|
||||
String(localized: "Most popular style on the Las Vegas Strip."),
|
||||
String(localized: "6 decks shuffled together."),
|
||||
String(localized: "Dealer stands on all 17s (including soft 17)."),
|
||||
String(localized: "Double down allowed on any two cards."),
|
||||
String(localized: "Double after split (DAS) allowed."),
|
||||
String(localized: "Split up to 4 hands, but not aces."),
|
||||
String(localized: "No surrender option."),
|
||||
String(localized: "Blackjack pays 3:2.")
|
||||
]
|
||||
),
|
||||
RulePage(
|
||||
title: String(localized: "Atlantic City"),
|
||||
icon: "building.2.fill",
|
||||
content: [
|
||||
String(localized: "Standard rules on the East Coast."),
|
||||
String(localized: "8 decks shuffled together."),
|
||||
String(localized: "Dealer stands on all 17s."),
|
||||
String(localized: "Double down on any two cards."),
|
||||
String(localized: "Double after split allowed."),
|
||||
String(localized: "Re-split aces allowed."),
|
||||
String(localized: "Late surrender available."),
|
||||
String(localized: "Blackjack pays 3:2.")
|
||||
]
|
||||
),
|
||||
RulePage(
|
||||
title: String(localized: "European"),
|
||||
icon: "globe.europe.africa.fill",
|
||||
content: [
|
||||
String(localized: "Traditional European casino style."),
|
||||
String(localized: "6 decks shuffled together."),
|
||||
String(localized: "No hole card: dealer takes second card after player acts."),
|
||||
String(localized: "Dealer stands on all 17s."),
|
||||
String(localized: "Double on 9, 10, or 11 only (some venues)."),
|
||||
String(localized: "Double after split allowed."),
|
||||
String(localized: "No surrender option."),
|
||||
String(localized: "Higher house edge due to no hole card.")
|
||||
]
|
||||
),
|
||||
RulePage(
|
||||
title: String(localized: "Deck Count"),
|
||||
icon: "rectangle.stack.fill",
|
||||
content: [
|
||||
String(localized: "1 Deck: Lowest house edge (~0.17%), rare to find."),
|
||||
String(localized: "2 Decks: Low house edge (~0.35%), common online."),
|
||||
String(localized: "4 Decks: Moderate house edge (~0.45%)."),
|
||||
String(localized: "6 Decks: Standard in Vegas (~0.50%)."),
|
||||
String(localized: "8 Decks: Standard in Atlantic City (~0.55%)."),
|
||||
String(localized: "More decks = harder to count cards."),
|
||||
String(localized: "Fewer decks favor the player slightly.")
|
||||
]
|
||||
),
|
||||
RulePage(
|
||||
title: String(localized: "Rule Variations"),
|
||||
icon: "slider.horizontal.3",
|
||||
content: [
|
||||
String(localized: "Dealer Hits Soft 17: Increases house edge by ~0.2%."),
|
||||
String(localized: "Double After Split (DAS): Reduces house edge by ~0.15%."),
|
||||
String(localized: "Re-split Aces: Reduces house edge by ~0.05%."),
|
||||
String(localized: "Late Surrender: Reduces house edge by ~0.07%."),
|
||||
String(localized: "6:5 Blackjack (avoid!): Increases house edge by ~1.4%.")
|
||||
]
|
||||
),
|
||||
RulePage(
|
||||
title: String(localized: "Basic Strategy"),
|
||||
icon: "lightbulb.fill",
|
||||
content: [
|
||||
String(localized: "Always split Aces and 8s."),
|
||||
String(localized: "Never split 10s or 5s."),
|
||||
String(localized: "Double on 11 vs dealer 2-10."),
|
||||
String(localized: "Double on 10 vs dealer 2-9."),
|
||||
String(localized: "Stand on 17+ always."),
|
||||
String(localized: "Hit on soft 17 or less."),
|
||||
String(localized: "Surrender 16 vs dealer 9, 10, Ace.")
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@ -27,6 +27,10 @@ struct SettingsView: View {
|
||||
SheetSection(title: String(localized: "DECK SETTINGS"), icon: "rectangle.portrait.on.rectangle.portrait") {
|
||||
DeckCountPicker(selection: $settings.deckCount)
|
||||
}
|
||||
.onChange(of: settings.deckCount) { _, _ in
|
||||
// Reshuffle with new deck count
|
||||
gameState?.applyDeckCountChange()
|
||||
}
|
||||
|
||||
// Table Limits
|
||||
SheetSection(title: String(localized: "TABLE LIMITS"), icon: "banknote") {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user