Compare commits
3 Commits
bf73860433
...
d34a72cf3b
| Author | SHA1 | Date | |
|---|---|---|---|
|
d34a72cf3b
|
|||
|
6c21eb960f
|
|||
|
338b5d442a
|
264
src/blackjack.rs
264
src/blackjack.rs
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/hand.rs
21
src/hand.rs
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user