From b2115a4a7e45bf088a36f5af990b34adc1ae4625 Mon Sep 17 00:00:00 2001 From: Nick Pegg Date: Thu, 10 Jul 2025 15:57:45 -0700 Subject: [PATCH] better split ace logic; more args --- src/main.rs | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index 817c64e..1e6c48c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,8 +13,8 @@ fn main() -> anyhow::Result<()> { // TODO: Use anyhow for error handling, get rid of unwraps let args = Args::parse(); match args.mode { - Mode::Interactive => interactive_play(args.show_count), - Mode::OldMan => old_man(), + Mode::Interactive => interactive_play(args), + Mode::OldMan => old_man(args), } } @@ -32,19 +32,23 @@ struct Args { #[arg(long, default_value_t = false)] show_count: bool, + + #[arg(long, default_value_t = 6)] + decks: u8, } -fn interactive_play(show_count: bool) -> anyhow::Result<()> { +fn interactive_play(args: Args) -> anyhow::Result<()> { // TODO: Make a way to reset bank let term = Term::stdout(); let mut bank: u32 = load_bank()?.unwrap_or(1_000); - let mut table = Table::new(6, bank).with_hit_on_soft_17(); + let mut table = Table::new(args.decks, bank).with_hit_on_soft_17(); let mut last_bet: Option = None; loop { println!("\nMoney in the bank: ${bank}"); - if show_count { + // TODO: show normalized card count + if args.show_count { println!("Card count: {}", table.count()); println!("Cards in shoe: {}", table.shoe_count()); } @@ -84,7 +88,7 @@ fn interactive_play(show_count: bool) -> anyhow::Result<()> { println!(); let mut turn = table.deal_hand(bet); - let split_turn = interactive_play_turn(&mut turn, &mut table, show_count)?; + let split_turn = interactive_play_turn(&mut turn, &mut table, args.show_count)?; if split_turn.is_none() { table.dealers_turn()?; let result = table.results(turn)?; @@ -92,7 +96,7 @@ fn interactive_play(show_count: bool) -> anyhow::Result<()> { print_result(&result); } else { let mut split_turn = split_turn.unwrap(); - interactive_play_turn(&mut split_turn, &mut table, show_count)?; + interactive_play_turn(&mut split_turn, &mut table, args.show_count)?; table.dealers_turn()?; let result = table.results(turn)?; let split_result = table.results(split_turn)?; @@ -215,7 +219,6 @@ fn print_result(result: &EndState) { println!("You got: ${}", result.returns); } -// TODO: Make this use a lookup table, this if logic is gnarly fn basic_strategy( hand: &Hand, dealer_showing: &Card, @@ -229,9 +232,8 @@ fn basic_strategy( // Got a pair, maybe should split // We check can_split right at the get-go, because if we can't we just follow the Hard // Total table. - let aces = hand.cards()[0].value == "A"; match hand.value() { - 12 if aces => PlayerChoice::Split, + 12 if hand.has_ace() => PlayerChoice::Split, 20 => PlayerChoice::Stand, 18 => match dv { 2..=6 => PlayerChoice::Split, @@ -353,7 +355,7 @@ fn basic_strategy_play_turn( /// addition to points/comps which he would use to eat brunch for free. /// /// Does this actually work, or was the driver full of it? -fn old_man() -> anyhow::Result<()> { +fn old_man(args: Args) -> anyhow::Result<()> { const START: u32 = 100_000; const PER_DAY: u32 = 400; const MIN_BET: u32 = 15; @@ -361,7 +363,7 @@ fn old_man() -> anyhow::Result<()> { // Walk away when we're up by this much const MAX_WIN: u32 = 100; // Walk away when we're down by this much - const MAX_LOSS: u32 = MAX_WIN; + const MAX_LOSS: u32 = 100; // Run sim for this many days const DAYS: u16 = 30; @@ -372,7 +374,7 @@ fn old_man() -> anyhow::Result<()> { for day in 1..=DAYS { bank -= PER_DAY; - let mut table = Table::new(6, PER_DAY).with_hit_on_soft_17(); + let mut table = Table::new(args.decks, PER_DAY).with_hit_on_soft_17(); let mut rounds = 0; while table.player_chips() > MIN_BET && table.player_chips() < (PER_DAY + MAX_WIN) @@ -465,6 +467,17 @@ mod tests { true, ), PlayerChoice::Stand, - ) + ); + + // Always split aces + assert_eq!( + basic_strategy( + &Hand::from([("", "A"), ("", "A")].to_vec()), + &Card::from(("", "2")), + true, + true, + ), + PlayerChoice::Split, + ); } }