// ── Blackjack game logic (pure functions, no ECS dependency) ── export const SUITS = ["♠", "♥", "♦", "♣"] as const; export const RANKS = [ "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", ] as const; export interface CardData { rank: string; suit: string; } // ── Hand evaluation ────────────────────────────────── /** Numeric value of a single card rank. */ export function rankValue(rank: string): number { if (rank === "A") return 11; if (rank === "K" || rank === "Q" || rank === "J") return 10; return parseInt(rank, 10); } /** Best total for a hand (aces count as 1 or 11). */ export function handValue(cards: CardData[]): number { let total = 0; let aces = 0; for (const c of cards) { total += rankValue(c.rank); if (c.rank === "A") aces++; } while (total > 21 && aces > 0) { total -= 10; aces--; } return total; } export function isBust(cards: CardData[]): boolean { return handValue(cards) > 21; } export function isBlackjack(cards: CardData[]): boolean { return cards.length === 2 && handValue(cards) === 21; } export function isSoft(cards: CardData[]): boolean { let total = 0; let aces = 0; for (const c of cards) { total += rankValue(c.rank); if (c.rank === "A") aces++; } return aces > 0 && total <= 21; } // ── Dealer logic ───────────────────────────────────── /** Dealer must hit on soft 17 in this variant. */ export function dealerShouldHit(cards: CardData[]): boolean { const val = handValue(cards); if (val < 17) return true; if (val === 17 && isSoft(cards)) return true; return false; } // ── Outcome ────────────────────────────────────────── export type Outcome = "win" | "lose" | "push" | "blackjack"; export function determineOutcome( playerCards: CardData[], dealerCards: CardData[], ): Outcome { if (isBust(playerCards)) return "lose"; if (isBust(dealerCards)) return "win"; if (isBlackjack(playerCards) && !isBlackjack(dealerCards)) return "blackjack"; const pv = handValue(playerCards); const dv = handValue(dealerCards); if (pv > dv) return "win"; if (pv < dv) return "lose"; return "push"; } /** Payout multiplier. Blackjack pays 3:2, win pays 1:1. */ export function payout(outcome: Outcome, bet: number): number { switch (outcome) { case "blackjack": return Math.floor(bet * 1.5); case "win": return bet; case "push": return 0; case "lose": return -bet; } }