1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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<String> {
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<String>,
pub dealer: Vec<String>,
pub player_total: u8,
pub dealer_total: u8,
pub bet: u64,
pub base_bet: u64,
pub balance: i64,
pub actions: Vec<String>,
pub result: String,
pub winnings: u64,
}
|