use std::collections::{HashMap, HashSet};
+use std::convert::TryInto;
use itertools::Itertools;
use rand::seq::IteratorRandom;
use crate::card::{Card, Suit, FIFTY_TWO_CARD_DECK};
use crate::seats::Seats;
use crate::username::Username;
+use crate::util::max::IteratorMaxItems;
use super::super::{Action, ActionError, Game, UserAction, ValidatedUserAction};
+use super::classify::rank_7_card_hand;
+
#[derive(Copy, Clone, Debug)]
enum State {
NotStarted,
self.stacks.insert(username, chips);
self.seats.add_player(seat, username)
}
- (State::NotStarted, Action::Leave) => {
+ (State::NotStarted, Action::Leave) | (_, Action::KnockedOut) => {
self.stacks.remove(&username);
self.seats.remove_player(username)
}
self.pot += *self.bets.entry(username).or_default();
self.bets.remove(&username);
self.players.remove(&username);
+ self.hands.remove(&username);
self.active = self.seats.player_after_in(username, &self.players);
Ok(())
}
} else if self.seats.players_len() == 1 {
self.players.iter().next()
.map(|&username| ValidatedUserAction(UserAction{username, action: Action::WinGame}))
+ } else if let Some((&username, _)) = self.stacks.iter().find(|&(_, &stack)| stack == 0) {
+ Some(ValidatedUserAction(UserAction{username, action: Action::KnockedOut}))
+ } else if let Some(username) = self.dealer.and_then(|dealer| self.seats.player_after(dealer)) {
+ Some(ValidatedUserAction(UserAction{username, action: Action::NextToDeal}))
} else {
- self.stacks.iter().find(|&(_, &stack)| stack == 0)
- .map(|(&username, _)| ValidatedUserAction(UserAction{username, action: Action::KnockedOut}))
+ error!("Logic error: no dealer could be chosen: {:#?}", self);
+ None
}
} else {
None
self.deck.iter().choose(&mut rng).map(|&card|
ValidatedUserAction(UserAction{username, action: Action::CommunityCard{card}})))
}
+ State::Showdown if self.pot == 0 => {
+ if let Some((&username, _)) = self.stacks.iter().find(|&(_, &stack)| stack == 0) {
+ Some(ValidatedUserAction(UserAction{username, action: Action::KnockedOut}))
+ } else if let Some(username) = self.dealer.and_then(|dealer| self.seats.player_after(dealer)) {
+ Some(ValidatedUserAction(UserAction{username, action: Action::NextToDeal}))
+ } else {
+ error!("Logic error: no dealer could be chosen: {:#?}", self);
+ None
+ }
+ }
State::Showdown => {
- todo!()
+ let winning_hands = self.hands.iter()
+ .map(|(&username, hand)| (username, hand.iter().chain(self.community.iter()).cloned().collect::<Vec<_>>()))
+ .filter_map(|(username, cards)| cards.try_into().ok().map(rank_7_card_hand).map(|hand| (username, hand)))
+ .max_items_by_key(|(_, hand)| *hand);
+ winning_hands.first()
+ .map(|&(username, _)| ValidatedUserAction(UserAction{username, action: Action::WinHand{chips: self.pot / winning_hands.len() as u64}}))
}
State::Completed => None,
}
--- /dev/null
+use std::cmp::Ordering;
+
+pub trait IteratorMaxItems : Iterator + Sized {
+ fn max_items(self) -> Vec<Self::Item>
+ where
+ Self::Item: Ord,
+ {
+ self.max_items_by(Ord::cmp)
+ }
+
+ fn max_items_by_key<B: Ord, F>(self, f: F) -> Vec<Self::Item>
+ where
+ F: FnMut(&Self::Item) -> B
+ {
+ #[inline]
+ fn key<T, B>(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) {
+ move |x| (f(&x), x)
+ }
+
+ #[inline]
+ fn compare<T, B: Ord>((x_p, _): &(B, T), (y_p, _): &(B, T)) -> Ordering {
+ x_p.cmp(y_p)
+ }
+
+ self.map(key(f))
+ .max_items_by(compare)
+ .into_iter()
+ .map(|(_, x)| x)
+ .collect()
+ }
+
+ fn max_items_by<F>(self, compare: F) -> Vec<Self::Item>
+ where
+ F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+ {
+ #[inline]
+ fn fold<T>(mut compare: impl FnMut(&T, &T) -> Ordering) -> impl FnMut(Vec<T>, T) -> Vec<T> {
+ move |mut vec, t| {
+ let ordering = match vec.first() {
+ Some(item) => compare(&t, item),
+ None => Ordering::Greater,
+ };
+ match ordering {
+ Ordering::Greater => vec![t],
+ Ordering::Equal => {vec.push(t); vec},
+ Ordering::Less => vec,
+ }
+ }
+ }
+
+ self.fold(Vec::new(), fold(compare))
+ }
+}
+
+impl<T> IteratorMaxItems for T where T: Iterator{}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn empty_max_items() {
+ assert_eq!(0, [(); 0].iter().max_items().len());
+ }
+
+ #[test]
+ fn single_max_item() {
+ assert_eq!(vec![&1], [1].iter().max_items());
+ assert_eq!(vec![&5], [1, 2, 3, 4, 5].iter().max_items());
+ assert_eq!(vec![&5], [5, 4, 3, 2, 1].iter().max_items());
+ assert_eq!(vec![&5], [3, 2, 5, 4, 1].iter().max_items());
+ }
+
+ #[test]
+ fn multiple_max_items() {
+ assert_eq!(vec![&1, &1], [1, 1].iter().max_items());
+ assert_eq!(vec![&5, &5], [5, 2, 3, 4, 5].iter().max_items());
+ assert_eq!(vec![&5, &5], [1, 2, 5, 5, 4].iter().max_items());
+ assert_eq!(vec![&5, &5, &5, &5], [5, 2, 5, 5, 5].iter().max_items());
+ }
+
+ #[test]
+ fn multiple_max_items_by_key() {
+ assert_eq!(vec![&("a", 5), &("e", 5)], [("a", 5), ("b", 4), ("c", 3), ("d", 2), ("e", 5)].iter().max_items_by_key(|&&(_, x)| x));
+ }
+}