use crate::card::{Card, Hand, Suit};
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, strum_macros::Display)]
pub enum Rank {
RoyalFlush,
StraightFlush,
FourOfAKind,
FullHouse,
Flush,
Straight,
ThreeOfAKind,
TwoPair,
OnePair,
HighCard(Card),
None,
}
pub struct MatchHandEvaluator;
impl MatchHandEvaluator {
pub fn match_eval(hand: &mut Hand) -> Rank {
hand.sort();
let hand = hand.get_hand_slice();
match hand {
[Card { suit: s1, val: 14 }, Card { suit: s2, val: 13 }, Card { suit: s3, val: 12 }, Card { suit: s4, val: 11 }, Card { suit: s5, val: 10 }]
if Self::suits(s1, s2, s3, s4, s5) =>
{
Rank::RoyalFlush
}
[Card { suit: s1, val: v1 }, Card { suit: s2, val: v2 }, Card { suit: s3, val: v3 }, Card { suit: s4, val: v4 }, Card { suit: s5, val: v5 }]
if Self::seq(*v1, *v2, *v3, *v4, *v5) && Self::suits(s1, s2, s3, s4, s5) =>
{
Rank::StraightFlush
}
[Card { suit: s1, val: 14 }, Card { suit: s2, val: 5 }, Card { suit: s3, val: 4 }, Card { suit: s4, val: 3 }, Card { suit: s5, val: 2 }]
if Self::suits(s1, s2, s3, s4, s5) =>
{
Rank::StraightFlush }
[Card { val: v1, .. }, Card { val: v2, .. }, Card { val: v3, .. }, Card { val: v4, .. }, Card { val: v5, .. }]
if (v1 == v2 && v2 == v3 && v3 == v4 || v2 == v3 && v3 == v4 && v4 == v5) =>
{
Rank::FourOfAKind
}
[Card { val: v1, .. }, Card { val: v2, .. }, Card { val: v3, .. }, Card { val: v4, .. }, Card { val: v5, .. }]
if (v1 == v2 && v2 == v3 && v4 == v5 || v3 == v4 && v4 == v5 && v1 == v2) =>
{
Rank::FullHouse
}
[Card { suit: s1, .. }, Card { suit: s2, .. }, Card { suit: s3, .. }, Card { suit: s4, .. }, Card { suit: s5, .. }]
if Self::suits(s1, s2, s3, s4, s5) =>
{
Rank::Flush
}
[Card { val: v1, .. }, Card { val: v2, .. }, Card { val: v3, .. }, Card { val: v4, .. }, Card { val: v5, .. }]
if Self::seq(*v1, *v2, *v3, *v4, *v5) =>
{
Rank::Straight
}
[Card { val: 14, .. }, Card { val: 5, .. }, Card { val: 4, .. }, Card { val: 3, .. }, Card { val: 2, .. }] =>
{
Rank::Straight }
[Card { val: v1, .. }, Card { val: v2, .. }, Card { val: v3, .. }, Card { val: v4, .. }, Card { val: v5, .. }]
if (v1 == v2 && v2 == v3 || v2 == v3 && v3 == v4 || v3 == v4 && v4 == v5) =>
{
Rank::ThreeOfAKind
}
[Card { val: v1, .. }, Card { val: v2, .. }, Card { val: v3, .. }, Card { val: v4, .. }, Card { val: v5, .. }]
if (v1 == v2 && v3 == v4 || v1 == v2 && v4 == v5 || v2 == v3 && v4 == v5) =>
{
Rank::TwoPair
}
[Card { val: v1, .. }, Card { val: v2, .. }, Card { val: v3, .. }, Card { val: v4, .. }, Card { val: v5, .. }]
if (v1 == v2 || v2 == v3 || v3 == v4 || v4 == v5) =>
{
Rank::OnePair
}
hand => Rank::HighCard(hand[0]),
}
}
fn suits(c0: &Suit, c1: &Suit, c2: &Suit, c3: &Suit, c4: &Suit) -> bool {
c0 == c1 && c1 == c2 && c2 == c3 && c3 == c4
}
fn seq(v0: u8, v1: u8, v2: u8, v3: u8, v4: u8) -> bool {
v0 == (v1 + 1) && v1 == (v2 + 1) && v2 == (v3 + 1) && v3 == (v4 + 1)
}
}
#[macro_export]
macro_rules! assert_rank {
($hand:expr, $rank:expr) => {
assert_eq!(MatchHandEvaluator::match_eval(&mut $hand), $rank);
};
}
#[cfg(test)]
mod test {
use super::MatchHandEvaluator;
use super::Rank;
use crate::card::{Card, Hand};
use crate::hand;
use crate::newcard;
#[test]
fn rank_royal_flush() {
assert_rank!(hand!["Ad", "Kd", "Qd", "Jd", "10d"], Rank::RoyalFlush);
assert_rank!(hand!["Ah", "Kh", "Qh", "Jh", "10h"], Rank::RoyalFlush);
assert_rank!(hand!["Ac", "Kc", "Qc", "Jc", "10c"], Rank::RoyalFlush);
assert_rank!(hand!["As", "Ks", "Qs", "Js", "10s"], Rank::RoyalFlush);
}
#[test]
fn rank_straight_flush() {
assert_rank!(hand!["5d", "4d", "3d", "2d", "Ad"], Rank::StraightFlush);
assert_rank!(hand!["10h", "9h", "8h", "7h", "6h"], Rank::StraightFlush);
assert_rank!(hand!["Ks", "Qs", "Js", "10s", "9s"], Rank::StraightFlush);
}
#[test]
fn rank_four_of_a_kind() {
assert_rank!(hand!["Kd", "Kh", "Kc", "Ks", "Qd"], Rank::FourOfAKind);
assert_rank!(hand!["Kd", "6h", "6c", "6s", "6d"], Rank::FourOfAKind);
}
#[test]
fn rank_full_house() {
assert_rank!(hand!["Kd", "Kh", "Kc", "8s", "8d"], Rank::FullHouse);
assert_rank!(hand!["2d", "2h", "Qc", "Qs", "Qd"], Rank::FullHouse);
}
#[test]
fn rank_3_of_a_kind() {
assert_rank!(hand!["Kd", "Kh", "Kc", "10s", "8d"], Rank::ThreeOfAKind);
assert_rank!(hand!["2d", "Jh", "Qc", "Qs", "Qd"], Rank::ThreeOfAKind);
assert_rank!(hand!["Ah", "Jd", "Jh", "Jc", "9s"], Rank::ThreeOfAKind);
}
#[test]
fn rank_two_pairs() {
assert_rank!(hand!["Kd", "Kh", "Jc", "Js", "10d"], Rank::TwoPair);
assert_rank!(hand!["9d", "5h", "5c", "6s", "6d"], Rank::TwoPair);
assert_rank!(hand!["Kd", "Kh", "Jc", "10s", "10d"], Rank::TwoPair);
}
#[test]
fn rank_one_pairs() {
assert_rank!(hand!["Kd", "Kh", "2c", "Js", "10d"], Rank::OnePair);
assert_rank!(hand!["9d", "5h", "5c", "3s", "6d"], Rank::OnePair);
}
#[test]
fn rank_flush() {
assert_rank!(hand!["Kh", "Jh", "9h", "7h", "3h"], Rank::Flush);
assert_rank!(hand!["Ad", "Qd", "10d", "6d", "2d"], Rank::Flush);
assert_rank!(hand!["Js", "10s", "8s", "5s", "4s"], Rank::Flush);
assert_rank!(hand!["Qc", "9c", "7c", "6c", "3c"], Rank::Flush);
}
#[test]
fn rank_straight() {
assert_rank!(hand!["Ah", "Kd", "Qc", "Js", "10h"], Rank::Straight);
assert_rank!(hand!["9d", "8h", "7c", "6s", "5d"], Rank::Straight);
assert_rank!(hand!["5c", "4h", "3d", "2s", "Ah"], Rank::Straight);
assert_rank!(hand!["7h", "6d", "5c", "4s", "3h"], Rank::Straight);
assert_rank!(hand!["Kd", "Qh", "Jc", "10s", "9d"], Rank::Straight);
}
#[test]
fn rank_high_card() {
assert_rank!(hand!["Ah", "Kd", "Qc", "Js", "9h"], Rank::HighCard(newcard!["Ah"]));
assert_rank!(hand!["Kh", "Jd", "9c", "7s", "5h"], Rank::HighCard(newcard!["Kh"]));
assert_rank!(hand!["Qd", "10h", "8c", "6s", "3h"], Rank::HighCard(newcard!["Qd"]));
assert_rank!(hand!["Jc", "9h", "7d", "5s", "2h"], Rank::HighCard(newcard!["Jc"]));
}
#[test]
fn rank_one_pair_all_positions() {
assert_rank!(hand!["Kd", "Kh", "2c", "Js", "10d"], Rank::OnePair);
assert_rank!(hand!["Ah", "Qd", "Qh", "Jc", "9s"], Rank::OnePair);
assert_rank!(hand!["Ah", "Kd", "10h", "10c", "6s"], Rank::OnePair);
assert_rank!(hand!["9d", "5h", "5c", "3s", "6d"], Rank::OnePair);
assert_rank!(hand!["2h", "2d", "Ac", "Ks", "Qh"], Rank::OnePair);
}
#[test]
fn rank_four_of_a_kind_all_positions() {
assert_rank!(hand!["Kd", "Kh", "Kc", "Ks", "Qd"], Rank::FourOfAKind);
assert_rank!(hand!["Kd", "6h", "6c", "6s", "6d"], Rank::FourOfAKind);
assert_rank!(hand!["Ad", "Ah", "Ac", "As", "Kd"], Rank::FourOfAKind);
assert_rank!(hand!["2d", "2h", "2c", "2s", "3d"], Rank::FourOfAKind);
}
#[test]
fn rank_full_house_variations() {
assert_rank!(hand!["Kd", "Kh", "Kc", "8s", "8d"], Rank::FullHouse);
assert_rank!(hand!["2d", "2h", "Qc", "Qs", "Qd"], Rank::FullHouse);
assert_rank!(hand!["Ad", "Ah", "Ac", "Ks", "Kd"], Rank::FullHouse);
assert_rank!(hand!["3d", "3h", "3c", "As", "Ad"], Rank::FullHouse);
}
#[test]
fn rank_straight_flush_variations() {
assert_rank!(hand!["5d", "4d", "3d", "2d", "Ad"], Rank::StraightFlush);
assert_rank!(hand!["10h", "9h", "8h", "7h", "6h"], Rank::StraightFlush);
assert_rank!(hand!["Ks", "Qs", "Js", "10s", "9s"], Rank::StraightFlush);
assert_rank!(hand!["6c", "5c", "4c", "3c", "2c"], Rank::StraightFlush);
}
#[test]
fn rank_seq() {
assert_eq!(MatchHandEvaluator::seq(14, 13, 12, 11, 10), true);
assert_eq!(MatchHandEvaluator::seq(13, 14, 12, 11, 5), false);
assert_eq!(MatchHandEvaluator::seq(9, 8, 7, 6, 5), true);
assert_eq!(MatchHandEvaluator::seq(5, 4, 3, 2, 1), true);
assert_eq!(MatchHandEvaluator::seq(10, 9, 8, 7, 5), false);
}
#[test]
fn rank_suits() {
use crate::card::Suit;
assert_eq!(MatchHandEvaluator::suits(&Suit::Hearts, &Suit::Hearts, &Suit::Hearts, &Suit::Hearts, &Suit::Hearts), true);
assert_eq!(MatchHandEvaluator::suits(&Suit::Spades, &Suit::Spades, &Suit::Spades, &Suit::Spades, &Suit::Spades), true);
assert_eq!(MatchHandEvaluator::suits(&Suit::Diamonds, &Suit::Diamonds, &Suit::Diamonds, &Suit::Diamonds, &Suit::Diamonds), true);
assert_eq!(MatchHandEvaluator::suits(&Suit::Clubs, &Suit::Clubs, &Suit::Clubs, &Suit::Clubs, &Suit::Clubs), true);
assert_eq!(MatchHandEvaluator::suits(&Suit::Hearts, &Suit::Diamonds, &Suit::Hearts, &Suit::Hearts, &Suit::Hearts), false);
assert_eq!(MatchHandEvaluator::suits(&Suit::Spades, &Suit::Spades, &Suit::Spades, &Suit::Spades, &Suit::Clubs), false);
assert_eq!(MatchHandEvaluator::suits(&Suit::Hearts, &Suit::Clubs, &Suit::Diamonds, &Suit::Spades, &Suit::Hearts), false);
}
#[test]
fn rank_comparison_order() {
assert!(Rank::RoyalFlush < Rank::StraightFlush);
assert!(Rank::StraightFlush < Rank::FourOfAKind);
assert!(Rank::FourOfAKind < Rank::FullHouse);
assert!(Rank::FullHouse < Rank::Flush);
assert!(Rank::Flush < Rank::Straight);
assert!(Rank::Straight < Rank::ThreeOfAKind);
assert!(Rank::ThreeOfAKind < Rank::TwoPair);
assert!(Rank::TwoPair < Rank::OnePair);
assert!(Rank::OnePair < Rank::HighCard(newcard!["Ah"]));
}
#[test]
fn rank_not_flush_or_straight() {
assert_rank!(hand!["Kd", "Kh", "Kc", "10s", "8d"], Rank::ThreeOfAKind);
assert_rank!(hand!["Kd", "Kh", "Jc", "Js", "10d"], Rank::TwoPair);
assert_rank!(hand!["Kh", "Qd", "Jc", "10s", "8h"], Rank::HighCard(newcard!["Kh"]));
assert_rank!(hand!["Kh", "Qh", "Jh", "9h", "7h"], Rank::Flush);
}
#[test]
fn rank_edge_cases() {
assert_rank!(hand!["Ad", "Ah", "Ac", "As", "Kd"], Rank::FourOfAKind);
assert_rank!(hand!["5d", "4h", "3c", "3s", "2d"], Rank::OnePair);
assert_rank!(hand!["9d", "8h", "6c", "5s", "4d"], Rank::HighCard(newcard!["9d"]));
assert_rank!(hand!["5c", "4h", "3d", "2s", "Ah"], Rank::Straight);
}
#[test]
fn rank_unsorted_hands() {
assert_rank!(hand!["10d", "Jd", "Ad", "Kd", "Qd"], Rank::RoyalFlush);
assert_rank!(hand!["7h", "7d", "7c", "2s", "7s"], Rank::FourOfAKind);
assert_rank!(hand!["Qh", "9d", "Qc", "9s", "Qs"], Rank::FullHouse);
assert_rank!(hand!["6d", "8h", "7c", "5s", "9d"], Rank::Straight);
assert_rank!(hand!["3h", "Kd", "3c", "Ks", "9d"], Rank::TwoPair);
assert_rank!(hand!["2h", "5d", "9c", "Ks", "7d"], Rank::HighCard(newcard!["Ks"]));
}
#[test]
fn rank_boundary_cases() {
assert_rank!(hand!["9d", "8d", "7d", "6d", "5h"], Rank::Straight);
assert_rank!(hand!["9d", "8d", "7d", "6d", "4d"], Rank::Flush);
assert_rank!(hand!["Kd", "Kh", "Kc", "Qs", "Jd"], Rank::ThreeOfAKind);
assert_rank!(hand!["Kd", "Kh", "Kc", "Qs", "Jd"], Rank::ThreeOfAKind);
assert_rank!(hand!["Kd", "Kh", "Qc", "Js", "10d"], Rank::OnePair);
assert_rank!(hand!["9d", "8h", "6c", "5s", "4d"], Rank::HighCard(newcard!["9d"]));
}
#[test]
fn rank_high_card_returns_highest() {
let mut hand1 = hand!["Ah", "Kd", "Qc", "Js", "9h"];
let rank1 = MatchHandEvaluator::match_eval(&mut hand1);
assert_eq!(rank1, Rank::HighCard(newcard!["Ah"]));
let mut hand2 = hand!["Kh", "Qd", "Jc", "10s", "8h"];
let rank2 = MatchHandEvaluator::match_eval(&mut hand2);
assert_eq!(rank2, Rank::HighCard(newcard!["Kh"]));
let mut hand3 = hand!["9h", "7d", "5c", "3s", "2h"];
let rank3 = MatchHandEvaluator::match_eval(&mut hand3);
assert_eq!(rank3, Rank::HighCard(newcard!["9h"]));
let mut hand4 = hand!["Ah", "Kd", "Jc", "9s", "7h"];
let rank4 = MatchHandEvaluator::match_eval(&mut hand4);
assert_eq!(rank4, Rank::HighCard(newcard!["Ah"]));
}
#[test]
fn rank_pattern_matching_priority() {
assert_rank!(hand!["Ad", "Ah", "Ac", "Ks", "Kd"], Rank::FullHouse);
assert_rank!(hand!["Ad", "Ah", "Ac", "As", "Kd"], Rank::FourOfAKind);
assert_rank!(hand!["9d", "8d", "7d", "6d", "5d"], Rank::StraightFlush);
assert_rank!(hand!["Ad", "Kd", "Qd", "Jd", "10d"], Rank::RoyalFlush);
assert_rank!(hand!["Kd", "Kh", "Jc", "Js", "10d"], Rank::TwoPair);
}
#[test]
fn rank_all_low_cards() {
assert_rank!(hand!["6d", "5h", "4c", "3s", "2d"], Rank::Straight);
assert_rank!(hand!["5d", "5h", "4c", "3s", "2d"], Rank::OnePair);
assert_rank!(hand!["6d", "5h", "4c", "4s", "2d"], Rank::OnePair);
assert_rank!(hand!["5d", "5h", "4c", "4s", "2d"], Rank::TwoPair);
assert_rank!(hand!["5d", "5h", "5c", "4s", "2d"], Rank::ThreeOfAKind);
}
#[test]
fn rank_all_high_cards() {
assert_rank!(hand!["Ad", "Kh", "Qc", "Js", "10d"], Rank::Straight);
assert_rank!(hand!["Ad", "Ah", "Kc", "Qs", "Jd"], Rank::OnePair);
assert_rank!(hand!["Kd", "Kh", "Qc", "Qs", "Jd"], Rank::TwoPair);
assert_rank!(hand!["Qd", "Qh", "Qc", "Ks", "Jd"], Rank::ThreeOfAKind);
assert_rank!(hand!["Kd", "Kh", "Kc", "Ks", "Ad"], Rank::FourOfAKind);
}
#[test]
fn rank_wheel_variations() {
assert_rank!(hand!["5c", "4h", "3d", "2s", "Ah"], Rank::Straight);
assert_rank!(hand!["5d", "4d", "3d", "2d", "Ad"], Rank::StraightFlush);
assert_rank!(hand!["Ad", "6h", "5c", "4s", "3d"], Rank::HighCard(newcard!["Ad"]));
assert_rank!(hand!["4c", "3h", "2d", "As", "Kh"], Rank::HighCard(newcard!["As"]));
}
}