use fixedbitset::FixedBitSet;
use crate::core::*;
#[derive(Debug)]
pub struct MonteCarloGame {
deck: FlatDeck,
board: Vec<Card>,
hands: Vec<Hand>,
current_offset: usize,
}
impl MonteCarloGame {
pub fn new_with_hands(hands: Vec<Hand>, board: Vec<Card>) -> Result<Self, String> {
let mut deck = Deck::default();
if board.len() > 5 {
return Err(String::from("Board passed in has more than 5 cards"));
}
for hand in &hands {
if hand.len() != 2 {
return Err(String::from("Hand passed in doesn't have 2 cards."));
}
for card in hand.iter() {
if !deck.remove(card) {
return Err(format!("Card {} was already removed from the deck.", card));
}
}
}
for card in &board {
if !deck.remove(card) {
return Err(format!("Card {} was already removed from the deck.", card));
}
}
let flat_deck: FlatDeck = deck.into();
let offset = flat_deck.len();
Ok(Self {
deck: flat_deck,
hands,
board,
current_offset: offset,
})
}
pub fn simulate(&mut self) -> (FixedBitSet, Rank) {
for c in &self.board {
for h in &mut self.hands {
h.push(*c);
}
}
let num_cards = 5 - self.board.len();
self.shuffle_if_needed();
for c in &self.deck[self.current_offset..self.current_offset + num_cards] {
for h in &mut self.hands {
h.push(*c);
}
}
self.current_offset += num_cards;
self.hands.iter().map(|h| h.rank()).enumerate().fold(
(
FixedBitSet::with_capacity(self.hands.len()),
Rank::HighCard(0),
),
|(mut found, max_rank), (idx, rank)| {
match rank.cmp(&max_rank) {
std::cmp::Ordering::Equal => {
found.set(idx, true);
(found, rank)
}
std::cmp::Ordering::Greater => {
found.clear();
found.set(idx, true);
(found, rank)
}
_ => (found, max_rank),
}
},
)
}
pub fn reset(&mut self) {
for h in &mut self.hands {
h.truncate(2);
}
}
fn shuffle_if_needed(&mut self) {
if self.current_offset + 5 > self.deck.len() {
self.current_offset = 0;
self.deck.shuffle();
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::core::Hand;
use crate::core::Rank;
#[test]
fn test_simulate_pocket_pair() {
let hands = ["AdAh", "2c2s"]
.iter()
.map(|s| Hand::new_from_str(s).unwrap())
.collect();
let mut g = MonteCarloGame::new_with_hands(hands, vec![]).unwrap();
let result = g.simulate();
assert!(result.1 >= Rank::OnePair(0));
}
#[test]
fn test_simulate_pocket_pair_with_board() {
let board = vec![
Card {
suit: Suit::Spade,
value: Value::Ace,
},
Card {
suit: Suit::Diamond,
value: Value::Three,
},
Card {
suit: Suit::Diamond,
value: Value::Four,
},
];
let hands = ["AdAh", "2c2s"]
.iter()
.map(|s| Hand::new_from_str(s).unwrap())
.collect();
let mut g = MonteCarloGame::new_with_hands(hands, board).unwrap();
let result = g.simulate();
assert!(result.1 >= Rank::ThreeOfAKind(0));
}
#[test]
fn test_simulate_set() {
let hands: Vec<Hand> = ["6d6h", "3d3h"]
.iter()
.map(|s| Hand::new_from_str(s).unwrap())
.collect();
let board: Vec<Card> = vec![
Card {
value: Value::Six,
suit: Suit::Spade,
},
Card {
value: Value::King,
suit: Suit::Diamond,
},
Card {
value: Value::Queen,
suit: Suit::Heart,
},
];
let mut g = MonteCarloGame::new_with_hands(hands, board).unwrap();
let result = g.simulate();
assert!(result.1 >= Rank::ThreeOfAKind(4));
}
}