pub suit: Suit,
}
+impl Card {
+ fn from_trailing_zeros(i: u32) -> Option<Self> {
+ Some(Card {
+ rank: match i / 4 + 2 {
+ 2 => Rank::Two,
+ 3 => Rank::Three,
+ 4 => Rank::Four,
+ 5 => Rank::Five,
+ 6 => Rank::Six,
+ 7 => Rank::Seven,
+ 8 => Rank::Eight,
+ 9 => Rank::Nine,
+ 10 => Rank::Ten,
+ 11 => Rank::Jack,
+ 12 => Rank::Queen,
+ 13 => Rank::King,
+ 14 => Rank::Ace,
+ _ => return None,
+ },
+ suit: match i % 4 {
+ 0 => Suit::Clubs,
+ 1 => Suit::Diamonds,
+ 2 => Suit::Hearts,
+ 3 => Suit::Spades,
+ _ => return None,
+ },
+ })
+ }
+
+ fn repr(&self) -> u64 {
+ 1 << (self.suit as u64 + 4 * (self.rank as u64 - 2))
+ }
+}
+
impl Display for Card {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.rank, self.suit)
}
}
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
+pub struct CardSet {
+ repr: u64,
+}
+
+impl CardSet {
+ pub fn new() -> CardSet {
+ CardSet { repr: 0 }
+ }
+
+ pub fn complete_deck() -> CardSet {
+ CardSet { repr: 0b1111111111111_1111111111111_1111111111111_1111111111111 }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.repr == 0
+ }
+
+ pub fn len(&self) -> usize {
+ self.repr.count_ones() as usize
+ }
+
+ pub fn clear(&mut self) {
+ self.repr = 0;
+ }
+
+ pub fn insert(&mut self, card: Card) -> bool {
+ let card_repr = card.repr();
+ if self.repr & card_repr != 0 {
+ false
+ } else {
+ self.repr |= card_repr;
+ true
+ }
+ }
+
+ pub fn contains(&self, card: &Card) -> bool {
+ self.repr & card.repr() != 0
+ }
+
+ pub fn remove(&mut self, card: &Card) -> bool {
+ let card_repr = card.repr();
+ if self.repr & card_repr == 0 {
+ false
+ } else {
+ self.repr &= !card_repr;
+ true
+ }
+ }
+}
+
+pub struct CardSetIntoIter {
+ inner: CardSet,
+}
+
+impl IntoIterator for CardSet {
+ type IntoIter = CardSetIntoIter;
+ type Item = Card;
+
+ fn into_iter(self) -> Self::IntoIter {
+ CardSetIntoIter { inner: self }
+ }
+}
+
+impl Iterator for CardSetIntoIter {
+ type Item = Card;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.inner.repr == 0 {
+ None
+ } else {
+ let i = self.inner.repr.trailing_zeros();
+ self.inner.repr &= !(1 << i);
+ Card::from_trailing_zeros(i)
+ }
+ }
+}
+
+impl FromIterator<Card> for CardSet {
+ fn from_iter<T>(iter: T) -> Self
+ where T: IntoIterator<Item=Card>
+ {
+ let mut set = CardSet::new();
+ for card in iter {
+ set.insert(card);
+ }
+ set
+ }
+}
+
pub const TWO_OF_CLUBS: Card = Card { rank: Two, suit: Clubs };
pub const THREE_OF_CLUBS: Card = Card { rank: Three, suit: Clubs };
pub const FOUR_OF_CLUBS: Card = Card { rank: Four, suit: Clubs };
use std::collections::{HashMap, HashSet};
-use crate::card::{Card, FIFTY_TWO_CARD_DECK, Rank};
+use crate::card::{Card, CardSet, Rank};
use crate::rng::{Seed, WaveRng};
use crate::seats::Seats;
use crate::username::Username;
seats: Seats,
dealer: Option<Username>,
receiver: Option<Username>,
- deck: HashSet<Card>,
- hands: HashMap<Username, HashSet<Card>>,
+ deck: CardSet,
+ hands: HashMap<Username, CardSet>,
turn_up: Option<Card>,
- box_cards: HashSet<Card>,
- revealed: HashSet<Card>,
+ box_cards: CardSet,
+ revealed: CardSet,
pegging_cards: Vec<(Username, Card)>,
used_pegging_cards: Vec<(Username, Card)>,
players_still_in: HashSet<Username>,
seats: Seats::new(),
dealer: None,
receiver: None,
- deck: FIFTY_TWO_CARD_DECK.iter().cloned().collect(),
+ deck: CardSet::complete_deck(),
hands: HashMap::new(),
turn_up: None,
- box_cards: HashSet::new(),
- revealed: HashSet::new(),
+ box_cards: CardSet::new(),
+ revealed: CardSet::new(),
pegging_cards: Vec::new(),
used_pegging_cards: Vec::new(),
players_still_in: HashSet::new(),
}
fn hand_size(&self, username: Username) -> usize {
- self.hands.get(&username).map_or(0, HashSet::len)
+ self.hands.get(&username).map_or(0, CardSet::len)
}
fn all_hands_are_empty(&self) -> bool {
- self.hands.values().all(HashSet::is_empty)
+ self.hands.values().all(CardSet::is_empty)
}
fn all_hands_dealt(&self) -> bool {
- self.hands.values().map(HashSet::len).all(|len| len == 6)
+ self.hands.values().map(CardSet::len).all(|len| len == 6)
}
fn pegging_total(&self) -> u32 {
fn hand_has_playable_pegging_card(&self, username: Username) -> bool {
let total = self.pegging_total();
- self.hands.get(&username).map_or(false, |hand| hand.iter().any(|card| total + value(card.rank) <= 31))
+ self.hands.get(&username).map_or(false, |hand| hand.into_iter().any(|card| total + value(card.rank) <= 31))
}
fn next_player_still_in(&self) -> Option<Username> {
}
fn unrevealed_card(&self, username: Username) -> Option<Card> {
- self.hands.get(&username).and_then(|hand| hand.iter().filter(|card| !self.revealed.contains(*card)).min().copied())
+ self.hands.get(&username).and_then(|hand| hand.into_iter().filter(|card| !self.revealed.contains(card)).min())
}
fn four_card_hand(&self, username: Username) -> Option<[Card; 4]> {
self.hands.get(&username).and_then(|hand| {
let hand: Vec<_> = hand.into_iter().collect();
if let [a, b, c, d] = hand[..] {
- Some([*a, *b, *c, *d])
+ Some([a, b, c, d])
} else {
None
}
}
fn unrevealed_box_card(&self) -> Option<Card> {
- self.box_cards.iter().filter(|card| !self.revealed.contains(*card)).min().copied()
+ self.box_cards.into_iter().filter(|card| !self.revealed.contains(card)).min()
}
fn four_card_box(&self) -> Option<[Card; 4]> {
- let hand: Vec<_> = self.box_cards.iter().collect();
+ let hand: Vec<_> = self.box_cards.into_iter().collect();
if let [a, b, c, d] = hand[..] {
- Some([*a, *b, *c, *d])
+ Some([a, b, c, d])
} else {
None
}
}
fn winner(&self) -> Option<Username> {
- self.points.iter().filter(|&(_, points)| *points >= self.settings.target_score).next().map(|(username, _)| *username)
+ self.points.iter().find(|&(_, points)| *points >= self.settings.target_score).map(|(username, _)| *username)
}
}
(State::NotStarted, Action::Leave) => self.seats.remove_player(username),
(_, Action::NextToDeal) => {
self.dealer = Some(username);
- self.deck = FIFTY_TWO_CARD_DECK.iter().cloned().collect();
+ self.deck = CardSet::complete_deck();
self.turn_up = None;
self.hands.clear();
self.receiver = self.seats.player_after(username);
match self.next_player_still_in() {
None => {
self.state = State::ScoringPegging;
- self.used_pegging_cards.extend(self.pegging_cards.drain(..));
+ self.used_pegging_cards.append(&mut self.pegging_cards);
self.players_still_in = self.hands.iter().filter(|(_, cards)| !cards.is_empty()).map(|(&username, _)| username).collect();
},
active => self.active = active,
(State::ScoringPegging, Action::Score { points, .. }) => {
*self.points.entry(username).or_default() += points as u32;
if self.next_player_still_in().is_none() || self.pegging_total() == 31 {
- self.used_pegging_cards.extend(self.pegging_cards.drain(..));
+ self.used_pegging_cards.append(&mut self.pegging_cards);
self.players_still_in = self.hands.iter().filter(|(_, cards)| !cards.is_empty()).map(|(&username, _)| username).collect();
}
if self.player_has_won(username) {
},
State::Dealing => {
if let Some(username) = self.receiver {
- let card = rng.choose_from(&self.deck).cloned();
+ let card = rng.choose_from(self.deck);
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::ReceiveCard { card } }))
} else if let Some(username) = self.dealer {
if self.seats.players_len() == 3 && self.box_cards.is_empty() {
- if let Some(&card) = rng.choose_from(&self.deck) {
+ if let Some(card) = rng.choose_from(self.deck) {
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::PutInBox { card: Some(card) } }))
} else {
error!("Expected to deal a card but none were left in deck");
State::Choosing => DealerAction::WaitForPlayer,
State::TurnUp => {
if let Some(username) = self.dealer {
- if let Some(&card) = rng.choose_from(&self.deck) {
+ if let Some(card) = rng.choose_from(self.deck) {
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::CommunityCard { card } }))
} else {
error!("Expected to deal a card but none were left in deck");
use itertools::Itertools;
-use crate::card::{Card, FIFTY_TWO_CARD_DECK};
+use crate::card::{Card, CardSet};
use crate::rng::{Seed, WaveRng};
use crate::seats::Seats;
use crate::username::Username;
state: State,
seats: Seats,
stacks: HashMap<Username, u64>,
- deck: HashSet<Card>,
- hands: HashMap<Username, HashSet<Card>>,
- community: HashSet<Card>,
+ deck: CardSet,
+ hands: HashMap<Username, CardSet>,
+ community: CardSet,
dealer: Option<Username>,
receiver: Option<Username>,
active: Option<Username>,
pot: u64,
level: Level,
ghosts: HashMap<Username, u8>,
- revealed: HashSet<(Username, Card)>,
+ revealed: CardSet,
}
impl TexasHoldEm {
state: State::NotStarted,
seats: Seats::new(),
stacks: HashMap::new(),
- deck: FIFTY_TWO_CARD_DECK.iter().cloned().collect(),
+ deck: CardSet::complete_deck(),
hands: HashMap::new(),
- community: HashSet::new(),
+ community: CardSet::new(),
dealer: None,
receiver: None,
active: None,
pot: 0,
level,
ghosts: HashMap::new(),
- revealed: HashSet::new(),
+ revealed: CardSet::new(),
}
}
if !self.settings.hide_cards && (self.players_able_to_bet() <= 1 || matches!(self.state, State::Showdown)) {
for &username in self.in_hand.iter().sorted() {
if let Some(hand) = self.hands.get(&username) {
- for &card in hand.iter().sorted() {
- if !self.revealed.contains(&(username, card)) {
+ for card in hand.into_iter().sorted() {
+ if !self.revealed.contains(&card) {
return Some((username, card));
}
}
}
self.remove_ghosts()?;
self.dealer = Some(username);
- self.deck = FIFTY_TWO_CARD_DECK.iter().cloned().collect();
+ self.deck = CardSet::complete_deck();
self.hands.clear();
self.community.clear();
self.active = None;
Ok(())
}
(_, Action::RevealCard { card }) => {
- self.revealed.insert((username, card));
+ self.revealed.insert(card);
Ok(())
}
(State::Dealing, Action::EndDeal) => {
},
State::Dealing => {
if let Some(username) = self.receiver {
- let card = rng.choose_from(&self.deck).cloned();
+ let card = rng.choose_from(self.deck);
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::ReceiveCard { card } }))
} else if let Some(username) = self.dealer {
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::EndDeal }))
if let Some((username, card)) = self.next_card_to_reveal() {
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::RevealCard { card } }))
} else if let Some(username) = self.dealer {
- if let Some(&card) = rng.choose_from(&self.deck) {
+ if let Some(card) = rng.choose_from(self.deck) {
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::CommunityCard { card } }))
} else {
error!("Dealing community card but there are no cards left in deck: {:#?}", self);
.hands
.iter()
.sorted_by_key(|&(username, _)| username)
- .map(|(&username, hand)| (username, hand.iter().chain(self.community.iter()).cloned().collect::<Vec<_>>()))
+ .map(|(&username, hand)| (username, hand.into_iter().chain(self.community.into_iter()).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);
info!("Showdown: community: {:?}", self.community);
use std::collections::{HashMap, HashSet};
-use crate::card::{Card, Suit, FIFTY_TWO_CARD_DECK};
+use crate::card::{Card, CardSet, Suit};
use crate::rng::{Seed, WaveRng};
use crate::seats::Seats;
use crate::username::Username;
dealer: Option<Username>,
receiver: Option<Username>,
call: Option<Username>,
- deck: HashSet<Card>,
- hands: HashMap<Username, HashSet<Card>>,
+ deck: CardSet,
+ hands: HashMap<Username, CardSet>,
trick: HashMap<Username, Card>,
active: Option<Username>,
tricks_won: HashMap<Username, u32>,
dealer: None,
receiver: None,
call: None,
- deck: FIFTY_TWO_CARD_DECK.iter().cloned().collect(),
+ deck: CardSet::complete_deck(),
hands: HashMap::new(),
trick: HashMap::new(),
active: None,
}
fn hand_contains_suit(&self, username: Username, suit: Suit) -> bool {
- self.hands.get(&username).map_or(false, |hand| hand.iter().any(|card| card.suit == suit))
+ self.hands.get(&username).map_or(false, |hand| hand.into_iter().any(|card| card.suit == suit))
}
fn trick_winner(&self) -> Option<Username> {
}
fn cards_by_player(&self) -> impl Iterator<Item = (Username, Card)> + '_ {
- self.hands.iter().flat_map(|(username, hand)| hand.iter().map(move |card| (*username, *card)))
+ self.hands.iter().flat_map(|(username, hand)| hand.into_iter().map(move |card| (*username, card)))
}
fn all_hands_dealt(&self) -> bool {
(State::NotStarted, Action::Leave) => self.seats.remove_player(username),
(_, Action::NextToDeal) => {
self.dealer = Some(username);
- self.deck = FIFTY_TWO_CARD_DECK.iter().cloned().collect();
+ self.deck = CardSet::complete_deck();
self.hands.clear();
self.receiver = self.seats.player_after(username);
self.trump_card = None;
self.call = self.winners.iter().next().cloned();
} else {
self.receiver = self.dealer.and_then(|dealer| self.seats.player_after_where(dealer, |username| self.winners.contains(&username)));
- self.deck = FIFTY_TWO_CARD_DECK.iter().cloned().collect();
+ self.deck = CardSet::complete_deck();
self.hands.clear();
self.trump_card = None;
self.state = State::CutForCall;
(State::CutForCall, Action::CutCard { card }) => {
self.deck.remove(&card);
self.hands.entry(username).or_default().insert(card);
- if self.hands.values().map(HashSet::len).sum::<usize>() == self.winners.len() {
+ if self.hands.values().map(CardSet::len).sum::<usize>() == self.winners.len() {
if let Some((username, max)) = self.cards_by_player().max_by_key(|(_, card)| card.rank) {
if self.cards_by_player().filter(|(_, card)| card.rank == max.rank).count() > 1 {
self.winners = self.cards_by_player().filter(|(_, card)| card.rank == max.rank).map(|(username, _)| username).collect();
},
State::Dealing => {
if let Some(username) = self.receiver {
- let card = rng.choose_from(&self.deck).cloned();
+ let card = rng.choose_from(self.deck);
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::ReceiveCard { card } }))
} else if let Some(username) = self.dealer {
match (self.call, self.trump_card) {
(None, None) => {
- if let Some(&card) = rng.choose_from(&self.deck) {
+ if let Some(card) = rng.choose_from(self.deck) {
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::CommunityCard { card } }))
} else {
error!("Expected to deal a card but none were left in deck");
}
State::CutForCall => {
if let Some(username) = self.receiver {
- if let Some(card) = rng.choose_from(&self.deck).cloned() {
+ if let Some(card) = rng.choose_from(self.deck) {
DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::CutCard { card } }))
} else {
error!("Expected to cut for call but there were no cards left in the deck");