use crate::{evaluate::utils, ext::AllUnique, Card, Eval, EvalError};
mod statics {
include!("../../table.in");
}
pub fn evaluate<C: AsRef<[Card]>>(cards: C) -> Result<Eval, EvalError> {
let cards = cards.as_ref();
if cards.all_unique() {
match cards.len() {
x if x < 5 => Err(EvalError::InvalidHandSize(x)),
5 => Ok(five(cards)),
_ => Ok(six_plus(cards)),
}
} else {
Err(EvalError::CardsNotUnique(cards.to_vec()))
}
}
fn five(cards: &[Card]) -> Eval {
debug_assert_eq!(cards.len(), 5);
let detect_flush = cards
.iter()
.fold(0xF000, |acc, card| acc & card.unique_integer())
!= 0;
if detect_flush {
let bit_rank_or = cards
.iter()
.fold(0, |acc, card| acc | card.unique_integer())
>> 16;
let prime = utils::prime_product_from_rank_bits(bit_rank_or as i16);
Eval(statics::FLUSH_LOOKUP[&prime])
} else {
let prime = utils::prime_product_from_hand(cards);
Eval(statics::UNSUITED_LOOKUP[&prime])
}
}
fn six_plus(cards: &[Card]) -> Eval {
debug_assert!(cards.len() > 5);
let mut current_max = Eval::WORST;
let all_five_card_combos = utils::combinations_generator(cards.iter().cloned(), 5);
for combo in all_five_card_combos {
let score = five(&combo);
if score > current_max {
current_max = score;
}
}
current_max
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
use crate::{
card::Card,
evaluate::{
hand_rank::PokerHandRank,
meta::Meta,
tests::{FiveCardHand, RepresentativeHand, SevenCardHand, SixCardHand},
utils,
},
};
#[test]
fn test_all_five_card_combos() {
let gen = utils::combinations_generator(Card::generate_deck(), 5);
let evals = gen.fold(HashSet::with_capacity(7462), |mut ints, hand| {
ints.insert(evaluate(&hand).unwrap());
ints
});
assert_eq!(evals.len(), 7462);
(1..=7462).for_each(|i| {
assert!(evals
.iter()
.any(|meta| meta.hand_rank() == PokerHandRank(i)))
});
}
fn representative_hand_evaluates_correctly<T: RepresentativeHand>() {
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::HIGH_CARD)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::HighCard { .. }
));
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::PAIR)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::Pair { .. }
));
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::TWO_PAIR)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::TwoPair { .. }
));
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::THREE_OF_A_KIND)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::ThreeOfAKind { .. }
));
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::STRAIGHT)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::Straight { .. }
));
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::FLUSH)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::Flush { .. }
));
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::FULL_HOUSE)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::FullHouse { .. }
));
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::FOUR_OF_A_KIND)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::FourOfAKind { .. }
));
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::STRAIGHT_FLUSH)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::StraightFlush { .. }
));
assert!(matches!(
evaluate(
&Card::parse_to_iter(T::ROYAL_FLUSH)
.try_collect::<Vec<_>>()
.unwrap()
)
.unwrap()
.0,
Meta::StraightFlush {
hand_rank: PokerHandRank(1),
..
}
));
}
#[test]
fn representative_five_card_hands() {
representative_hand_evaluates_correctly::<FiveCardHand>();
}
#[test]
fn representative_six_card_hands() { representative_hand_evaluates_correctly::<SixCardHand>(); }
#[test]
fn representative_seven_card_hands() {
representative_hand_evaluates_correctly::<SevenCardHand>();
}
}