// // Hand.swift // Blackjack // // Represents a Blackjack hand with value calculation. // import Foundation import CasinoKit /// A hand of cards in Blackjack. struct BlackjackHand: Identifiable, Equatable { let id = UUID() var cards: [Card] var bet: Int var isDoubledDown: Bool = false var isSplit: Bool = false var isStanding: Bool = false var result: HandResult? init(cards: [Card] = [], bet: Int = 0) { self.cards = cards self.bet = bet } /// The best possible value (highest without busting, or lowest if busted). var value: Int { let (hard, soft) = calculateValues() if soft <= 21 { return soft } return hard } /// Whether this hand has a soft value (usable ace). var isSoft: Bool { let (hard, soft) = calculateValues() return soft <= 21 && soft != hard } /// Whether the hand is over 21. var isBusted: Bool { value > 21 } /// Whether this is a natural blackjack (two cards totaling 21). var isBlackjack: Bool { cards.count == 2 && value == 21 && !isSplit } /// Whether this hand has a splittable pair (two cards of same rank). /// Note: Additional conditions (balance, max splits, resplit aces) are checked by the engine. var canSplit: Bool { cards.count == 2 && cards[0].rank == cards[1].rank } /// Whether this hand has the card count to double down. /// Note: Additional conditions (balance, DAS rule) are checked by the engine. var canDoubleDown: Bool { cards.count == 2 && !isDoubledDown } /// Whether this hand can hit. /// Note: Standard Blackjack has NO card limit - you can hit until you bust or stand. var canHit: Bool { !isBusted && !isStanding && !isBlackjack } /// Calculates both hard and soft values. private func calculateValues() -> (hard: Int, soft: Int) { var hardValue = 0 var aceCount = 0 for card in cards { switch card.rank { case .ace: hardValue += 1 aceCount += 1 case .two: hardValue += 2 case .three: hardValue += 3 case .four: hardValue += 4 case .five: hardValue += 5 case .six: hardValue += 6 case .seven: hardValue += 7 case .eight: hardValue += 8 case .nine: hardValue += 9 case .ten, .jack, .queen, .king: hardValue += 10 } } // Calculate soft value (one ace as 11) var softValue = hardValue if aceCount > 0 && hardValue + 10 <= 21 { softValue = hardValue + 10 } return (hardValue, softValue) } /// Display string for the hand value. var valueDisplay: String { if isBlackjack { return "BJ" } let (hard, soft) = calculateValues() if isBusted { return "\(hard) 💥" } if isSoft && soft != hard { return "\(hard)/\(soft)" } return "\(value)" } } // MARK: - Card Value Extension extension Card { /// The blackjack value of this card (Ace = 1 or 11, face cards = 10). var blackjackValue: Int { switch rank { case .ace: return 1 // Or 11, handled by hand calculation case .two: return 2 case .three: return 3 case .four: return 4 case .five: return 5 case .six: return 6 case .seven: return 7 case .eight: return 8 case .nine: return 9 case .ten, .jack, .queen, .king: return 10 } } /// The Hi-Lo card counting value. /// Low cards (2-6): +1 (good for player when removed) /// Neutral (7-9): 0 /// High cards (10-A): -1 (bad for player when removed) var hiLoValue: Int { switch rank { case .two, .three, .four, .five, .six: return 1 // Low cards case .seven, .eight, .nine: return 0 // Neutral case .ten, .jack, .queen, .king, .ace: return -1 // High cards } } /// Display text for the Hi-Lo count value. var hiLoDisplayText: String { switch hiLoValue { case 1: return "+1" case -1: return "-1" default: return "0" } } }