more improvements
This commit is contained in:
parent
4387e2cb14
commit
bf73860433
209
src/blackjack.rs
209
src/blackjack.rs
@ -2,22 +2,41 @@ use crate::{decks::new_blackjack_shoe, hand::Hand};
|
|||||||
|
|
||||||
pub struct BlackjackGame {
|
pub struct BlackjackGame {
|
||||||
shoe: Hand,
|
shoe: Hand,
|
||||||
player_hand: Hand,
|
players: Vec<Player>,
|
||||||
dealer_hand: Hand,
|
dealer_hand: Hand,
|
||||||
state: GameState,
|
state: GameState,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PlayResult {
|
pub struct Player {
|
||||||
DealerBlackJack, // Dealer has a blackjack. Player does not
|
hand: Hand,
|
||||||
PlayerBlackJack, // Player has a blackjack. Dealer does not
|
state: PlayerState,
|
||||||
PushBlackjack, // Player and dealer have a blackjack
|
}
|
||||||
Push, // Dealer has a Blackjack and so does the Player
|
|
||||||
Bust, // Player bust
|
impl Player {
|
||||||
DealerBust, // Dealer bust
|
fn new() -> Self {
|
||||||
StandPlayerLose, // Dealer stands and wins
|
Player {
|
||||||
StandPlayerWin, // Dealer stands and loses
|
hand: Hand::new(),
|
||||||
Continue, // Game goes on as normal
|
state: PlayerState::Playing,
|
||||||
InvalidMove,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@ -25,22 +44,22 @@ pub enum PlayMoves {
|
|||||||
Hit,
|
Hit,
|
||||||
Stand,
|
Stand,
|
||||||
DoubleDown,
|
DoubleDown,
|
||||||
Bet, // At the Start of the game
|
Deal(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum GameState {
|
pub enum GameState {
|
||||||
Empty, // No cards have been dealt
|
Starting,
|
||||||
Over, // Game is over
|
Over, // Game is over
|
||||||
PlayerTurn, // Its the turn of the player
|
PlayerTurn(usize), // Its the turn of the player
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlackjackGame {
|
impl BlackjackGame {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
BlackjackGame {
|
BlackjackGame {
|
||||||
shoe: new_blackjack_shoe(6),
|
shoe: new_blackjack_shoe(6),
|
||||||
player_hand: Hand::new(),
|
players: Vec::new(),
|
||||||
dealer_hand: Hand::new(),
|
dealer_hand: Hand::new(),
|
||||||
state: GameState::Empty,
|
state: GameState::Starting,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,98 +67,128 @@ impl BlackjackGame {
|
|||||||
&self.dealer_hand
|
&self.dealer_hand
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_player_hand(&self) -> &Hand {
|
|
||||||
&self.player_hand
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_state(&self) -> &GameState {
|
pub fn get_state(&self) -> &GameState {
|
||||||
&self.state
|
&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) {
|
match (&self.state, action) {
|
||||||
(GameState::Empty, PlayMoves::Bet) => {
|
(GameState::PlayerTurn(i), PlayMoves::Hit) => {
|
||||||
self.player_hand.add_card(self.shoe.pop_card().unwrap());
|
let Some(player) = self.players.get_mut(*i) else {
|
||||||
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
self.player_hand.add_card(self.shoe.pop_card().unwrap());
|
if player.state != PlayerState::Playing {
|
||||||
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
|
return false;
|
||||||
|
|
||||||
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
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
(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 => {
|
std::cmp::Ordering::Equal => {
|
||||||
self.state = GameState::Over;
|
player.state = PlayerState::Maxed;
|
||||||
self.dealer_play()
|
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Greater => {
|
std::cmp::Ordering::Greater => {
|
||||||
self.state = GameState::Over;
|
player.state = PlayerState::Busted;
|
||||||
PlayResult::Bust
|
}
|
||||||
|
std::cmp::Ordering::Less => {
|
||||||
|
// Player is still playing
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Less => PlayResult::Continue,
|
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
(GameState::PlayerTurn, PlayMoves::DoubleDown) => {
|
(GameState::PlayerTurn(i), PlayMoves::DoubleDown) => {
|
||||||
if self.player_hand.count() > 2{
|
let Some(player) = self.players.get_mut(*i) else {
|
||||||
return PlayResult::InvalidMove;
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if player.state != PlayerState::Playing {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.player_hand.add_card(self.shoe.pop_card().unwrap());
|
player.hand.add_card(self.shoe.pop_card().unwrap());
|
||||||
self.state = GameState::Over;
|
match player.hand.get_blackjack_value().cmp(&21) {
|
||||||
return self.dealer_play();
|
std::cmp::Ordering::Equal | std::cmp::Ordering::Less => {
|
||||||
},
|
player.state = PlayerState::Maxed;
|
||||||
(GameState::PlayerTurn, PlayMoves::Stand) => {
|
|
||||||
self.state = GameState::Over;
|
|
||||||
return self.dealer_play();
|
|
||||||
}
|
}
|
||||||
(GameState::Over, _)
|
std::cmp::Ordering::Greater => {
|
||||||
| (GameState::Empty, _)
|
player.state = PlayerState::Busted;
|
||||||
| (GameState::PlayerTurn, PlayMoves::Bet) => {
|
|
||||||
return PlayResult::InvalidMove;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
(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 {
|
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
|
||||||
// Stand on 17 or above
|
|
||||||
// Hit on 16 and below
|
|
||||||
|
|
||||||
|
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 {
|
loop {
|
||||||
let dealer_value = self.dealer_hand.get_blackjack_value();
|
let dealer_value = self.dealer_hand.get_blackjack_value();
|
||||||
if dealer_value >= 17 {
|
if dealer_value >= 17 {
|
||||||
let player_value = self.player_hand.get_blackjack_value();
|
// Check winners
|
||||||
|
self.state = GameState::Over;
|
||||||
return match dealer_value.cmp(&player_value) {
|
return true;
|
||||||
std::cmp::Ordering::Equal => PlayResult::Push,
|
|
||||||
std::cmp::Ordering::Greater => PlayResult::StandPlayerLose,
|
|
||||||
std::cmp::Ordering::Less => PlayResult::StandPlayerWin,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
|
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
|
||||||
|
|
||||||
if self.dealer_hand.get_blackjack_value() > 21 {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,73 +4,31 @@ use crate::blackjack::{BlackjackGame, GameState, PlayMoves};
|
|||||||
|
|
||||||
pub fn play() -> Result<(), Box<dyn std::error::Error>> {
|
pub fn play() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut game = BlackjackGame::new();
|
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 {
|
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();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
stdin().read_line(&mut buffer)?;
|
stdin().read_line(&mut buffer)?;
|
||||||
@ -85,14 +43,10 @@ fn get_move() -> Result<PlayMoves, io::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_full_state(game: &BlackjackGame) {
|
fn print_full_state(game: &BlackjackGame) {
|
||||||
println!(
|
println!("Dealer: {} ({})", game.get_dealer_hand(), game.get_dealer_hand().get_blackjack_value());
|
||||||
"Dealer: {} ({})",
|
|
||||||
game.get_dealer_hand(),
|
for player_index in 0..game.get_player_count(){
|
||||||
game.get_dealer_hand().get_blackjack_value()
|
let player = game.get_player(player_index).unwrap();
|
||||||
);
|
println!("P{}: {} ({})",player_index,player.get_hand(),player.get_hand().get_blackjack_value());
|
||||||
println!(
|
}
|
||||||
"Player: {} ({})",
|
|
||||||
game.get_player_hand(),
|
|
||||||
game.get_player_hand().get_blackjack_value()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user