1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::fmt;

use crate::Rank;

/// A utility enumeration type for pattern matching against the result of
/// [`Eval::class`](super::Eval::class). Each variant represents a class of
/// poker hand. Royal flush is not included, but can be matched against
/// `EvalClass:StraightFlush { high_card: Rank::Ace }` if desired.
///
/// # Example
///
/// ```
/// use poker::{cards, Evaluator, EvalClass, Rank};
///
/// let hand = cards!(
///     Ace of Clubs,
///     Two of Spades,
///     Three of Diamonds,
///     Four of Diamonds,
///     Five of Clubs,
/// );
/// let eval = Evaluator::new();
/// let result = eval.evaluate(&hand).expect("couldn't evaluate hand");
/// assert!(matches!(result.class(), EvalClass::Straight { high_rank: Rank::Five }));
/// ```
///
/// [`Eval::class`]: crate::Eval::class
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum EvalClass {
    /// A high card, or no hand.
    HighCard {
        /// The high card rank.
        high_rank: Rank,
    },
    /// A pair, two cards of the same rank
    Pair {
        /// The ranks of the pair.
        pair: Rank,
    },
    /// Two pair. two cards of the same rank and two other cards of the same but
    /// distinct rank
    TwoPair {
        /// The ranks of the first pair.
        first_pair: Rank,
        /// The ranks of the second pair.
        second_pair: Rank,
    },
    /// Three of a kind, three cards of the same rank. Sometimes called trips.
    ThreeOfAKind {
        /// The ranks of the trips.
        trips: Rank,
    },
    /// A straight, five cards in rank order. Straights go from A2345 to TJQKA.
    Straight {
        /// The rank of the highest card in the straight.
        high_rank: Rank,
    },
    /// A flush, five cards of the same suit.
    Flush {
        /// The rank of the highest card in the flush.
        high_rank: Rank,
    },
    /// A full house, one pair and one three of a kind
    FullHouse {
        /// The rank of the trips of the full house.
        trips: Rank,
        /// The rank of the pair of the full house.
        pair: Rank,
    },
    /// Four of a kind, four cards of the same rank. Sometimes called quads.
    FourOfAKind {
        /// The rank of the quads.
        quads: Rank,
    },
    /// A straight flush, like a straight but all the cards are of the same
    /// suit.
    StraightFlush {
        /// The rank of the highest card in the straight flush.
        high_rank: Rank,
    },
}

impl fmt::Display for EvalClass {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Self::HighCard { high_rank } => write!(f, "High card, {}", high_rank.as_str_name()),
            Self::Pair { pair } => write!(f, "Pair, {}", pair.as_str_name_plural()),
            Self::TwoPair {
                first_pair: high_pair,
                second_pair: low_pair,
                ..
            } => write!(
                f,
                "Two pair, {} and {}",
                high_pair.as_str_name_plural(),
                low_pair.as_str_name_plural(),
            ),
            Self::ThreeOfAKind { trips } => {
                write!(f, "Three of a kind, {}", trips.as_str_name_plural())
            }
            Self::Straight { high_rank } => {
                write!(f, "Straight, {}-high", high_rank.as_str_name())
            }
            Self::Flush { high_rank } => write!(f, "Flush, {}-high", high_rank.as_str_name()),
            Self::FullHouse { trips, pair } => write!(
                f,
                "Full house, {} over {}",
                trips.as_str_name_plural(),
                pair.as_str_name_plural()
            ),
            Self::FourOfAKind { quads, .. } => {
                write!(f, "Four of a kind, {}", quads.as_str_name_plural())
            }
            Self::StraightFlush { high_rank, .. } => match high_rank {
                Rank::Ace => write!(f, "Royal flush"),
                high_rank => write!(f, "Straight flush, {}-high", high_rank.as_str_name()),
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn eval_class_derived_ord_works() {
        let class1 = EvalClass::HighCard {
            high_rank: Rank::Ace,
        };
        let class2 = EvalClass::HighCard {
            high_rank: Rank::King,
        };
        assert!(class1 > class2);
        let class3 = EvalClass::Pair { pair: Rank::Two };
        assert!(class3 > class1);
    }
}