// // BaccaratEngine.swift // Baccarat // // Core game engine implementing all baccarat rules including third card logic. // import Foundation /// The baccarat game engine implementing Punto Banco rules. struct BaccaratEngine { private(set) var shoe: Shoe private(set) var playerHand: Hand private(set) var bankerHand: Hand /// Creates a new engine with a fresh shoe. init(deckCount: Int = 8) { shoe = Shoe(deckCount: deckCount) playerHand = Hand() bankerHand = Hand() // Burn first card according to casino rules shoe.burn(1) } /// Clears hands and checks if shoe needs reshuffling. mutating func prepareNewRound() { playerHand.clear() bankerHand.clear() if shoe.needsReshuffle { shoe.shuffle() shoe.burn(1) } } /// Deals the initial two cards to both Player and Banker. /// Returns the cards in order they were dealt: P1, B1, P2, B2 mutating func dealInitialCards() -> [Card] { var dealtCards: [Card] = [] // Deal alternating: Player, Banker, Player, Banker if let p1 = shoe.deal() { playerHand.addCard(p1) dealtCards.append(p1) } if let b1 = shoe.deal() { bankerHand.addCard(b1) dealtCards.append(b1) } if let p2 = shoe.deal() { playerHand.addCard(p2) dealtCards.append(p2) } if let b2 = shoe.deal() { bankerHand.addCard(b2) dealtCards.append(b2) } return dealtCards } /// Determines if the Player should draw a third card. /// Player draws on 0-5, stands on 6-7. Natural (8-9) prevents drawing. func shouldPlayerDraw() -> Bool { // Check for naturals first if playerHand.isNatural || bankerHand.isNatural { return false } return playerHand.value <= 5 } /// Draws a third card for the Player if rules allow. /// - Returns: The drawn card, or nil if Player stands. mutating func drawPlayerThirdCard() -> Card? { guard shouldPlayerDraw() else { return nil } if let card = shoe.deal() { playerHand.addCard(card) return card } return nil } /// Determines if the Banker should draw a third card. /// This follows the complex Punto Banco third card rules. func shouldBankerDraw() -> Bool { // Check for naturals first if playerHand.isNatural || bankerHand.isNatural { return false } let bankerValue = bankerHand.value // If Player didn't draw (stood on 6-7), Banker uses simple rules if playerHand.cardCount == 2 { return bankerValue <= 5 } // Player drew a third card - apply complex Banker rules guard let playerThirdCard = playerHand.thirdCard else { return bankerValue <= 5 } let p3Value = playerThirdCard.baccaratValue // Banker third card rules based on Banker's total and Player's third card switch bankerValue { case 0, 1, 2: // Banker always draws on 0-2 return true case 3: // Banker draws unless Player's third card was 8 return p3Value != 8 case 4: // Banker draws if Player's third card was 2-7 return (2...7).contains(p3Value) case 5: // Banker draws if Player's third card was 4-7 return (4...7).contains(p3Value) case 6: // Banker draws if Player's third card was 6-7 return (6...7).contains(p3Value) case 7: // Banker stands on 7 return false default: // 8-9 are naturals, shouldn't reach here return false } } /// Draws a third card for the Banker if rules allow. /// - Returns: The drawn card, or nil if Banker stands. mutating func drawBankerThirdCard() -> Card? { guard shouldBankerDraw() else { return nil } if let card = shoe.deal() { bankerHand.addCard(card) return card } return nil } /// Determines the winner of the current round. func determineResult() -> GameResult { let playerValue = playerHand.value let bankerValue = bankerHand.value if playerValue > bankerValue { return .playerWins } else if bankerValue > playerValue { return .bankerWins } else { return .tie } } /// Calculates the payout for a bet given the result. /// - Parameters: /// - bet: The bet that was placed. /// - result: The result of the round. /// - Returns: The net winnings (positive), net loss (negative), or 0 for push. func calculatePayout(bet: Bet, result: GameResult) -> Int { if result.isPush(for: bet.type) { // Push - bet is returned return 0 } if result.isWinningBet(bet.type) { // Win - return winnings based on payout multiplier return Int(Double(bet.amount) * bet.type.payoutMultiplier) } else { // Loss - lose the bet amount return -bet.amount } } /// Plays a complete round automatically and returns the result. /// Used for simulation/testing purposes. mutating func playRound() -> GameResult { prepareNewRound() _ = dealInitialCards() _ = drawPlayerThirdCard() _ = drawBankerThirdCard() return determineResult() } }