309 lines
9.5 KiB
Rust
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
|
|
}
|
|
}
|