diff --git a/src/main.rs b/src/main.rs index 623ed4f..d60f923 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use crate::types::hand::{ Hand, Outcome, Strategy, DEALER_INFINITE_CREDITS, DEFAULT_BET_VALUE, HUMAN_DEFAULT_CREDITS, NO_BET_VALUE, }; +use crate::types::stats::RunStats; pub mod data; pub mod types; @@ -105,7 +106,7 @@ fn reset_game(player: &mut Hand, dealer: &mut Hand) -> Deck { /// This simulates a single "session" of a player sitting down to play a game. /// TODO: Add Monte Carlo and other betting strats /// TODO: Add support for a physical game by re-using the Deck to some degree. -fn run_automated_match(max_games: usize) { +fn run_automated_match(max_games: usize) -> RunStats { let mut deck = Deck::new(); let mut dealer = Hand::new("Dealer", Strategy::Dealer, DEALER_INFINITE_CREDITS); let mut player = Hand::new( @@ -114,6 +115,8 @@ fn run_automated_match(max_games: usize) { HUMAN_DEFAULT_CREDITS, ); + let mut stats = RunStats::new(); + for _ in 0..max_games { init_game(&mut player, &mut dealer, &mut deck); @@ -138,7 +141,8 @@ fn run_automated_match(max_games: usize) { } } - match Hand::determine_outcome(&player, &dealer) { + let match_outcome = Hand::determine_outcome(&player, &dealer); + match match_outcome { Outcome::Win => { player.add_credits(final_bet * 2); } @@ -147,6 +151,7 @@ fn run_automated_match(max_games: usize) { player.add_credits(final_bet); } } + stats.record_match_end(match_outcome); // Broke players can't play if player.get_credits() <= 0 { @@ -156,7 +161,9 @@ fn run_automated_match(max_games: usize) { // According to the internet, digital Blackjack machines reset the deck every game instance. deck = reset_game(&mut player, &mut dealer); } - // TODO return some final stats + + stats.record_credits(player.get_credits()); + stats } /// Runs a single player text-based game or runs a parallelized simulation. @@ -166,7 +173,7 @@ fn main() { if args.runs > 0 { // TODO run n simulations in parallel. for _ in 0..args.runs { - run_automated_match(DEFAULT_MAX_GAMES_PER_RUN); + println!("{}", run_automated_match(DEFAULT_MAX_GAMES_PER_RUN)); } process::exit(0); } diff --git a/src/types/mod.rs b/src/types/mod.rs index 5a124b6..dfc8f26 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,3 +1,4 @@ pub mod card; pub mod deck; pub mod hand; +pub mod stats; diff --git a/src/types/stats.rs b/src/types/stats.rs new file mode 100644 index 0000000..44ab16a --- /dev/null +++ b/src/types/stats.rs @@ -0,0 +1,56 @@ +//! +//! File: stats.rs +//! Description: Data structures for gathering statistics +//! +//! +//! + +use std::fmt; + +use crate::types::hand::Outcome; + +/// Data to track per player "run" (how long a player sits at the table) +pub struct RunStats { + num_games: usize, + wins: usize, + losses: usize, + pushes: usize, + remaining_credits: isize, +} + +impl RunStats { + pub fn new() -> Self { + RunStats { + num_games: 0, + wins: 0, + losses: 0, + pushes: 0, + remaining_credits: 0, + } + } + + /// Records stats when a game (single match) ends + pub fn record_match_end(&mut self, outcome: Outcome) { + self.num_games += 1; + match outcome { + Outcome::Win => self.wins += 1, + Outcome::Loss => self.losses += 1, + Outcome::Push => self.pushes += 1, + } + } + + /// Record the final credit count + pub fn record_credits(&mut self, credits: isize) { + self.remaining_credits = credits; + } +} + +impl fmt::Display for RunStats { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Games: {} | W/L/P: {}/{}/{} | Credits: ${}", + self.num_games, self.wins, self.losses, self.pushes, self.remaining_credits + ) + } +}