use crate::cards;
pub enum PlayerAction {
Hit,
Stand,
DoubleDown,
Split,
Bet(i32),
None,
}
pub enum DealerRequest {
Bet,
Play(usize),
UpCard([char; 2]),
HitCard([char; 2]),
DealerHand(Vec<[char; 2]>),
LowCards,
Error(PlayerActionError),
}
pub enum PlayerActionError {
NotEnoughMoney(usize, PlayerAction),
UnexpectedAction(usize, PlayerAction),
}
pub struct GameConfig {
pub stand_soft_17: bool,
pub blackjack_payout: f32,
pub splitting: bool,
pub doubling_down: bool,
pub double_after_split: bool,
pub min_bet: i32,
pub max_bet: i32,
pub shoe_deck_count: u8,
pub low_cards_threshold: usize,
}
pub const DEFAULT_CONFIG: GameConfig = GameConfig {
stand_soft_17: true,
blackjack_payout: 1.5,
splitting: true,
doubling_down: true,
double_after_split: true,
min_bet: 1,
max_bet: i32::MAX,
shoe_deck_count: 6,
low_cards_threshold: 52,
};
pub struct Dealer<'a> {
hand: Vec<[char; 2]>,
shoe: Vec<[char; 2]>,
players: Vec<Player>,
config: GameConfig,
callback: &'a dyn Fn(DealerRequest, Option<&Player>, &Dealer) -> PlayerAction,
}
pub struct Player {
money: i32,
hands: Vec<Vec<[char; 2]>>,
}
impl Dealer<'_> {
pub fn new<'a>(
shoe: Vec<[char; 2]>,
game_config: GameConfig,
callback: &'a dyn Fn(DealerRequest, Option<&Player>, &Dealer) -> PlayerAction,
) -> Dealer {
Dealer {
hand: Vec::new(),
shoe: shoe,
players: Vec::new(),
config: game_config,
callback: callback,
}
}
pub fn hand(&self) -> &Vec<[char; 2]> {
&self.hand
}
pub fn shoe(&self) -> &Vec<[char; 2]> {
&self.shoe
}
pub fn players(&self) -> &Vec<Player> {
&self.players
}
pub fn hand_mut(&mut self) -> &mut Vec<[char; 2]> {
&mut self.hand
}
pub fn shoe_mut(&mut self) -> &mut Vec<[char; 2]> {
&mut self.shoe
}
pub fn players_mut(&mut self) -> &mut Vec<Player> {
&mut self.players
}
pub fn clear_table(&mut self) {
self.hand.clear();
for player in self.players.iter_mut() {
player.hands_mut().clear();
player.hands_mut().push(Vec::new());
}
}
pub fn deal_hands(&mut self) {
for _ in 0..2 {
cards::hit_card(&mut self.shoe, &mut self.hand);
for player in self.players.iter_mut() {
cards::hit_card(&mut self.shoe, &mut player.hands_mut()[0]);
}
}
}
pub fn hit_card(&mut self, player: usize, hand: usize) {
cards::hit_card(&mut self.shoe, &mut self.players[player].hands[hand]);
}
pub fn play_round(&mut self, clear_table: bool) {
if clear_table {
self.clear_table();
}
let mut player_bets: Vec<i32> = Vec::new();
for i in 0..self.players.len() {
loop {
let bet = (self.callback)(DealerRequest::Bet, Some(&self.players[i]), &self);
if let PlayerAction::Bet(amount) = bet {
if self.players[i].money() >= &amount {
if self.config.min_bet <= amount && amount <= self.config.max_bet {
player_bets.push(amount);
*self.players[i].money_mut() -= amount;
break;
} else {
let error = PlayerActionError::UnexpectedAction(0, bet);
(self.callback)(
DealerRequest::Error(error),
Some(&self.players[i]),
&self,
);
}
} else {
let error = PlayerActionError::NotEnoughMoney(0, bet);
(self.callback)(DealerRequest::Error(error), Some(&self.players[i]), &self);
}
} else {
let error = PlayerActionError::UnexpectedAction(0, bet);
(self.callback)(DealerRequest::Error(error), Some(&self.players[i]), &self);
}
}
}
self.deal_hands();
(self.callback)(DealerRequest::UpCard(self.hand[1]), None, &self);
for i in 0..self.players.len() {
let mut can_double: Vec<bool>;
let mut can_split: bool;
if self.config.doubling_down {
can_double = vec![self.players[i].money() >= &player_bets[i]];
} else {
can_double = vec![false];
}
if self.config.splitting {
can_split = crate::game::can_split(&self.players[i].hands()[0]) && can_double[0];
} else {
can_split = false;
}
let mut stood = vec![false];
let original_bet = player_bets[i];
let mut hand_count = 1;
let mut j = 0;
loop {
if self.shoe.len() <= self.config.low_cards_threshold {
(self.callback)(DealerRequest::LowCards, None, &self);
self.shoe = cards::create_shoe(self.config.shoe_deck_count);
cards::shuffle_deck(&mut self.shoe);
}
if j >= hand_count {
break;
}
while !stood[j] {
let action =
(self.callback)(DealerRequest::Play(j), Some(&self.players[i]), &self);
match action {
PlayerAction::Hit => {
self.hit_card(i, j);
can_double[j] = false;
}
PlayerAction::Stand => stood[j] = true,
PlayerAction::DoubleDown => {
if can_double[j] {
*self.players[i].money_mut() -= original_bet;
player_bets[i] += original_bet;
stood[j] = true;
self.hit_card(i, j);
can_double[j] = false;
} else {
(self.callback)(
DealerRequest::Error(PlayerActionError::UnexpectedAction(
j, action,
)),
Some(&self.players[i]),
&self,
);
}
}
PlayerAction::Split => {
if can_split {
*self.players[i].money_mut() -= original_bet;
player_bets[i] += original_bet;
self.players[i].hands_mut().push(Vec::new());
stood.push(false);
if self.config.double_after_split && self.config.doubling_down {
can_double.push(true);
} else {
can_double[0] = false;
can_double.push(false);
}
let card = cards::draw_card(
self.players[i].hands_mut().get_mut(0).unwrap(),
);
self.players[i].hands_mut()[1].push(card.unwrap());
self.hit_card(i, 0);
self.hit_card(i, 1);
hand_count = 2;
can_split = false;
} else {
(self.callback)(
DealerRequest::Error(PlayerActionError::UnexpectedAction(
j, action,
)),
Some(&self.players[i]),
&self,
);
}
}
_ => {
let error = PlayerActionError::UnexpectedAction(j, action);
(self.callback)(
DealerRequest::Error(error),
Some(&self.players[i]),
&self,
);
}
}
if get_hand_value(&self.players[i].hands()[j], true) > 21 {
stood[j] = true;
}
}
j += 1;
}
}
let mut busted = false;
loop {
let hand_value = get_hand_value(&self.hand, true);
if hand_value > 21 {
busted = true;
break;
} else if hand_value >= 17 {
if self.config.stand_soft_17 {
break;
} else if hand_value == 17 && self.hand.iter().any(|&i| i[1] == 'A') {
if hand_value == get_hand_value(&self.hand, false) {
let card = cards::draw_card(&mut self.shoe).unwrap();
self.hand.push(card);
(self.callback)(DealerRequest::HitCard(card), None, &self);
} else {
break;
}
}
break;
} else {
let card = cards::draw_card(&mut self.shoe).unwrap();
self.hand.push(card);
(self.callback)(DealerRequest::HitCard(card), None, &self);
}
}
let dealer_hand_value = get_hand_value(&self.hand, true);
for i in 0..self.players.len() {
for j in 0..self.players[i].hands().len() {
let hand_value = get_hand_value(&self.players[i].hands()[j], true);
if hand_value > 21 {
continue;
}
if hand_value < 21 && (busted || hand_value > dealer_hand_value) {
self.players[i].money +=
player_bets[i] * 2 / self.players[i].hands().len() as i32;
} else if hand_value == 21 && (busted || hand_value > dealer_hand_value) {
if self.players[i].hands()[j].len() == 2 {
if dealer_hand_value == 21 && self.hand.len() == 2 {
self.players[i].money += player_bets[i];
} else {
self.players[i].money += player_bets[i]
+ (player_bets[i] as f32 * self.config.blackjack_payout) as i32;
}
} else {
self.players[i].money +=
player_bets[i] * 2 / self.players[i].hands().len() as i32;
}
} else if hand_value == dealer_hand_value {
self.players[i].money += player_bets[i];
}
}
}
(self.callback)(DealerRequest::DealerHand(self.hand.clone()), None, &self);
}
}
impl Player {
pub fn new(money: i32) -> Player {
Player {
money: money,
hands: vec![Vec::new()],
}
}
pub fn money(&self) -> &i32 {
&self.money
}
pub fn hands(&self) -> &Vec<Vec<[char; 2]>> {
&self.hands
}
pub fn money_mut(&mut self) -> &mut i32 {
&mut self.money
}
pub fn hands_mut(&mut self) -> &mut Vec<Vec<[char; 2]>> {
&mut self.hands
}
}
pub fn get_hand_value(hand: &Vec<[char; 2]>, auto_aces: bool) -> u8 {
let mut value = 0;
let mut aces = 0;
for i in hand.iter() {
value += match i[1] {
'2' => 2,
'3' => 3,
'4' => 4,
'5' => 5,
'6' => 6,
'7' => 7,
'8' => 8,
'9' => 9,
'T' | 'J' | 'Q' | 'K' => 10,
'A' => {
aces += 1;
0
}
_ => 0,
}
}
if auto_aces {
for _ in 0..aces {
if value + 11 > 21 {
value += 1;
} else {
value += 11;
}
}
} else {
value += 11 * aces;
}
value
}
pub fn can_split(hand: &Vec<[char; 2]>) -> bool {
if hand.len() != 2 {
return false;
}
hand[0][1] == hand[1][1]
}