136 lines
3.6 KiB
Swift
136 lines
3.6 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 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.
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|