more improvements

This commit is contained in:
Niklas Kapelle 2024-05-15 13:54:41 +02:00
parent 4387e2cb14
commit bf73860433
Signed by: niklas
GPG Key ID: 4EB651B36D841D16
2 changed files with 169 additions and 166 deletions

View File

@ -2,22 +2,41 @@ use crate::{decks::new_blackjack_shoe, hand::Hand};
pub struct BlackjackGame {
shoe: Hand,
player_hand: Hand,
players: Vec<Player>,
dealer_hand: Hand,
state: GameState,
}
pub enum PlayResult {
DealerBlackJack, // Dealer has a blackjack. Player does not
PlayerBlackJack, // Player has a blackjack. Dealer does not
PushBlackjack, // Player and dealer have a blackjack
Push, // Dealer has a Blackjack and so does the Player
Bust, // Player bust
DealerBust, // Dealer bust
StandPlayerLose, // Dealer stands and wins
StandPlayerWin, // Dealer stands and loses
Continue, // Game goes on as normal
InvalidMove,
pub struct Player {
hand: Hand,
state: PlayerState,
}
impl Player {
fn new() -> Self {
Player {
hand: Hand::new(),
state: PlayerState::Playing,
}
}
pub fn get_hand(&self) -> &Hand {
&self.hand
}
pub fn get_state(&self) -> &PlayerState {
&self.state
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PlayerState {
Playing,
Standing,
DoubleDown,
Busted,
Blackjack,
Maxed, // Reached 21
}
#[derive(Clone, Copy, Debug)]
@ -25,22 +44,22 @@ pub enum PlayMoves {
Hit,
Stand,
DoubleDown,
Bet, // At the Start of the game
Deal(usize),
}
pub enum GameState {
Empty, // No cards have been dealt
Starting,
Over, // Game is over
PlayerTurn, // Its the turn of the player
PlayerTurn(usize), // Its the turn of the player
}
impl BlackjackGame {
pub fn new() -> Self {
BlackjackGame {
shoe: new_blackjack_shoe(6),
player_hand: Hand::new(),
players: Vec::new(),
dealer_hand: Hand::new(),
state: GameState::Empty,
state: GameState::Starting,
}
}
@ -48,98 +67,128 @@ impl BlackjackGame {
&self.dealer_hand
}
pub fn get_player_hand(&self) -> &Hand {
&self.player_hand
}
pub fn get_state(&self) -> &GameState {
&self.state
}
pub fn play(&mut self, action: PlayMoves) -> PlayResult {
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::Empty, PlayMoves::Bet) => {
self.player_hand.add_card(self.shoe.pop_card().unwrap());
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
(GameState::PlayerTurn(i), PlayMoves::Hit) => {
let Some(player) = self.players.get_mut(*i) else {
return false;
};
self.player_hand.add_card(self.shoe.pop_card().unwrap());
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
match (self.dealer_hand.is_backjack(),self.player_hand.is_backjack()) {
(true,true) => {
self.state = GameState::Over;
PlayResult::PushBlackjack
},
(true,false) => {
self.state = GameState::Over;
PlayResult::DealerBlackJack
},
(false,true) => {
self.state = GameState::Over;
PlayResult::PlayerBlackJack
},
(false,false) => {
self.state = GameState::PlayerTurn;
PlayResult::Continue
},
if player.state != PlayerState::Playing {
return false;
}
}
(GameState::PlayerTurn, PlayMoves::Hit) => {
self.player_hand.add_card(self.shoe.pop_card().unwrap());
return match self.player_hand.get_blackjack_value().cmp(&21) {
player.hand.add_card(self.shoe.pop_card().unwrap());
match player.hand.get_blackjack_value().cmp(&21) {
std::cmp::Ordering::Equal => {
self.state = GameState::Over;
self.dealer_play()
player.state = PlayerState::Maxed;
}
std::cmp::Ordering::Greater => {
self.state = GameState::Over;
PlayResult::Bust
player.state = PlayerState::Busted;
}
std::cmp::Ordering::Less => {
// Player is still playing
}
std::cmp::Ordering::Less => PlayResult::Continue,
};
},
(GameState::PlayerTurn, PlayMoves::DoubleDown) => {
if self.player_hand.count() > 2{
return PlayResult::InvalidMove;
}
(GameState::PlayerTurn(i), PlayMoves::DoubleDown) => {
let Some(player) = self.players.get_mut(*i) else {
return false;
};
if player.state != PlayerState::Playing {
return false;
}
self.player_hand.add_card(self.shoe.pop_card().unwrap());
self.state = GameState::Over;
return self.dealer_play();
},
(GameState::PlayerTurn, PlayMoves::Stand) => {
self.state = GameState::Over;
return self.dealer_play();
player.hand.add_card(self.shoe.pop_card().unwrap());
match player.hand.get_blackjack_value().cmp(&21) {
std::cmp::Ordering::Equal | std::cmp::Ordering::Less => {
player.state = PlayerState::Maxed;
}
(GameState::Over, _)
| (GameState::Empty, _)
| (GameState::PlayerTurn, PlayMoves::Bet) => {
return PlayResult::InvalidMove;
std::cmp::Ordering::Greater => {
player.state = PlayerState::Busted;
}
};
}
(GameState::PlayerTurn(i), PlayMoves::Stand) => {
let Some(player) = self.players.get_mut(*i) else {
return false;
};
if player.state != PlayerState::Playing {
return false;
}
player.state = PlayerState::Standing;
}
(GameState::Over, PlayMoves::Deal(player_count))
| (GameState::Starting, PlayMoves::Deal(player_count)) => {
for i in 0..player_count {
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());
for i in 0..player_count {
self.players[i].hand.add_card(self.shoe.pop_card().unwrap());
if self.players[i].hand.is_backjack() {
self.players[i].state = PlayerState::Blackjack;
}
}
fn dealer_play(&mut self) -> PlayResult {
// Stand on 17 or above
// Hit on 16 and below
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
self.state = GameState::PlayerTurn(0);
}
(_, PlayMoves::Deal(_)) | (GameState::Over, _) | (GameState::Starting, _) => {
return false;
}
}
let player_count = self.players.len();
match self.state {
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 {
let player_value = self.player_hand.get_blackjack_value();
return match dealer_value.cmp(&player_value) {
std::cmp::Ordering::Equal => PlayResult::Push,
std::cmp::Ordering::Greater => PlayResult::StandPlayerLose,
std::cmp::Ordering::Less => PlayResult::StandPlayerWin,
};
// Check winners
self.state = GameState::Over;
return true;
}
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
if self.dealer_hand.get_blackjack_value() > 21 {
return PlayResult::DealerBust;
// Dealer busts
self.state = GameState::Over;
return true;
}
}
} else {
self.state = GameState::PlayerTurn(i + 1);
}
return true;
}
_ => {
return false;
}
}
}

View File

@ -4,73 +4,31 @@ use crate::blackjack::{BlackjackGame, GameState, PlayMoves};
pub fn play() -> Result<(), Box<dyn std::error::Error>> {
let mut game = BlackjackGame::new();
let mut play_move = PlayMoves::Bet;
while !matches!(game.get_state(), GameState::Over) {
match game.play(play_move) {
crate::blackjack::PlayResult::DealerBlackJack => {
print_full_state(&game);
println!("Dealer wins with blackjack!");
}
crate::blackjack::PlayResult::PlayerBlackJack => {
print_full_state(&game);
println!("Player has a blackjack!");
},
crate::blackjack::PlayResult::PushBlackjack => {
print_full_state(&game);
println!("Player and Dealer have a blackjack!");
}
crate::blackjack::PlayResult::DealerBust => {
print_full_state(&game);
println!("Dealer busts. Player wins!");
}
crate::blackjack::PlayResult::StandPlayerLose => {
print_full_state(&game);
println!(
"Dealer wins with {}",
game.get_dealer_hand().get_blackjack_value()
);
}
crate::blackjack::PlayResult::StandPlayerWin => {
print_full_state(&game);
println!(
"Player wins with {}",
game.get_player_hand().get_blackjack_value()
);
}
crate::blackjack::PlayResult::Push => {
print_full_state(&game);
println!("Player and dealer have the same value. Push!");
}
crate::blackjack::PlayResult::Bust => {
print_full_state(&game);
println!(
"Player busts with {}",
game.get_player_hand().get_blackjack_value()
);
}
crate::blackjack::PlayResult::InvalidMove => {
println!("You cannot do that!");
play_move = get_move()?;
}
crate::blackjack::PlayResult::Continue => {
println!("Dealer: {} ?", game.get_dealer_hand().get_card(0).unwrap());
println!(
"Player: {} ({})",
game.get_player_hand(),
game.get_player_hand().get_blackjack_value()
);
play_move = get_move()?
}
}
}
Ok(())
}
fn get_move() -> Result<PlayMoves, io::Error> {
loop {
println!("(H)it (S)tand (D)double");
match game.get_state() {
GameState::PlayerTurn(player_index) => {
print_full_state(&game);
let play_move = get_move(*player_index)?;
if !game.play(play_move){
println!("You can't do that");
}
},
GameState::Over => {
print_full_state(&game);
println!("Game over");
return Ok(());
}
GameState::Starting => {
game.play(PlayMoves::Deal(2));
},
}
}
}
fn get_move(player_index: usize) -> Result<PlayMoves, io::Error> {
loop {
println!("P{}, (H)it (S)tand (D)double",player_index);
let mut buffer = String::new();
stdin().read_line(&mut buffer)?;
@ -85,14 +43,10 @@ fn get_move() -> Result<PlayMoves, io::Error> {
}
fn print_full_state(game: &BlackjackGame) {
println!(
"Dealer: {} ({})",
game.get_dealer_hand(),
game.get_dealer_hand().get_blackjack_value()
);
println!(
"Player: {} ({})",
game.get_player_hand(),
game.get_player_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(){
let player = game.get_player(player_index).unwrap();
println!("P{}: {} ({})",player_index,player.get_hand(),player.get_hand().get_blackjack_value());
}
}