use std::cmp::Ordering;
use std::collections::HashMap;
use crate::card::{Card, Rank};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum HandRank {
HighCard,
Pair,
TwoPair,
ThreeOfAKind,
Straight,
Flush,
FullHouse,
FourOfAKind,
StraightFlush,
RoyalFlush,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HandEvaluation {
pub rank: HandRank,
pub hand_rank_value: u32,
pub kickers: Vec<Rank>,
}
impl PartialOrd for HandEvaluation {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for HandEvaluation {
fn cmp(&self, other: &Self) -> Ordering {
match self.rank.cmp(&other.rank) {
Ordering::Equal => {
match self.hand_rank_value.cmp(&other.hand_rank_value) {
Ordering::Equal => {
for (s, o) in self.kickers.iter().zip(other.kickers.iter()) {
let cmp = s.cmp(o);
if cmp != Ordering::Equal {
return cmp;
}
}
Ordering::Equal
}
ordering => ordering,
}
}
ordering => ordering,
}
}
}
pub fn evaluate_hand(cards: &[Card]) -> HandEvaluation {
if cards.len() < 5 {
return HandEvaluation {
rank: HandRank::HighCard,
hand_rank_value: 0,
kickers: cards.iter().map(|c| c.rank).collect(),
};
}
let is_flush = cards.iter().all(|c| c.suit == cards[0].suit);
let mut rank_counts: HashMap<Rank, usize> = HashMap::new();
for card in cards {
*rank_counts.entry(card.rank).or_insert(0) += 1;
}
let mut rank_groups: Vec<(Rank, usize)> = rank_counts.into_iter().collect();
rank_groups.sort_by(|(rank_a, count_a), (rank_b, count_b)| {
count_b.cmp(count_a).then_with(|| rank_b.cmp(rank_a))
});
let mut ranks: Vec<Rank> = cards.iter().map(|c| c.rank).collect();
ranks.sort_by(|a, b| b.cmp(a)); ranks.dedup();
let is_straight = check_straight(&ranks);
let is_wheel = ranks.contains(&Rank::Ace) &&
ranks.contains(&Rank::Five) &&
ranks.contains(&Rank::Four) &&
ranks.contains(&Rank::Three) &&
ranks.contains(&Rank::Two);
let (hand_rank, hand_rank_value, kickers) = if is_flush && is_straight {
if ranks[0] == Rank::Ace && ranks[1] == Rank::King {
(HandRank::RoyalFlush, ranks[0].value() as u32, Vec::new())
} else {
(HandRank::StraightFlush, ranks[0].value() as u32, Vec::new())
}
} else if rank_groups[0].1 == 4 {
let quad_rank = rank_groups[0].0;
let kicker = if rank_groups.len() > 1 { vec![rank_groups[1].0] } else { Vec::new() };
(HandRank::FourOfAKind, quad_rank.value() as u32, kicker)
} else if rank_groups[0].1 == 3 && rank_groups.get(1).map_or(false, |&(_, count)| count == 2) {
let trip_rank = rank_groups[0].0;
let pair_rank = rank_groups[1].0;
(HandRank::FullHouse, (trip_rank.value() as u32) * 100 + pair_rank.value() as u32, Vec::new())
} else if is_flush {
let kickers = ranks.iter().take(5).cloned().collect();
(HandRank::Flush, ranks[0].value() as u32, kickers)
} else if is_straight || is_wheel {
let high_card = if is_wheel { Rank::Five } else { ranks[0] };
(HandRank::Straight, high_card.value() as u32, Vec::new())
} else if rank_groups[0].1 == 3 {
let trip_rank = rank_groups[0].0;
let kickers: Vec<Rank> = rank_groups.iter()
.skip(1)
.take(2)
.map(|&(rank, _)| rank)
.collect();
(HandRank::ThreeOfAKind, trip_rank.value() as u32, kickers)
} else if rank_groups[0].1 == 2 && rank_groups.get(1).map_or(false, |&(_, count)| count == 2) {
let high_pair = rank_groups[0].0;
let low_pair = rank_groups[1].0;
let kicker = if rank_groups.len() > 2 { vec![rank_groups[2].0] } else { Vec::new() };
(HandRank::TwoPair, (high_pair.value() as u32) * 100 + low_pair.value() as u32, kicker)
} else if rank_groups[0].1 == 2 {
let pair_rank = rank_groups[0].0;
let kickers: Vec<Rank> = rank_groups.iter()
.skip(1)
.take(3)
.map(|&(rank, _)| rank)
.collect();
(HandRank::Pair, pair_rank.value() as u32, kickers)
} else {
let kickers = ranks.iter().take(5).cloned().collect();
(HandRank::HighCard, ranks[0].value() as u32, kickers)
};
HandEvaluation {
rank: hand_rank,
hand_rank_value,
kickers,
}
}
fn check_straight(ranks: &[Rank]) -> bool {
if ranks.len() < 5 {
return false;
}
let mut consecutive = 1;
let mut max_consecutive = 1;
for i in 1..ranks.len() {
if ranks[i].value() == ranks[i-1].value() - 1 {
consecutive += 1;
max_consecutive = max_consecutive.max(consecutive);
} else if ranks[i].value() != ranks[i-1].value() {
consecutive = 1;
}
}
if ranks.contains(&Rank::Ace) &&
ranks.contains(&Rank::Five) &&
ranks.contains(&Rank::Four) &&
ranks.contains(&Rank::Three) &&
ranks.contains(&Rank::Two) {
return true;
}
max_consecutive >= 5
}
pub fn find_best_hand(hole_cards: &[Card], community_cards: &[Card]) -> (Vec<Card>, HandEvaluation) {
let all_cards: Vec<Card> = hole_cards.iter().chain(community_cards.iter()).cloned().collect();
let mut best_hand = Vec::new();
let mut best_eval = HandEvaluation {
rank: HandRank::HighCard,
hand_rank_value: 0,
kickers: Vec::new(),
};
for i in 0..all_cards.len() {
for j in i+1..all_cards.len() {
for k in j+1..all_cards.len() {
for l in k+1..all_cards.len() {
for m in l+1..all_cards.len() {
let hand = vec![
all_cards[i], all_cards[j], all_cards[k],
all_cards[l], all_cards[m]
];
let eval = evaluate_hand(&hand);
if eval > best_eval {
best_hand = hand;
best_eval = eval;
}
}
}
}
}
}
(best_hand, best_eval)
}
pub fn find_best_omaha_hand(hole_cards: &[Card], community_cards: &[Card]) -> (Vec<Card>, HandEvaluation) {
let mut best_hand = Vec::new();
let mut best_eval = HandEvaluation {
rank: HandRank::HighCard,
hand_rank_value: 0,
kickers: Vec::new(),
};
for i in 0..hole_cards.len() {
for j in i+1..hole_cards.len() {
let hole_combo = vec![hole_cards[i], hole_cards[j]];
for a in 0..community_cards.len() {
for b in a+1..community_cards.len() {
for c in b+1..community_cards.len() {
let comm_combo = vec![
community_cards[a],
community_cards[b],
community_cards[c]
];
let hand = vec![
hole_combo[0], hole_combo[1],
comm_combo[0], comm_combo[1], comm_combo[2]
];
let eval = evaluate_hand(&hand);
if eval > best_eval {
best_hand = hand;
best_eval = eval;
}
}
}
}
}
}
(best_hand, best_eval)
}