use crate::cards::{card::Card, decks::new_blackjack_shoe, hand::Hand}; use super::{gamestate::GameState, play_moves::PlayMoves, player::Player, playing_hand::{HandState, PlayingHand}}; pub struct BlackjackGame { shoe: Hand, players: Vec, dealer_hand: Hand, state: GameState, } impl BlackjackGame { pub fn new() -> Self { BlackjackGame { shoe: new_blackjack_shoe(6), players: Vec::new(), dealer_hand: Hand::new(), state: GameState::Starting, } } pub fn get_dealer_hand(&self) -> &Hand { &self.dealer_hand } pub fn get_dealer_upcard(&self) -> Option<&Card> { self.dealer_hand.get_card(0) } pub fn get_state(&self) -> &GameState { &self.state } pub fn get_players(&self) -> &Vec{ &self.players } pub fn get_player(&self, index: usize) -> Option<&Player> { self.players.get(index) } pub fn get_player_count(&self) -> usize { self.players.len() } pub fn play(&mut self, action: PlayMoves) -> bool { match (&self.state, action) { ( GameState::PlayerTurn { player_index, hand_index, }, PlayMoves::Hit, ) => { let Some(player) = self.players.get_mut(*player_index) else { // Player does not exists return false; }; let Some(hand) = player.hands.get_mut(*hand_index) else { // Hand does not exist return false; }; if hand.state != HandState::Playing { return false; } hand.hand.add_card(self.shoe.pop_card().unwrap()); match hand.hand.get_blackjack_value().cmp(&21) { std::cmp::Ordering::Equal => { hand.state = HandState::Maxed; } std::cmp::Ordering::Greater => { hand.state = HandState::Busted; } std::cmp::Ordering::Less => { // Player is still playing } }; } ( GameState::PlayerTurn { player_index, hand_index, }, PlayMoves::Split, ) => { let Some(player) = self.players.get_mut(*player_index) else { // Player does not exists return false; }; let Some(hand) = player.hands.get_mut(*hand_index) else { // Hand does not exist return false; }; if hand.state != HandState::Playing { // Hand is not playing return false; } if hand.hand.count() != 2 { // Can only split with 2 cards return false; } if !hand.hand.is_valid_for_bj_split() { return false; } // Split the hands let mut new_hand = PlayingHand::new(); // Add card from the current hand and a card from the shoe new_hand.hand.add_card(hand.hand.pop_card().unwrap()); new_hand.hand.add_card(self.shoe.pop_card().unwrap()); // Add card to current hand hand.hand.add_card(self.shoe.pop_card().unwrap()); player.hands.push(new_hand); } ( GameState::PlayerTurn { player_index, hand_index, }, PlayMoves::DoubleDown, ) => { let Some(player) = self.players.get_mut(*player_index) else { // Player does not exists return false; }; let Some(hand) = player.hands.get_mut(*hand_index) else { // Hand does not exist return false; }; if hand.state != HandState::Playing { return false; } if hand.hand.count() != 2 { // Can oly double down as your first move return false; } hand.hand.add_card(self.shoe.pop_card().unwrap()); match hand.hand.get_blackjack_value().cmp(&21) { std::cmp::Ordering::Equal => { hand.state = HandState::Maxed; } std::cmp::Ordering::Less => { hand.state = HandState::Standing; } std::cmp::Ordering::Greater => { hand.state = HandState::Busted; } }; } ( GameState::PlayerTurn { player_index, hand_index, }, PlayMoves::Stand, ) => { let Some(player) = self.players.get_mut(*player_index) else { // Player does not exists return false; }; let Some(hand) = player.hands.get_mut(*hand_index) else { // Hand does not exist return false; }; if hand.state != HandState::Playing { // Can only stand on playing hands return false; } hand.state = HandState::Standing; } (GameState::Over, PlayMoves::Deal { players }) | (GameState::Starting, PlayMoves::Deal { players }) => { // Create players for _ in 0..players { self.players.push(Player::new()); } // Create one hand for the players for player in self.players.iter_mut() { player.hands.push(PlayingHand::new()); } // Add one card to each hand the players have for player in self.players.iter_mut() { for hand in player.hands.iter_mut() { hand.hand.add_card(self.shoe.pop_card().unwrap()); } } // Add one card to the dealer self.dealer_hand.add_card(self.shoe.pop_card().unwrap()); // Add the 2nd card to the players hand for player in self.players.iter_mut() { for hand in player.hands.iter_mut() { hand.hand.add_card(self.shoe.pop_card().unwrap()); if hand.hand.is_backjack() { hand.state = HandState::Blackjack; } } } // Add 2nd card to the dealer self.dealer_hand.add_card(self.shoe.pop_card().unwrap()); self.state = GameState::PlayerTurn { player_index: 0, hand_index: 0, }; } (_, PlayMoves::Deal { .. }) | (GameState::Over, _) | (GameState::Starting, _) => { return false; } } // Find next player or dealer turn if let Some(next_turn) = self.next_player_and_hand() { self.state = GameState::PlayerTurn { player_index: next_turn.0, hand_index: next_turn.1, }; } else { self.dealer_turn(); } true } fn dealer_turn(&mut self) { loop { let dealer_value = self.dealer_hand.get_blackjack_value(); if dealer_value >= 17 { // Dealer stands self.state = GameState::Over; return; } self.dealer_hand.add_card(self.shoe.pop_card().unwrap()); if self.dealer_hand.get_blackjack_value() > 21 { // Dealer busts self.state = GameState::Over; return; } } } fn next_player_and_hand(&self) -> Option<(usize, usize)> { if let GameState::PlayerTurn { player_index, hand_index, } = self.state { let Some(player) = self.players.get(player_index) else { // Player does not exists return None; }; let Some(hand) = player.hands.get(hand_index) else { // Hand does not exist return None; }; if hand.state == HandState::Playing { // Hand is still playing return Some((player_index, hand_index)); } // Get next valid hand for player if let Some(next_hand) = player.next_playing_hand() { return Some((player_index, next_hand)); } // Get next valid player if let Some(next_hand) = self .players .iter() .enumerate() .find_map(|p| p.1.next_playing_hand().map(|h| (p.0, h))) { return Some((next_hand.0, next_hand.1)); } } None } }