167 lines
5.1 KiB
Swift
167 lines
5.1 KiB
Swift
//
|
|
// Card.swift
|
|
// CasinoKit
|
|
//
|
|
// A playing card model with suit, rank, and value calculations.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Represents the four suits in a standard deck of cards.
|
|
public enum Suit: String, CaseIterable, Identifiable, Sendable {
|
|
case hearts = "♥"
|
|
case diamonds = "♦"
|
|
case clubs = "♣"
|
|
case spades = "♠"
|
|
|
|
public var id: String { rawValue }
|
|
|
|
/// Whether this suit is red (hearts or diamonds).
|
|
public var isRed: Bool {
|
|
self == .hearts || self == .diamonds
|
|
}
|
|
|
|
/// Accessibility name for VoiceOver.
|
|
public var accessibilityName: String {
|
|
switch self {
|
|
case .hearts: return String(localized: "Hearts", bundle: .module)
|
|
case .diamonds: return String(localized: "Diamonds", bundle: .module)
|
|
case .clubs: return String(localized: "Clubs", bundle: .module)
|
|
case .spades: return String(localized: "Spades", bundle: .module)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Represents the rank of a card from Ace through King.
|
|
public enum Rank: Int, CaseIterable, Identifiable, Sendable {
|
|
case ace = 1
|
|
case two = 2
|
|
case three = 3
|
|
case four = 4
|
|
case five = 5
|
|
case six = 6
|
|
case seven = 7
|
|
case eight = 8
|
|
case nine = 9
|
|
case ten = 10
|
|
case jack = 11
|
|
case queen = 12
|
|
case king = 13
|
|
|
|
public var id: Int { rawValue }
|
|
|
|
/// The display symbol for this rank.
|
|
public var symbol: String {
|
|
switch self {
|
|
case .ace: return "A"
|
|
case .jack: return "J"
|
|
case .queen: return "Q"
|
|
case .king: return "K"
|
|
default: return String(rawValue)
|
|
}
|
|
}
|
|
|
|
/// The standard point value (Ace = 1, 2-10 = face value, J/Q/K = 10).
|
|
public var standardValue: Int {
|
|
switch self {
|
|
case .ace: return 1
|
|
case .jack, .queen, .king: return 10
|
|
default: return rawValue
|
|
}
|
|
}
|
|
|
|
/// The baccarat point value (Ace = 1, 2-9 = face value, 10/J/Q/K = 0).
|
|
public var baccaratValue: Int {
|
|
switch self {
|
|
case .ace: return 1
|
|
case .two, .three, .four, .five, .six, .seven, .eight, .nine:
|
|
return rawValue
|
|
case .ten, .jack, .queen, .king:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
/// The blackjack value (Ace = 1 or 11, 2-10 = face value, J/Q/K = 10).
|
|
/// Note: Ace flexibility (1 or 11) should be handled by game logic.
|
|
public var blackjackValue: Int {
|
|
switch self {
|
|
case .ace: return 11 // Game logic should handle soft/hard hands
|
|
case .jack, .queen, .king: return 10
|
|
default: return rawValue
|
|
}
|
|
}
|
|
|
|
/// Accessibility name for VoiceOver.
|
|
public var accessibilityName: String {
|
|
switch self {
|
|
case .ace: return String(localized: "Ace", bundle: .module)
|
|
case .two: return String(localized: "Two", bundle: .module)
|
|
case .three: return String(localized: "Three", bundle: .module)
|
|
case .four: return String(localized: "Four", bundle: .module)
|
|
case .five: return String(localized: "Five", bundle: .module)
|
|
case .six: return String(localized: "Six", bundle: .module)
|
|
case .seven: return String(localized: "Seven", bundle: .module)
|
|
case .eight: return String(localized: "Eight", bundle: .module)
|
|
case .nine: return String(localized: "Nine", bundle: .module)
|
|
case .ten: return String(localized: "Ten", bundle: .module)
|
|
case .jack: return String(localized: "Jack", bundle: .module)
|
|
case .queen: return String(localized: "Queen", bundle: .module)
|
|
case .king: return String(localized: "King", bundle: .module)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Represents a single playing card with a suit and rank.
|
|
public struct Card: Identifiable, Equatable, Sendable {
|
|
public let id: UUID
|
|
public let suit: Suit
|
|
public let rank: Rank
|
|
|
|
public init(suit: Suit, rank: Rank, id: UUID = UUID()) {
|
|
self.id = id
|
|
self.suit = suit
|
|
self.rank = rank
|
|
}
|
|
|
|
/// The baccarat point value of this card.
|
|
public var baccaratValue: Int {
|
|
rank.baccaratValue
|
|
}
|
|
|
|
/// The blackjack value of this card.
|
|
public var blackjackValue: Int {
|
|
rank.blackjackValue
|
|
}
|
|
|
|
/// Display string showing rank and suit together.
|
|
public var display: String {
|
|
"\(rank.symbol)\(suit.rawValue)"
|
|
}
|
|
|
|
/// Accessibility description for VoiceOver.
|
|
public var accessibilityDescription: String {
|
|
"\(rank.accessibilityName) of \(suit.accessibilityName)"
|
|
}
|
|
|
|
/// The PDF page index (0-51) for this card.
|
|
/// PDF order: Spades (0-12), Hearts (13-25), Clubs (26-38), Diamonds (39-51)
|
|
/// Each suit: 2,3,4,5,6,7,8,9,10,J,Q,K,A
|
|
public var pdfIndex: Int {
|
|
let suitOffset: Int
|
|
switch suit {
|
|
case .spades: suitOffset = 0
|
|
case .hearts: suitOffset = 13
|
|
case .clubs: suitOffset = 26
|
|
case .diamonds: suitOffset = 39
|
|
}
|
|
// PDF order: 2=0, 3=1, ..., 10=8, J=9, Q=10, K=11, A=12
|
|
let rankOffset: Int
|
|
switch rank {
|
|
case .ace: rankOffset = 12 // Ace is last in PDF
|
|
default: rankOffset = rank.rawValue - 2 // 2=0, 3=1, etc.
|
|
}
|
|
return suitOffset + rankOffset
|
|
}
|
|
}
|
|
|