better split ace logic; more args

This commit is contained in:
Nick Pegg 2025-07-10 15:57:45 -07:00
parent 1106f1484f
commit b2115a4a7e

View file

@ -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<u32> = 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,
);
}
}