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)
|
.disabled(gameState.currentBets.isEmpty)
|
||||||
} else {
|
} else {
|
||||||
Button("Clear", systemImage: "xmark.circle", action: onClear)
|
Button("Clear", systemImage: "xmark.circle", action: onClear)
|
||||||
.labelStyle(.titleOnly)
|
.labelStyle(.titleAndIcon)
|
||||||
.font(.system(size: buttonFontSize, weight: .semibold))
|
.font(.system(size: buttonFontSize, weight: .semibold))
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.padding(.horizontal, Design.Spacing.xxLarge)
|
.padding(.horizontal, Design.Spacing.xxLarge)
|
||||||
@ -839,10 +839,10 @@ struct ActionButtonsView: View {
|
|||||||
.disabled(!gameState.canDeal)
|
.disabled(!gameState.canDeal)
|
||||||
} else {
|
} else {
|
||||||
Button("Deal", systemImage: "play.fill", action: onDeal)
|
Button("Deal", systemImage: "play.fill", action: onDeal)
|
||||||
.labelStyle(.titleOnly)
|
.labelStyle(.titleAndIcon)
|
||||||
.font(.system(size: buttonFontSize, weight: .bold))
|
.font(.system(size: buttonFontSize, weight: .bold))
|
||||||
.foregroundStyle(.black)
|
.foregroundStyle(.black)
|
||||||
.padding(.horizontal, Design.Spacing.xxxLarge + Design.Spacing.small)
|
.padding(.horizontal, Design.Spacing.xxLarge)
|
||||||
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
|
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
|
||||||
.background(
|
.background(
|
||||||
Capsule()
|
Capsule()
|
||||||
@ -863,7 +863,7 @@ struct ActionButtonsView: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var newRoundButton: some View {
|
private var newRoundButton: some View {
|
||||||
if isAccessibilitySize {
|
if isAccessibilitySize {
|
||||||
Button("New Round", systemImage: "arrow.right.circle", action: onNewRound)
|
Button("New Round", systemImage: "arrow.clockwise", action: onNewRound)
|
||||||
.labelStyle(.iconOnly)
|
.labelStyle(.iconOnly)
|
||||||
.font(.system(size: iconSize, weight: .bold))
|
.font(.system(size: iconSize, weight: .bold))
|
||||||
.foregroundStyle(.black)
|
.foregroundStyle(.black)
|
||||||
@ -880,11 +880,11 @@ struct ActionButtonsView: View {
|
|||||||
)
|
)
|
||||||
.shadow(color: .yellow.opacity(Design.Opacity.light), radius: Design.Shadow.radiusMedium)
|
.shadow(color: .yellow.opacity(Design.Opacity.light), radius: Design.Shadow.radiusMedium)
|
||||||
} else {
|
} else {
|
||||||
Button("New Round", systemImage: "arrow.right.circle", action: onNewRound)
|
Button("New Round", systemImage: "arrow.clockwise", action: onNewRound)
|
||||||
.labelStyle(.titleOnly)
|
.labelStyle(.titleAndIcon)
|
||||||
.font(.system(size: buttonFontSize, weight: .bold))
|
.font(.system(size: buttonFontSize, weight: .bold))
|
||||||
.foregroundStyle(.black)
|
.foregroundStyle(.black)
|
||||||
.padding(.horizontal, Design.Spacing.xxxLarge + Design.Spacing.small)
|
.padding(.horizontal, Design.Spacing.xxLarge)
|
||||||
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
|
.padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall)
|
||||||
.background(
|
.background(
|
||||||
Capsule()
|
Capsule()
|
||||||
|
|||||||
@ -17,12 +17,14 @@ final class BlackjackEngine {
|
|||||||
/// The card shoe.
|
/// The card shoe.
|
||||||
private(set) var shoe: Deck
|
private(set) var shoe: Deck
|
||||||
|
|
||||||
/// Number of decks in the shoe.
|
|
||||||
let deckCount: Int
|
|
||||||
|
|
||||||
/// Settings reference for rule variations.
|
/// Settings reference for rule variations.
|
||||||
private let settings: GameSettings
|
private let settings: GameSettings
|
||||||
|
|
||||||
|
/// Number of decks in the shoe (reads from current settings).
|
||||||
|
var deckCount: Int {
|
||||||
|
settings.deckCount.rawValue
|
||||||
|
}
|
||||||
|
|
||||||
/// Cards remaining in shoe.
|
/// Cards remaining in shoe.
|
||||||
var cardsRemaining: Int {
|
var cardsRemaining: Int {
|
||||||
shoe.cardsRemaining
|
shoe.cardsRemaining
|
||||||
@ -38,16 +40,15 @@ final class BlackjackEngine {
|
|||||||
|
|
||||||
init(settings: GameSettings) {
|
init(settings: GameSettings) {
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.deckCount = settings.deckCount.rawValue
|
self.shoe = Deck(deckCount: settings.deckCount.rawValue)
|
||||||
self.shoe = Deck(deckCount: deckCount)
|
|
||||||
shoe.shuffle()
|
shoe.shuffle()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Shoe Management
|
// MARK: - Shoe Management
|
||||||
|
|
||||||
/// Reshuffles the shoe.
|
/// Reshuffles the shoe with the current deck count from settings.
|
||||||
func reshuffle() {
|
func reshuffle() {
|
||||||
shoe = Deck(deckCount: deckCount)
|
shoe = Deck(deckCount: settings.deckCount.rawValue)
|
||||||
shoe.shuffle()
|
shoe.shuffle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -168,6 +168,11 @@ final class GameState {
|
|||||||
sound.volume = settings.soundVolume
|
sound.volume = settings.soundVolume
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when deck count setting changes - reshuffles with new deck count.
|
||||||
|
func applyDeckCountChange() {
|
||||||
|
engine.reshuffle()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Persistence
|
// MARK: - Persistence
|
||||||
|
|
||||||
/// Loads saved game data from iCloud or local storage.
|
/// 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
|
let delay = settings.showAnimations ? 0.3 * settings.dealingSpeed : 0
|
||||||
|
|
||||||
// Deal cards: player, dealer, player, dealer
|
// European no-hole-card: deal 3 cards (player, dealer, player)
|
||||||
for i in 0..<4 {
|
// 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 let card = engine.dealCard() {
|
||||||
if i % 2 == 0 {
|
if i % 2 == 0 {
|
||||||
playerHands[0].cards.append(card)
|
playerHands[0].cards.append(card)
|
||||||
@ -283,14 +291,24 @@ final class GameState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for insurance offer
|
// Check for insurance offer (only in American style with hole card)
|
||||||
if let upCard = dealerUpCard, engine.shouldOfferInsurance(dealerUpCard: upCard) {
|
if !settings.noHoleCard, let upCard = dealerUpCard, engine.shouldOfferInsurance(dealerUpCard: upCard) {
|
||||||
currentPhase = .insurance
|
currentPhase = .insurance
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for immediate blackjacks
|
// Check for immediate blackjacks (only in American style - European checks after player acts)
|
||||||
await checkForBlackjacks()
|
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.
|
/// Checks for blackjacks and handles accordingly.
|
||||||
@ -491,12 +509,42 @@ final class GameState {
|
|||||||
private func dealerTurn() async {
|
private func dealerTurn() async {
|
||||||
currentPhase = .dealerTurn
|
currentPhase = .dealerTurn
|
||||||
|
|
||||||
// Reveal hole card
|
|
||||||
sound.play(.cardFlip)
|
|
||||||
|
|
||||||
let delay = settings.showAnimations ? 0.5 * settings.dealingSpeed : 0
|
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
|
// Dealer draws
|
||||||
|
|||||||
@ -99,6 +99,9 @@ final class GameSettings {
|
|||||||
/// Whether late surrender is allowed.
|
/// Whether late surrender is allowed.
|
||||||
var lateSurrender: Bool = true { didSet { save() } }
|
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.
|
/// Whether insurance is offered.
|
||||||
var insuranceAllowed: Bool = true { didSet { save() } }
|
var insuranceAllowed: Bool = true { didSet { save() } }
|
||||||
|
|
||||||
@ -171,6 +174,7 @@ final class GameSettings {
|
|||||||
doubleAfterSplit = true
|
doubleAfterSplit = true
|
||||||
resplitAces = false
|
resplitAces = false
|
||||||
lateSurrender = false
|
lateSurrender = false
|
||||||
|
noHoleCard = false // American: dealer gets hole card upfront
|
||||||
blackjackPayout = 1.5
|
blackjackPayout = 1.5
|
||||||
|
|
||||||
case .atlantic:
|
case .atlantic:
|
||||||
@ -179,6 +183,7 @@ final class GameSettings {
|
|||||||
doubleAfterSplit = true
|
doubleAfterSplit = true
|
||||||
resplitAces = true
|
resplitAces = true
|
||||||
lateSurrender = true
|
lateSurrender = true
|
||||||
|
noHoleCard = false // American: dealer gets hole card upfront
|
||||||
blackjackPayout = 1.5
|
blackjackPayout = 1.5
|
||||||
|
|
||||||
case .european:
|
case .european:
|
||||||
@ -187,6 +192,7 @@ final class GameSettings {
|
|||||||
doubleAfterSplit = true
|
doubleAfterSplit = true
|
||||||
resplitAces = false
|
resplitAces = false
|
||||||
lateSurrender = false
|
lateSurrender = false
|
||||||
|
noHoleCard = true // European: dealer gets second card after player acts
|
||||||
blackjackPayout = 1.5
|
blackjackPayout = 1.5
|
||||||
|
|
||||||
case .custom:
|
case .custom:
|
||||||
@ -220,6 +226,7 @@ final class GameSettings {
|
|||||||
self.doubleAfterSplit = data.doubleAfterSplit
|
self.doubleAfterSplit = data.doubleAfterSplit
|
||||||
self.resplitAces = data.resplitAces
|
self.resplitAces = data.resplitAces
|
||||||
self.lateSurrender = data.lateSurrender
|
self.lateSurrender = data.lateSurrender
|
||||||
|
self.noHoleCard = data.noHoleCard
|
||||||
self.blackjackPayout = data.blackjackPayout
|
self.blackjackPayout = data.blackjackPayout
|
||||||
self.insuranceAllowed = data.insuranceAllowed
|
self.insuranceAllowed = data.insuranceAllowed
|
||||||
self.showAnimations = data.showAnimations
|
self.showAnimations = data.showAnimations
|
||||||
@ -243,6 +250,7 @@ final class GameSettings {
|
|||||||
doubleAfterSplit: doubleAfterSplit,
|
doubleAfterSplit: doubleAfterSplit,
|
||||||
resplitAces: resplitAces,
|
resplitAces: resplitAces,
|
||||||
lateSurrender: lateSurrender,
|
lateSurrender: lateSurrender,
|
||||||
|
noHoleCard: noHoleCard,
|
||||||
blackjackPayout: blackjackPayout,
|
blackjackPayout: blackjackPayout,
|
||||||
insuranceAllowed: insuranceAllowed,
|
insuranceAllowed: insuranceAllowed,
|
||||||
showAnimations: showAnimations,
|
showAnimations: showAnimations,
|
||||||
|
|||||||
@ -38,10 +38,34 @@
|
|||||||
"• Use an online tool to generate all sizes" : {
|
"• Use an online tool to generate all sizes" : {
|
||||||
"comment" : "A step in the process of exporting app icons.",
|
"comment" : "A step in the process of exporting app icons.",
|
||||||
"isCommentAutoGenerated" : true
|
"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" : {
|
"2-10: Face value" : {
|
||||||
"comment" : "Description of the card values for cards with values 2 through 10.",
|
"comment" : "Description of the card values for cards with values 2 through 10.",
|
||||||
"isCommentAutoGenerated" : true
|
"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." : {
|
"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.",
|
"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" : {
|
"Alternative: Use an online tool" : {
|
||||||
"comment" : "A section header that suggests using an online tool to generate app icons.",
|
"comment" : "A section header that suggests using an online tool to generate app icons.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Always split Aces and 8s." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"An Ace + 10-value card dealt initially is 'Blackjack'." : {
|
"An Ace + 10-value card dealt initially is 'Blackjack'." : {
|
||||||
"comment" : "Description of a blackjack hand.",
|
"comment" : "Description of a blackjack hand.",
|
||||||
@ -188,6 +215,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Basic Strategy" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Basic strategy suggestions" : {
|
"Basic strategy suggestions" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -306,6 +336,9 @@
|
|||||||
"Blackjack pays 3:2 (1.5x your bet)." : {
|
"Blackjack pays 3:2 (1.5x your bet)." : {
|
||||||
"comment" : "Description of the payout for blackjack in the Blackjack rules help view.",
|
"comment" : "Description of the payout for blackjack in the Blackjack rules help view.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Blackjack pays 3:2." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Blackjack: 3:2" : {
|
"Blackjack: 3:2" : {
|
||||||
"comment" : "Payout description for a Blackjack win.",
|
"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." : {
|
"Dealer must hit on 16 or less." : {
|
||||||
"comment" : "Description of the dealer's rule to hit if the hand value is 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" : {
|
"Dealer stands on soft 17, double after split, 3:2 blackjack" : {
|
||||||
"comment" : "Description of the \"Vegas Strip\" blackjack rule variation.",
|
"comment" : "Description of the \"Vegas Strip\" blackjack rule variation.",
|
||||||
@ -724,6 +766,9 @@
|
|||||||
"Dealer: No cards" : {
|
"Dealer: No cards" : {
|
||||||
"comment" : "Accessibility label for the dealer hand when there are no cards visible.",
|
"comment" : "Accessibility label for the dealer hand when there are no cards visible.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Deck Count" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"DECK SETTINGS" : {
|
"DECK SETTINGS" : {
|
||||||
"localizations" : {
|
"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" : {
|
"Double Down" : {
|
||||||
"localizations" : {
|
"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" : {
|
"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.",
|
"comment" : "Action available in Blackjack when the player wants to double their bet, take one more card, and then stand.",
|
||||||
"isCommentAutoGenerated" : true
|
"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" : {
|
"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.",
|
"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" : {
|
"GAME STYLE" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -990,6 +1062,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Higher house edge due to no hole card." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Hint: %@" : {
|
"Hint: %@" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1034,6 +1109,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Hit on soft 17 or less." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Hit: Take another card" : {
|
"Hit: Take another card" : {
|
||||||
"comment" : "Action available in Blackjack: 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" : {
|
"Launch" : {
|
||||||
"comment" : "A tab in the BrandingPreviewView that links to the launch screen preview.",
|
"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" : {
|
"Net" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1350,6 +1440,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Never split 10s or 5s." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"NEW GAME" : {
|
"NEW GAME" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1438,6 +1531,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"No hole card: dealer takes second card after player acts." : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"No surrender option." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Objective" : {
|
"Objective" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1635,6 +1734,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Re-split aces allowed." : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Re-split Aces: Reduces house edge by ~0.05%." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Roulette" : {
|
"Roulette" : {
|
||||||
"comment" : "The name of a roulette card.",
|
"comment" : "The name of a roulette card.",
|
||||||
@ -1665,6 +1770,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Rule Variations" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"RULES" : {
|
"RULES" : {
|
||||||
"localizations" : {
|
"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" : {
|
"Split: If you have two cards of the same value, split into two hands" : {
|
||||||
"comment" : "Description of the 'Split' action in the game rules.",
|
"comment" : "Description of the 'Split' action in the game rules.",
|
||||||
@ -1919,6 +2030,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Stand on 17+ always." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Stand: Keep your current hand" : {
|
"Stand: Keep your current hand" : {
|
||||||
"comment" : "Action to keep your current hand in Blackjack.",
|
"comment" : "Action to keep your current hand in Blackjack.",
|
||||||
@ -1927,6 +2041,9 @@
|
|||||||
"Standard casino" : {
|
"Standard casino" : {
|
||||||
"comment" : "Description of a deck count option when the user selects 6 decks.",
|
"comment" : "Description of a deck count option when the user selects 6 decks.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Standard rules on the East Coast." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Statistics" : {
|
"Statistics" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1993,6 +2110,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Surrender 16 vs dealer 9, 10, Ace." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Surrender after dealer checks for blackjack" : {
|
"Surrender after dealer checks for blackjack" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -2093,6 +2213,9 @@
|
|||||||
"These show how the same pattern works for other games" : {
|
"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.",
|
"comment" : "A description below the section of the view that previews icons for other games.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Traditional European casino style." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : {
|
"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.",
|
"comment" : "A description of an alternative method for generating app icons.",
|
||||||
|
|||||||
@ -63,6 +63,7 @@ struct BlackjackSettingsData: PersistableGameData {
|
|||||||
doubleAfterSplit: true,
|
doubleAfterSplit: true,
|
||||||
resplitAces: false,
|
resplitAces: false,
|
||||||
lateSurrender: true,
|
lateSurrender: true,
|
||||||
|
noHoleCard: false,
|
||||||
blackjackPayout: 1.5,
|
blackjackPayout: 1.5,
|
||||||
insuranceAllowed: true,
|
insuranceAllowed: true,
|
||||||
showAnimations: true,
|
showAnimations: true,
|
||||||
@ -84,6 +85,7 @@ struct BlackjackSettingsData: PersistableGameData {
|
|||||||
var doubleAfterSplit: Bool
|
var doubleAfterSplit: Bool
|
||||||
var resplitAces: Bool
|
var resplitAces: Bool
|
||||||
var lateSurrender: Bool
|
var lateSurrender: Bool
|
||||||
|
var noHoleCard: Bool
|
||||||
var blackjackPayout: Double
|
var blackjackPayout: Double
|
||||||
var insuranceAllowed: Bool
|
var insuranceAllowed: Bool
|
||||||
var showAnimations: Bool
|
var showAnimations: Bool
|
||||||
|
|||||||
@ -120,8 +120,14 @@ struct DealerHandView: View {
|
|||||||
.font(.system(size: labelFontSize, weight: .bold, design: .rounded))
|
.font(.system(size: labelFontSize, weight: .bold, design: .rounded))
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
|
|
||||||
if !hand.cards.isEmpty && showHoleCard {
|
// Show value: always show if hole card visible, or show single card value in European mode
|
||||||
ValueBadge(value: hand.value, color: Color.Hand.dealer)
|
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))
|
.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: "Push: Bet returned"),
|
||||||
String(localized: "Surrender: Half 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") {
|
SheetSection(title: String(localized: "DECK SETTINGS"), icon: "rectangle.portrait.on.rectangle.portrait") {
|
||||||
DeckCountPicker(selection: $settings.deckCount)
|
DeckCountPicker(selection: $settings.deckCount)
|
||||||
}
|
}
|
||||||
|
.onChange(of: settings.deckCount) { _, _ in
|
||||||
|
// Reshuffle with new deck count
|
||||||
|
gameState?.applyDeckCountChange()
|
||||||
|
}
|
||||||
|
|
||||||
// Table Limits
|
// Table Limits
|
||||||
SheetSection(title: String(localized: "TABLE LIMITS"), icon: "banknote") {
|
SheetSection(title: String(localized: "TABLE LIMITS"), icon: "banknote") {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user