// // RulesHelpView.swift // Blackjack // // Game rules and how to play guide. // import SwiftUI import CasinoKit struct RulesHelpView: View { @Environment(\.dismiss) private var dismiss @State private var currentPage = 0 private let pages: [RulePage] = [ RulePage( title: String(localized: "Objective"), icon: "target", content: [ String(localized: "Beat the dealer by getting a hand value closer to 21 without going over."), String(localized: "If you go over 21, you 'bust' and lose immediately."), String(localized: "If the dealer busts and you haven't, you win.") ] ), RulePage( title: String(localized: "Card Values"), icon: "suit.spade.fill", content: [ String(localized: "2-10: Face value"), String(localized: "Jack, Queen, King: 10"), String(localized: "Ace: 1 or 11 (whichever helps your hand)"), String(localized: "A 'soft' hand has an Ace counting as 11.") ] ), RulePage( title: String(localized: "Blackjack"), icon: "star.fill", content: [ String(localized: "An Ace + 10-value card dealt initially is 'Blackjack'."), String(localized: "Blackjack pays 3:2 (1.5x your bet)."), String(localized: "If both you and dealer have Blackjack, it's a push (tie).") ] ), RulePage( title: String(localized: "Actions"), icon: "hand.tap.fill", content: [ String(localized: "Hit: Take another card"), String(localized: "Stand: Keep your current hand"), String(localized: "Double Down: Double your bet, take one card, then stand"), String(localized: "Split: If you have two cards of the same value, split into two hands"), String(localized: "Surrender: Give up half your bet and end the hand") ] ), RulePage( title: String(localized: "Insurance"), icon: "shield.fill", content: [ String(localized: "Offered when dealer shows an Ace."), String(localized: "Costs half your original bet."), String(localized: "Pays 2:1 if dealer has Blackjack."), String(localized: "Generally not recommended by basic strategy.") ] ), RulePage( title: String(localized: "Dealer Rules"), icon: "person.fill", content: [ String(localized: "Dealer must hit on 16 or less."), String(localized: "Dealer must stand on 17 or more (varies by rules)."), String(localized: "Some games: Dealer hits on 'soft 17' (Ace + 6).") ] ), RulePage( title: String(localized: "Payouts"), icon: "dollarsign.circle.fill", content: [ String(localized: "Win: 1:1 (even money)"), String(localized: "Blackjack: 3:2"), String(localized: "Insurance: 2:1"), 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.") ] ), RulePage( title: String(localized: "Card Counting"), icon: "number.circle.fill", content: [ String(localized: "Hi-Lo is the most popular counting system."), String(localized: "2-6: +1 (low cards favor house)"), String(localized: "7-9: 0 (neutral cards)"), String(localized: "10-A: -1 (high cards favor player)"), String(localized: "Running Count: Sum of all card values seen."), String(localized: "True Count: Running count ÷ decks remaining."), String(localized: "Positive count = more high cards remain = player advantage.") ] ), RulePage( title: String(localized: "Using the Count"), icon: "chart.line.uptrend.xyaxis", content: [ String(localized: "True count of +2 or higher favors the player."), String(localized: "Increase bets when the count is positive."), String(localized: "Decrease bets when the count is negative."), String(localized: "Fewer decks = easier to count accurately."), String(localized: "Count resets to 0 when the shoe is shuffled."), String(localized: "Enable 'Card Count' in Settings to practice.") ] ) ] var body: some View { NavigationStack { ZStack { Color.Settings.background .ignoresSafeArea() VStack(spacing: 0) { // Page content TabView(selection: $currentPage) { ForEach(pages.indices, id: \.self) { index in RulePageView(page: pages[index]) .tag(index) } } .tabViewStyle(.page(indexDisplayMode: .never)) // Page indicator HStack(spacing: Design.Spacing.small) { ForEach(pages.indices, id: \.self) { index in Circle() .fill(index == currentPage ? Color.Settings.accent : Color.white.opacity(Design.Opacity.light)) .frame(width: 8, height: 8) } } .padding(.vertical, Design.Spacing.medium) } } .navigationTitle(String(localized: "How to Play")) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button(String(localized: "Done")) { dismiss() } .foregroundStyle(Color.Settings.accent) } } .toolbarBackground(Color.Settings.background, for: .navigationBar) .toolbarColorScheme(.dark, for: .navigationBar) } } } // MARK: - Rule Page Model struct RulePage: Identifiable { let id = UUID() let title: String let icon: String let content: [String] } // MARK: - Rule Page View struct RulePageView: View { let page: RulePage @ScaledMetric(relativeTo: .title) private var iconSize: CGFloat = Design.BaseFontSize.display @ScaledMetric(relativeTo: .title) private var titleSize: CGFloat = Design.BaseFontSize.title @ScaledMetric(relativeTo: .body) private var bodySize: CGFloat = Design.BaseFontSize.body var body: some View { ScrollView { VStack(spacing: Design.Spacing.xLarge) { // Icon Image(systemName: page.icon) .font(.system(size: iconSize)) .foregroundStyle(Color.Settings.accent) .padding(.top, Design.Spacing.xxLarge) // Title Text(page.title) .font(.system(size: titleSize, weight: .bold)) .foregroundStyle(.white) // Content VStack(alignment: .leading, spacing: Design.Spacing.medium) { ForEach(page.content.indices, id: \.self) { index in HStack(alignment: .top, spacing: Design.Spacing.medium) { Text("•") .foregroundStyle(Color.Settings.accent) Text(page.content[index]) .font(.system(size: bodySize)) .foregroundStyle(.white.opacity(Design.Opacity.heavy)) } } } .padding(.horizontal, Design.Spacing.xxLarge) Spacer() } } } } #Preview { RulesHelpView() }