CasinoGames/Blackjack/Models/Hand.swift

134 lines
3.4 KiB
Swift

//
// 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 can be split (two cards of same rank).
var canSplit: Bool {
cards.count == 2 && cards[0].rank == cards[1].rank && !isSplit
}
/// Whether this hand can double down.
var canDoubleDown: Bool {
cards.count == 2 && !isDoubledDown && !isSplit
}
/// Whether this hand can hit.
var canHit: Bool {
!isBusted && !isStanding && !isBlackjack && cards.count < 5
}
/// 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
}
}
}