Compare commits

...

3 Commits

Author SHA1 Message Date
d34a72cf3b moved split check to hand 2024-05-22 14:07:04 +02:00
6c21eb960f added split 2024-05-21 16:48:42 +02:00
338b5d442a that's all folks 2024-05-21 14:04:44 +02:00
3 changed files with 243 additions and 72 deletions

View File

@@ -8,15 +8,35 @@ pub struct BlackjackGame {
} }
pub struct Player { pub struct Player {
hand: Hand, hands: Vec<PlayingHand>,
state: PlayerState,
} }
impl Player { impl Player {
fn new() -> Self { fn new() -> Self {
Player { Player { hands: Vec::new() }
}
pub fn get_hands(&self) -> &Vec<PlayingHand> {
&self.hands
}
fn next_playing_hand(&self) -> Option<usize> {
self.hands
.iter()
.position(|e| e.state == HandState::Playing)
}
}
pub struct PlayingHand {
hand: Hand,
state: HandState,
}
impl PlayingHand {
fn new() -> Self {
PlayingHand {
hand: Hand::new(), hand: Hand::new(),
state: PlayerState::Playing, state: HandState::Playing,
} }
} }
@@ -24,13 +44,13 @@ impl Player {
&self.hand &self.hand
} }
pub fn get_state(&self) -> &PlayerState { pub fn get_state(&self) -> HandState {
&self.state self.state
} }
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum PlayerState { pub enum HandState {
Playing, Playing,
Standing, Standing,
DoubleDown, DoubleDown,
@@ -44,13 +64,14 @@ pub enum PlayMoves {
Hit, Hit,
Stand, Stand,
DoubleDown, DoubleDown,
Split,
Deal(usize), Deal(usize),
} }
pub enum GameState { pub enum GameState {
Starting, Starting,
Over, // Game is over Over, // Game is over
PlayerTurn(usize), // Its the turn of the player PlayerTurn(usize, usize), // Its the turn of the player
} }
impl BlackjackGame { impl BlackjackGame {
@@ -81,115 +102,230 @@ impl BlackjackGame {
pub fn play(&mut self, action: PlayMoves) -> bool { pub fn play(&mut self, action: PlayMoves) -> bool {
match (&self.state, action) { match (&self.state, action) {
(GameState::PlayerTurn(i), PlayMoves::Hit) => { (GameState::PlayerTurn(player_index, hand_index), PlayMoves::Hit) => {
let Some(player) = self.players.get_mut(*i) else { let Some(player) = self.players.get_mut(*player_index) else {
// Player does not exists
return false; return false;
}; };
if player.state != PlayerState::Playing { let Some(hand) = player.hands.get_mut(*hand_index) else {
// Hand does not exist
return false;
};
if hand.state != HandState::Playing {
return false; return false;
} }
player.hand.add_card(self.shoe.pop_card().unwrap()); hand.hand.add_card(self.shoe.pop_card().unwrap());
match player.hand.get_blackjack_value().cmp(&21) {
match hand.hand.get_blackjack_value().cmp(&21) {
std::cmp::Ordering::Equal => { std::cmp::Ordering::Equal => {
player.state = PlayerState::Maxed; hand.state = HandState::Maxed;
} }
std::cmp::Ordering::Greater => { std::cmp::Ordering::Greater => {
player.state = PlayerState::Busted; hand.state = HandState::Busted;
} }
std::cmp::Ordering::Less => { std::cmp::Ordering::Less => {
// Player is still playing // Player is still playing
} }
}; };
} }
(GameState::PlayerTurn(i), PlayMoves::DoubleDown) => { (GameState::PlayerTurn(player_index, hand_index), PlayMoves::Split) => {
let Some(player) = self.players.get_mut(*i) else { let Some(player) = self.players.get_mut(*player_index) else {
// Player does not exists
return false; return false;
}; };
if player.state != PlayerState::Playing { 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; return false;
} }
player.hand.add_card(self.shoe.pop_card().unwrap()); if hand.hand.count() != 2 {
match player.hand.get_blackjack_value().cmp(&21) { // Can only split with 2 cards
std::cmp::Ordering::Equal | std::cmp::Ordering::Less => { return false;
player.state = PlayerState::Maxed; }
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 => { std::cmp::Ordering::Greater => {
player.state = PlayerState::Busted; hand.state = HandState::Busted;
} }
}; };
} }
(GameState::PlayerTurn(i), PlayMoves::Stand) => { (GameState::PlayerTurn(player_index, hand_index), PlayMoves::Stand) => {
let Some(player) = self.players.get_mut(*i) else { let Some(player) = self.players.get_mut(*player_index) else {
// Player does not exists
return false; return false;
}; };
if player.state != PlayerState::Playing { 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; return false;
} }
player.state = PlayerState::Standing; hand.state = HandState::Standing;
} }
(GameState::Over, PlayMoves::Deal(player_count)) (GameState::Over, PlayMoves::Deal(player_count))
| (GameState::Starting, PlayMoves::Deal(player_count)) => { | (GameState::Starting, PlayMoves::Deal(player_count)) => {
for i in 0..player_count { // Create players
for _ in 0..player_count {
self.players.push(Player::new()); self.players.push(Player::new());
self.players[i].hand.add_card(self.shoe.pop_card().unwrap());
} }
self.dealer_hand.add_card(self.shoe.pop_card().unwrap()); // Create one hand for the players
for player in self.players.iter_mut() {
player.hands.push(PlayingHand::new());
}
for i in 0..player_count { // Add one card to each hand the players have
self.players[i].hand.add_card(self.shoe.pop_card().unwrap()); for player in self.players.iter_mut() {
for hand in player.hands.iter_mut() {
if self.players[i].hand.is_backjack() { hand.hand.add_card(self.shoe.pop_card().unwrap());
self.players[i].state = PlayerState::Blackjack;
} }
} }
// Add one card to the dealer
self.dealer_hand.add_card(self.shoe.pop_card().unwrap()); self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
self.state = GameState::PlayerTurn(0); // 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(0, 0);
} }
(_, PlayMoves::Deal(_)) | (GameState::Over, _) | (GameState::Starting, _) => { (_, PlayMoves::Deal(_)) | (GameState::Over, _) | (GameState::Starting, _) => {
return false; return false;
} }
} }
let player_count = self.players.len(); // Find next player or dealer turn
if let Some(next_turn) = self.next_player_and_hand() {
self.state = GameState::PlayerTurn(next_turn.0, next_turn.1);
} else {
self.dealer_turn();
}
match self.state { true
GameState::PlayerTurn(i) => { }
if self.players[i].state == PlayerState::Playing {
self.state = GameState::PlayerTurn(i);
} else if i + 1 >= player_count {
loop {
let dealer_value = self.dealer_hand.get_blackjack_value();
if dealer_value >= 17 {
// Check winners
self.state = GameState::Over;
return true;
}
self.dealer_hand.add_card(self.shoe.pop_card().unwrap()); fn dealer_turn(&mut self) {
loop {
if self.dealer_hand.get_blackjack_value() > 21 { let dealer_value = self.dealer_hand.get_blackjack_value();
// Dealer busts if dealer_value >= 17 {
self.state = GameState::Over; // Dealer stands
return true; self.state = GameState::Over;
} return;
}
} else {
self.state = GameState::PlayerTurn(i + 1);
}
return true;
} }
_ => {
return false; 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
}
} }

View File

@@ -7,13 +7,13 @@ pub fn play() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
match game.get_state() { match game.get_state() {
GameState::PlayerTurn(player_index) => { GameState::PlayerTurn(player_index, _) => {
print_full_state(&game); print_full_state(&game);
let play_move = get_move(*player_index)?; let play_move = get_move(*player_index)?;
if !game.play(play_move){ if !game.play(play_move) {
println!("You can't do that"); println!("You can't do that");
} }
}, }
GameState::Over => { GameState::Over => {
print_full_state(&game); print_full_state(&game);
println!("Game over"); println!("Game over");
@@ -21,14 +21,14 @@ pub fn play() -> Result<(), Box<dyn std::error::Error>> {
} }
GameState::Starting => { GameState::Starting => {
game.play(PlayMoves::Deal(2)); game.play(PlayMoves::Deal(2));
}, }
} }
} }
} }
fn get_move(player_index: usize) -> Result<PlayMoves, io::Error> { fn get_move(player_index: usize) -> Result<PlayMoves, io::Error> {
loop { loop {
println!("P{}, (H)it (S)tand (D)double",player_index); println!("Turn {}: (H)it (S)tand (D)double, S(p)lit", player_index);
let mut buffer = String::new(); let mut buffer = String::new();
stdin().read_line(&mut buffer)?; stdin().read_line(&mut buffer)?;
@@ -37,16 +37,30 @@ fn get_move(player_index: usize) -> Result<PlayMoves, io::Error> {
"h" | "H" => return Ok(PlayMoves::Hit), "h" | "H" => return Ok(PlayMoves::Hit),
"s" | "S" => return Ok(PlayMoves::Stand), "s" | "S" => return Ok(PlayMoves::Stand),
"d" | "D" => return Ok(PlayMoves::DoubleDown), "d" | "D" => return Ok(PlayMoves::DoubleDown),
"p" | "P" => return Ok(PlayMoves::Split),
_ => {} _ => {}
} }
} }
} }
fn print_full_state(game: &BlackjackGame) { fn print_full_state(game: &BlackjackGame) {
println!("Dealer: {} ({})", game.get_dealer_hand(), game.get_dealer_hand().get_blackjack_value()); println!(
"Dealer: {} ({})",
game.get_dealer_hand(),
game.get_dealer_hand().get_blackjack_value()
);
for player_index in 0..game.get_player_count(){ for player_index in 0..game.get_player_count() {
let player = game.get_player(player_index).unwrap(); let player = game.get_player(player_index).unwrap();
println!("P{}: {} ({})",player_index,player.get_hand(),player.get_hand().get_blackjack_value()); println!("Player {}:", player_index);
for hand in player.get_hands().iter().enumerate() {
println!(
"{}: {} ({}) - {:?}",
hand.0,
hand.1.get_hand(),
hand.1.get_hand().get_blackjack_value(),
hand.1.get_state()
);
}
} }
} }

View File

@@ -40,6 +40,27 @@ impl Hand {
self.cards.len() == 2 && self.get_blackjack_value() == 21 self.cards.len() == 2 && self.get_blackjack_value() == 21
} }
/**
* Returns true if this hand could be split in a blackjack game.
*/
pub fn is_valid_for_bj_split(&self) -> bool {
if self.cards.len() != 2 {
return false;
}
if let Some(card_0) = self.get_card(0) {
if let Some(card_1) = self.get_card(1) {
if card_0.index.get_blackjack_value(true) != card_1.index.get_blackjack_value(true)
{
// Cards are not the same value
return false;
}
}
}
true
}
/** /**
* Put another hand into this. * Put another hand into this.
* Leaves the other hand empty. * Leaves the other hand empty.