rustjack/backend/src/blackjack/blackjack_game.rs
2024-05-24 17:43:35 +02:00

309 lines
9.5 KiB
Rust

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<Player>,
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<Player>{
&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
}
}