use std::{fmt::Display, thread::sleep, time::Duration};
use anyhow::{Context, Result};
use colored::Colorize;
use inquire::{Select, Text};
use num::rational::Ratio;
use spinners::{Spinner, Spinners};
use crate::{
cards::{Card, Rank, Shoe},
money::Money,
Casino,
};
#[derive(Clone, Debug)]
pub struct Baccarat {
bet_type: BaccaratBet,
bet_amount: Money,
player: Vec<Card>,
banker: Vec<Card>,
shoe: Shoe,
}
impl Baccarat {
pub fn new(bet_type: &BaccaratBet, bet_amount: &Money) -> Self {
Self {
bet_type: bet_type.clone(),
bet_amount: *bet_amount,
shoe: Shoe::new(8, 0.75),
player: Vec::new(),
banker: Vec::new(),
}
}
pub fn deal_cards(&mut self) {
self.player.push(self.shoe.draw_card());
self.banker.push(self.shoe.draw_card());
self.player.push(self.shoe.draw_card());
self.banker.push(self.shoe.draw_card());
}
pub fn draw_player_card(&mut self) {
self.player.push(self.shoe.draw_card());
}
pub fn draw_banker_card(&mut self) {
self.banker.push(self.shoe.draw_card());
}
pub fn print_hands(&self) {
print!("Player: ");
for card in self.player.iter() {
print!("{}", card);
}
print!(" ({})", self.player.baccarat_sum());
print!(" Banker: ");
for card in self.banker.iter() {
print!("{}", card);
}
print!(" ({})", self.banker.baccarat_sum());
println!();
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BaccaratBet {
Player,
Banker,
Tie,
}
impl Display for BaccaratBet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Player => write!(f, "Player (1:1 payout)"),
Self::Banker => write!(f, "Banker (1:1 payout, -5% commission)"),
Self::Tie => write!(f, "Tie (8:1 payout)"),
}
}
}
pub trait BaccaratValue {
fn baccarat_value(&self) -> u8;
}
impl BaccaratValue for Card {
fn baccarat_value(&self) -> u8 {
match &self.rank {
Rank::Ace => 1,
Rank::Two => 2,
Rank::Three => 3,
Rank::Four => 4,
Rank::Five => 5,
Rank::Six => 6,
Rank::Seven => 7,
Rank::Eight => 8,
Rank::Nine => 9,
Rank::Ten | Rank::Jack | Rank::Queen | Rank::King => 0,
}
}
}
pub trait BaccaratSum {
fn baccarat_sum(&self) -> u8;
}
impl<T> BaccaratSum for Vec<T>
where
T: BaccaratValue,
{
fn baccarat_sum(&self) -> u8 {
let mut sum = 0;
for bv in self {
sum += bv.baccarat_value();
}
sum % 10
}
}
pub fn play_baccarat() -> Result<()> {
let mut casino = Casino::from_filesystem()?;
println!("Your money: {}", casino.bankroll);
let opts = vec![BaccaratBet::Player, BaccaratBet::Banker, BaccaratBet::Tie];
let bet_type = Select::new("What bet do you want to make?", opts).prompt()?;
let bet_amount: Money;
loop {
let bet_text = Text::new("How much will you bet?").prompt()?;
let bet = bet_text
.trim()
.parse::<Money>()
.with_context(|| "Failed to parse prompt text into an integer")?;
if casino.bankroll >= bet {
bet_amount = bet;
break;
} else {
println!("You can't bet that amount, try again.");
}
}
println!("Betting {} on {}", bet_amount, bet_type);
let mut sp = Spinner::new(Spinners::Dots, "Dealing cards...".into());
sleep(Duration::from_millis(1_500));
sp.stop_with_message(format!("{}", "* The dealer issues your cards.".dimmed()));
let mut game = Baccarat::new(&bet_type, &bet_amount);
game.deal_cards();
game.print_hands();
if game.player.baccarat_sum() < 8 && game.banker.baccarat_sum() < 8 {
if game.player.baccarat_sum() <= 5 {
let mut sp = Spinner::new(Spinners::Dots, "Dealing cards...".into());
sleep(Duration::from_millis(500));
sp.stop_with_message(format!(
"{}",
"* The dealer issues another card to the Player hand".dimmed()
));
game.draw_player_card();
} else {
let mut sp = Spinner::new(Spinners::Dots, "Dealing cards...".into());
sleep(Duration::from_millis(500));
sp.stop_with_message(format!("{}", "* The Player hand stands".dimmed()));
}
let banker_draw = (game.banker.baccarat_sum() <= 2)
|| (game.banker.baccarat_sum() == 3 && game.player.baccarat_sum() != 8)
|| (game.banker.baccarat_sum() == 4 && (2..7).contains(&game.player.baccarat_sum()))
|| (game.banker.baccarat_sum() == 5 && (4..7).contains(&game.player.baccarat_sum()))
|| (game.banker.baccarat_sum() == 5 && (6..7).contains(&game.player.baccarat_sum()));
if banker_draw {
let mut sp = Spinner::new(Spinners::Dots, "Dealing cards...".into());
sleep(Duration::from_millis(500));
sp.stop_with_message(format!(
"{}",
"* The dealer issues another card to the Banker hand".dimmed()
));
game.draw_banker_card();
} else {
println!("{}", "* The Banker hand stands".dimmed());
}
game.print_hands();
}
if game.player.baccarat_sum() > game.banker.baccarat_sum() {
let mut sp = Spinner::new(Spinners::Dots, "Calculating results...".into());
sleep(Duration::from_millis(1000));
sp.stop_with_message(format!("{}", "Player wins!".bold()));
if game.bet_type == BaccaratBet::Player {
casino.add_bankroll(game.bet_amount);
println!(
"YOU WIN! You receive {}. You now have {}",
game.bet_amount, casino.bankroll
);
casino
.stats
.baccarat
.record_win(&game.bet_type, game.bet_amount);
} else {
casino.subtract_bankroll(game.bet_amount)?;
casino
.stats
.baccarat
.record_loss(&game.bet_type, game.bet_amount);
println!(
"You lose {}. You now have {}",
game.bet_amount, casino.bankroll
)
}
} else if game.banker.baccarat_sum() > game.player.baccarat_sum() {
let mut sp = Spinner::new(Spinners::Dots, "Calculating results...".into());
sleep(Duration::from_millis(1000));
sp.stop_with_message(format!("{}", "Banker wins!".bold()));
if game.bet_type == BaccaratBet::Banker {
let win_amount = game.bet_amount * Ratio::new(19, 20);
casino.add_bankroll(win_amount);
casino.stats.baccarat.record_win(&game.bet_type, win_amount);
println!(
"YOU WIN! You receive {} (even money minus 5% commission). You now have {}",
win_amount, casino.bankroll
)
} else {
casino.subtract_bankroll(bet_amount)?;
casino
.stats
.baccarat
.record_loss(&game.bet_type, game.bet_amount);
println!(
"You lose {}. You now have {}",
game.bet_amount, casino.bankroll
)
}
} else if game.player.baccarat_sum() == game.banker.baccarat_sum() {
let mut sp = Spinner::new(Spinners::Dots, "Calculating results...".into());
sleep(Duration::from_millis(1000));
sp.stop_with_message(format!("{}", "Player and Banker tie!".bold()));
casino.stats.baccarat.record_tie(&game.bet_type);
println!(
"You receive your {} bet back. You still have {}",
game.bet_amount, casino.bankroll
)
}
casino.check_for_mister_green();
casino.save();
Ok(())
}