ctm / mb2-doc

Mb2, poker software
https://devctm.com
7 stars 2 forks source link

Badugi > 4 card evaluator is broken #1394

Closed ctm closed 2 months ago

ctm commented 2 months ago

Fix the Badugi 5 card evaluator.

I cranked out a quick one this morning and didn't create any tests because it was "obviously" (cough) correct. It isn't. Oops.

I'm marking this as a regression, because our 4-card Badugi evaluator works fine. However, we've never had a 5-card evaluator, so in that sense it's not a regression.

ctm commented 2 months ago

jpmassar: Badugi Three Card: 6 4 3 6d 4h 3c Jc Tc Omaha Jack High Flush Jc Tc 9c 8c 5c jpmassar 8h Ah 3d 4c Ac Badugi Three Card: 8 3 A 8h 3d Ac 4c Ah

ctm commented 2 months ago

My broken evaluator saw that jpmasser had two clubs, so it decided the 4c was the higher of the two, so it removed it and reduced his hand to 8h Ah 3d Ac, then decided he had a 8 3 A badugi. I wasn't thinking clearly this morning when I wrote the code that reduced an N > 4 hand to a 4 card hand.

I'll fix it and we can play again tomorrow.

ctm commented 2 months ago

Fixed. I also added a single unit test for the specific set of cards that jp ran into as well as an exhaustive test that looks at all five card hands and uses a slow recursive solver to compare. The slow solver isn't invoked by default. To invoke it specifically, use cargo test exhaustive_5 --release --lib -- --include-ignored.

FWIW, my working 5 card evaluator simply calls the 4 card evaluator five times, once for each of the four card subsets. It does this by removing one card for each iteration:

    fn eval5_u64(cards: u64, aces_low: bool) -> Evaluation {
        Evaluation::Badugi({
            let cards = cards.convert_aces(aces_low).no_joker();
            BitIter::from(cards)
                .map(|to_remove| {
                    let to_remove = 1 << to_remove;
                    let (clubs, diamonds, hearts, spades) = (cards ^ to_remove).cdhs();
                    Self::eval4(clubs, diamonds, hearts, spades)
                })
                .max()
                .unwrap()
        })
    }

Deployed.