From b5a52fc0312afdf5d5fbe53275dc88d6d881c2ff Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 29 Oct 2026 18:22:40 +0100 Subject: v2.2.0: add blackjack with reserved bets and hit/stand/double --- src/blackjack.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/blackjack.rs (limited to 'src/blackjack.rs') diff --git a/src/blackjack.rs b/src/blackjack.rs new file mode 100644 index 0000000..ac0060a --- /dev/null +++ b/src/blackjack.rs @@ -0,0 +1,90 @@ +//! Shared blackjack rules and wire state. + +use serde::{Deserialize, Serialize}; + +pub const DECKS: usize = 6; +pub const SUITS: [char; 4] = ['C', 'D', 'H', 'S']; +pub const RANKS: [&str; 13] = [ + "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", +]; + +/// A fresh, unshuffled six-deck shoe. +pub fn fresh_shoe() -> Vec { + let mut shoe = Vec::with_capacity(DECKS * 52); + for _ in 0..DECKS { + for suit in SUITS { + for rank in RANKS { + shoe.push(format!("{rank}{suit}")); + } + } + } + shoe +} + +pub fn card_value(card: &str) -> u8 { + let rank = &card[..card.len().saturating_sub(1)]; + match rank { + "A" => 11, + "10" | "J" | "Q" | "K" => 10, + _ => rank.parse().unwrap_or(0), + } +} + +/// Best total: aces count as 11 until that would bust the hand. +pub fn hand_total(cards: &[String]) -> u8 { + let mut total = 0u16; + let mut aces = 0u16; + for card in cards { + if card_value(card) == 11 { + aces += 1; + total += 1; + } else { + total += card_value(card) as u16; + } + } + for _ in 0..aces { + if total + 10 <= 21 { + total += 10; + } + } + total.min(255) as u8 +} + +pub fn is_blackjack(cards: &[String]) -> bool { + cards.len() == 2 && hand_total(cards) == 21 +} + +pub fn is_red_suit(card: &str) -> bool { + matches!(card.chars().last(), Some('D') | Some('H')) +} + +pub fn card_label(card: &str) -> String { + let Some(suit) = card.chars().last() else { + return card.to_string(); + }; + let rank = &card[..card.len() - 1]; + let suit = match suit { + 'C' => '♣', + 'D' => '♦', + 'H' => '♥', + 'S' => '♠', + other => other, + }; + format!("{rank}{suit}") +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BlackjackState { + pub session_id: String, + pub phase: String, + pub player: Vec, + pub dealer: Vec, + pub player_total: u8, + pub dealer_total: u8, + pub bet: u64, + pub base_bet: u64, + pub balance: i64, + pub actions: Vec, + pub result: String, + pub winnings: u64, +} -- cgit v1.2.3