initial commit
This commit is contained in:
commit
1982d27c4f
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
75
Cargo.lock
generated
Normal file
75
Cargo.lock
generated
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.154"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustjack"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "rustjack"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand = "0.8.5"
|
104
src/blackjack.rs
Normal file
104
src/blackjack.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
use crate::{decks::new_blackjack_shoe, hand::Hand};
|
||||||
|
|
||||||
|
pub struct BlackjackGame {
|
||||||
|
shoe: Hand,
|
||||||
|
player_hand: Hand,
|
||||||
|
dealer_hand: Hand,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DealResult {
|
||||||
|
DealerBlackJack, // Dealer has a blackjack. Player does not.
|
||||||
|
Push, // Dealer has a Blackjack and so does the Player
|
||||||
|
PlayerBlackJack, // Player has a blackjack. Dealer does not.
|
||||||
|
Continue, // Game continues
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HitResult {
|
||||||
|
Bust,
|
||||||
|
Continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DealerPlayResult {
|
||||||
|
Bust,
|
||||||
|
Push, // Player and dealer have the same value
|
||||||
|
StandPlayerLose, // Dealer stands and wins
|
||||||
|
StandPlayerWin, // Dealer stands and loses
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlackjackGame {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
BlackjackGame {
|
||||||
|
shoe: new_blackjack_shoe(6),
|
||||||
|
player_hand: Hand::new(),
|
||||||
|
dealer_hand: Hand::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_dealer_hand(&self) -> &Hand {
|
||||||
|
&self.dealer_hand
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_player_hand(&self) -> &Hand {
|
||||||
|
&self.player_hand
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deal(&mut self) -> DealResult {
|
||||||
|
self.player_hand.add_card(self.shoe.pop_card().unwrap());
|
||||||
|
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
|
||||||
|
|
||||||
|
self.player_hand.add_card(self.shoe.pop_card().unwrap());
|
||||||
|
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
|
||||||
|
|
||||||
|
if self.dealer_hand.is_backjack() {
|
||||||
|
if self.player_hand.is_backjack() {
|
||||||
|
return DealResult::Push;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DealResult::DealerBlackJack;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.player_hand.is_backjack() {
|
||||||
|
return DealResult::PlayerBlackJack;
|
||||||
|
}
|
||||||
|
|
||||||
|
DealResult::Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hit(&mut self) -> HitResult {
|
||||||
|
self.player_hand.add_card(self.shoe.pop_card().unwrap());
|
||||||
|
|
||||||
|
if self.player_hand.get_blackjack_value() > 21 {
|
||||||
|
return HitResult::Bust;
|
||||||
|
}
|
||||||
|
|
||||||
|
HitResult::Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stand(&mut self) {
|
||||||
|
// ???
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dealer_play(&mut self) -> DealerPlayResult {
|
||||||
|
// Stand on 17 or above
|
||||||
|
// Hit on 16 and below
|
||||||
|
|
||||||
|
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 => DealerPlayResult::Push,
|
||||||
|
std::cmp::Ordering::Greater => DealerPlayResult::StandPlayerLose,
|
||||||
|
std::cmp::Ordering::Less => DealerPlayResult::StandPlayerWin,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dealer_hand.add_card(self.shoe.pop_card().unwrap());
|
||||||
|
|
||||||
|
if self.dealer_hand.get_blackjack_value() > 21 {
|
||||||
|
return DealerPlayResult::Bust;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/card.rs
Normal file
18
src/card.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::{card_index::CardIndex, card_suit::CardSuit};
|
||||||
|
|
||||||
|
pub struct Card {
|
||||||
|
pub suit: CardSuit,
|
||||||
|
pub index: CardIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Card {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.suit.fmt(f)?;
|
||||||
|
f.write_str(" ")?;
|
||||||
|
self.index.fmt(f)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
102
src/card_index.rs
Normal file
102
src/card_index.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum CardIndex {
|
||||||
|
A = 14,
|
||||||
|
K = 13,
|
||||||
|
Q = 12,
|
||||||
|
J = 11,
|
||||||
|
N10 = 10,
|
||||||
|
N9 = 9,
|
||||||
|
N8 = 8,
|
||||||
|
N7 = 7,
|
||||||
|
N6 = 6,
|
||||||
|
N5 = 5,
|
||||||
|
N4 = 4,
|
||||||
|
N3 = 3,
|
||||||
|
N2 = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CardIndex {
|
||||||
|
pub fn get_blackjack_value(&self, ace_as_1: bool) -> u32 {
|
||||||
|
match self {
|
||||||
|
Self::J | Self::K | Self::Q => 10,
|
||||||
|
Self::A => {
|
||||||
|
if ace_as_1 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (*self as u8) as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<char> for CardIndex {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
'A' => Ok(CardIndex::A),
|
||||||
|
'K' => Ok(CardIndex::K),
|
||||||
|
'Q' => Ok(CardIndex::Q),
|
||||||
|
'J' => Ok(CardIndex::J),
|
||||||
|
'T' => Ok(CardIndex::N10),
|
||||||
|
'9' => Ok(CardIndex::N9),
|
||||||
|
'8' => Ok(CardIndex::N8),
|
||||||
|
'7' => Ok(CardIndex::N7),
|
||||||
|
'6' => Ok(CardIndex::N6),
|
||||||
|
'5' => Ok(CardIndex::N5),
|
||||||
|
'4' => Ok(CardIndex::N4),
|
||||||
|
'3' => Ok(CardIndex::N3),
|
||||||
|
'2' => Ok(CardIndex::N2),
|
||||||
|
_ => Err("Not a card"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CardIndex {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let index = match self {
|
||||||
|
Self::A => "A".to_owned(),
|
||||||
|
Self::K => "K".to_owned(),
|
||||||
|
Self::Q => "Q".to_owned(),
|
||||||
|
Self::J => "J".to_owned(),
|
||||||
|
_ => (*self as u8).to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
f.write_str(&index)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn blackjack_value_ace() {
|
||||||
|
assert_eq!(CardIndex::A.get_blackjack_value(true), 1);
|
||||||
|
assert_eq!(CardIndex::A.get_blackjack_value(false), 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn blackjack_value_face() {
|
||||||
|
assert_eq!(CardIndex::J.get_blackjack_value(true), 10);
|
||||||
|
assert_eq!(CardIndex::Q.get_blackjack_value(true), 10);
|
||||||
|
assert_eq!(CardIndex::K.get_blackjack_value(true), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn blackjack_value_num() {
|
||||||
|
assert_eq!(CardIndex::N10.get_blackjack_value(true), 10);
|
||||||
|
assert_eq!(CardIndex::N9.get_blackjack_value(true), 9);
|
||||||
|
assert_eq!(CardIndex::N8.get_blackjack_value(true), 8);
|
||||||
|
assert_eq!(CardIndex::N5.get_blackjack_value(true), 5);
|
||||||
|
assert_eq!(CardIndex::N3.get_blackjack_value(true), 3);
|
||||||
|
assert_eq!(CardIndex::N2.get_blackjack_value(true), 2);
|
||||||
|
}
|
||||||
|
}
|
47
src/card_suit.rs
Normal file
47
src/card_suit.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum CardSuit {
|
||||||
|
Spades,
|
||||||
|
Clubs,
|
||||||
|
Hearts,
|
||||||
|
Diamonds,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CardSuit {
|
||||||
|
pub fn is_red(&self) -> bool {
|
||||||
|
matches!(self, CardSuit::Hearts | CardSuit::Diamonds)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_black(&self) -> bool {
|
||||||
|
!self.is_red()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CardSuit {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let icon = match self {
|
||||||
|
CardSuit::Clubs => "♣️",
|
||||||
|
CardSuit::Diamonds => "♦️",
|
||||||
|
CardSuit::Hearts => "♥️",
|
||||||
|
CardSuit::Spades => "♠️",
|
||||||
|
};
|
||||||
|
|
||||||
|
f.write_str(icon)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn color() {
|
||||||
|
assert!(CardSuit::Hearts.is_red());
|
||||||
|
assert!(CardSuit::Diamonds.is_red());
|
||||||
|
assert!(CardSuit::Clubs.is_black());
|
||||||
|
assert!(CardSuit::Spades.is_black());
|
||||||
|
}
|
||||||
|
}
|
100
src/console_blackjack.rs
Normal file
100
src/console_blackjack.rs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
use std::io::stdin;
|
||||||
|
|
||||||
|
use crate::blackjack::BlackjackGame;
|
||||||
|
|
||||||
|
pub fn play() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut game = BlackjackGame::new();
|
||||||
|
|
||||||
|
let deal = game.deal();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Dealer hand: {} ?",
|
||||||
|
game.get_dealer_hand().get_card(0).unwrap()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"Player hand: {} ({})",
|
||||||
|
game.get_player_hand(),
|
||||||
|
game.get_player_hand().get_blackjack_value()
|
||||||
|
);
|
||||||
|
|
||||||
|
match deal {
|
||||||
|
crate::blackjack::DealResult::DealerBlackJack => {
|
||||||
|
println!("Dealer has a blackjack!");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
crate::blackjack::DealResult::PlayerBlackJack => {
|
||||||
|
println!("Player has a blackjack!");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
crate::blackjack::DealResult::Push => {
|
||||||
|
println!("Both Player and dealer have a blackjack!");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
crate::blackjack::DealResult::Continue => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
println!("(H)it (S)tand");
|
||||||
|
|
||||||
|
let mut buffer = String::new();
|
||||||
|
stdin().read_line(&mut buffer)?;
|
||||||
|
|
||||||
|
let hit = buffer.trim() == "h";
|
||||||
|
|
||||||
|
if hit {
|
||||||
|
let hit_result = game.hit();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Dealer hand: {} ?",
|
||||||
|
game.get_dealer_hand().get_card(0).unwrap()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"Player hand: {} ({})",
|
||||||
|
game.get_player_hand(),
|
||||||
|
game.get_player_hand().get_blackjack_value()
|
||||||
|
);
|
||||||
|
|
||||||
|
match hit_result {
|
||||||
|
crate::blackjack::HitResult::Bust => {
|
||||||
|
println!("Player busts");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
crate::blackjack::HitResult::Continue => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
game.stand();
|
||||||
|
|
||||||
|
let dealer_play = game.dealer_play();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Dealer hand: {} ({})",
|
||||||
|
game.get_dealer_hand(),
|
||||||
|
game.get_dealer_hand().get_blackjack_value()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"Player hand: {} ({})",
|
||||||
|
game.get_player_hand(),
|
||||||
|
game.get_player_hand().get_blackjack_value()
|
||||||
|
);
|
||||||
|
|
||||||
|
match dealer_play {
|
||||||
|
crate::blackjack::DealerPlayResult::Bust => {
|
||||||
|
println!("Dealer bust. Player wins.");
|
||||||
|
}
|
||||||
|
crate::blackjack::DealerPlayResult::Push => {
|
||||||
|
println!("Player and dealer have same value. Push.");
|
||||||
|
}
|
||||||
|
crate::blackjack::DealerPlayResult::StandPlayerLose => {
|
||||||
|
println!("Dealer wins");
|
||||||
|
}
|
||||||
|
crate::blackjack::DealerPlayResult::StandPlayerWin => {
|
||||||
|
println!("Player wins");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
54
src/decks.rs
Normal file
54
src/decks.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use crate::{card::Card, hand::Hand, CardIndex as CI, CardSuit as CS};
|
||||||
|
|
||||||
|
pub fn new_full_deck() -> Hand {
|
||||||
|
let mut hand = Hand::new();
|
||||||
|
|
||||||
|
for suit in [CS::Clubs, CS::Diamonds, CS::Hearts, CS::Spades] {
|
||||||
|
for index in [
|
||||||
|
CI::A,
|
||||||
|
CI::J,
|
||||||
|
CI::K,
|
||||||
|
CI::Q,
|
||||||
|
CI::N10,
|
||||||
|
CI::N9,
|
||||||
|
CI::N8,
|
||||||
|
CI::N7,
|
||||||
|
CI::N6,
|
||||||
|
CI::N5,
|
||||||
|
CI::N4,
|
||||||
|
CI::N3,
|
||||||
|
CI::N2,
|
||||||
|
] {
|
||||||
|
hand.add_card(Card { suit, index });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hand
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_blackjack_shoe(decks: u32) -> Hand {
|
||||||
|
let mut hand = Hand::new();
|
||||||
|
|
||||||
|
for _ in 0..decks {
|
||||||
|
hand.merge(&mut new_full_deck());
|
||||||
|
}
|
||||||
|
|
||||||
|
hand.shuffle();
|
||||||
|
|
||||||
|
hand
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn full_deck_count() {
|
||||||
|
assert_eq!(new_full_deck().count(), 52);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shoe_count() {
|
||||||
|
assert_eq!(new_blackjack_shoe(6).count(), 6 * 52);
|
||||||
|
}
|
||||||
|
}
|
199
src/hand.rs
Normal file
199
src/hand.rs
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::{card::Card, card_index::CardIndex};
|
||||||
|
|
||||||
|
pub struct Hand {
|
||||||
|
cards: Vec<Card>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hand {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Hand { cards: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_card(&mut self, card: Card) {
|
||||||
|
self.cards.push(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_card(&self, i: usize) -> Option<&Card> {
|
||||||
|
return self.cards.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.cards.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_card(&mut self, i: usize) -> Card {
|
||||||
|
self.cards.remove(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_card(&mut self) -> Option<Card> {
|
||||||
|
self.cards.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shuffle(&mut self) {
|
||||||
|
self.cards.shuffle(&mut thread_rng());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_backjack(&self) -> bool {
|
||||||
|
self.cards.len() == 2 && self.get_blackjack_value() == 21
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put another hand into this.
|
||||||
|
* Leaves the other hand empty.
|
||||||
|
*/
|
||||||
|
pub fn merge(&mut self, other: &mut Hand) {
|
||||||
|
self.cards.append(&mut other.cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_blackjack_value(&self) -> u32 {
|
||||||
|
let mut sum: u32 = self
|
||||||
|
.cards
|
||||||
|
.iter()
|
||||||
|
.map(|card| card.index.get_blackjack_value(true))
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
// Add 10 for the all aces (adds up to 11) until we go over 21
|
||||||
|
let aces = self
|
||||||
|
.cards
|
||||||
|
.iter()
|
||||||
|
.filter(|card| card.index == CardIndex::A)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
for _ in 0..aces {
|
||||||
|
if sum + 10 > 21 {
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Hand {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for card in self.cards.iter() {
|
||||||
|
card.fmt(f)?;
|
||||||
|
f.write_str(" ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_blackjack_ace_first() {
|
||||||
|
let mut hand = Hand::new();
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Hearts,
|
||||||
|
index: CardIndex::A,
|
||||||
|
});
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Diamonds,
|
||||||
|
index: CardIndex::N10,
|
||||||
|
});
|
||||||
|
assert!(hand.is_backjack());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_blackjack_ace_last() {
|
||||||
|
let mut hand = Hand::new();
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Diamonds,
|
||||||
|
index: CardIndex::J,
|
||||||
|
});
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Hearts,
|
||||||
|
index: CardIndex::A,
|
||||||
|
});
|
||||||
|
assert!(hand.is_backjack());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_not_blackjack_too_many() {
|
||||||
|
let mut hand = Hand::new();
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Diamonds,
|
||||||
|
index: CardIndex::J,
|
||||||
|
});
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Hearts,
|
||||||
|
index: CardIndex::A,
|
||||||
|
});
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Spades,
|
||||||
|
index: CardIndex::A,
|
||||||
|
});
|
||||||
|
assert!(!hand.is_backjack());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_not_blackjack_too_few() {
|
||||||
|
let mut hand = Hand::new();
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Hearts,
|
||||||
|
index: CardIndex::A,
|
||||||
|
});
|
||||||
|
assert!(!hand.is_backjack());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_not_blackjack_value_21() {
|
||||||
|
let mut hand = Hand::new();
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Hearts,
|
||||||
|
index: CardIndex::K,
|
||||||
|
});
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Clubs,
|
||||||
|
index: CardIndex::N7,
|
||||||
|
});
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Hearts,
|
||||||
|
index: CardIndex::N4,
|
||||||
|
});
|
||||||
|
assert!(!hand.is_backjack());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn blackjack_value() {
|
||||||
|
let mut hand = Hand::new();
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Hearts,
|
||||||
|
index: CardIndex::K,
|
||||||
|
});
|
||||||
|
assert_eq!(hand.get_blackjack_value(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merge_hands() {
|
||||||
|
let mut hand = Hand::new();
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Diamonds,
|
||||||
|
index: CardIndex::N5,
|
||||||
|
});
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Spades,
|
||||||
|
index: CardIndex::Q,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut other = Hand::new();
|
||||||
|
hand.add_card(Card {
|
||||||
|
suit: crate::card_suit::CardSuit::Spades,
|
||||||
|
index: CardIndex::K,
|
||||||
|
});
|
||||||
|
|
||||||
|
hand.merge(&mut other);
|
||||||
|
|
||||||
|
assert_eq!(other.count(), 0);
|
||||||
|
assert_eq!(hand.count(), 3);
|
||||||
|
}
|
||||||
|
}
|
19
src/main.rs
Normal file
19
src/main.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use console_blackjack::play;
|
||||||
|
|
||||||
|
use crate::{card_index::CardIndex, card_suit::CardSuit};
|
||||||
|
|
||||||
|
mod blackjack;
|
||||||
|
mod card;
|
||||||
|
mod card_index;
|
||||||
|
mod card_suit;
|
||||||
|
mod console_blackjack;
|
||||||
|
mod decks;
|
||||||
|
mod hand;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
play()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user