From: MA Beaudet Date: Tue, 9 Nov 2021 10:19:44 +0000 (+0100) Subject: feat!: remove Rc constraint in Evaluator and add simplification for evals X-Git-Url: https://git.beaudet.xyz/?a=commitdiff_plain;h=fe8f880deaacc72e558c302ed8e99eea1b5ac97b;p=poker-eval.git feat!: remove Rc constraint in Evaluator and add simplification for evals `cards` and `table` are not Rc anymore, u32 operations are cheap so it should be ok. --- diff --git a/examples/simulation.rs b/examples/simulation.rs index 0825ad3..55260bd 100644 --- a/examples/simulation.rs +++ b/examples/simulation.rs @@ -6,15 +6,16 @@ fn main() { let mut deck = Deck::new(); deck.shuffle(); - let player_hands = deck.get_with_rules_and_player_nb(&rules, 4).unwrap(); - let table = deck.get(5).unwrap(); + let player_hands = deck.deal_with_rules_and_player_nb(&rules, 4).unwrap(); + let table = deck.deal(5).unwrap(); let result = Evaluator::new() - .with_rules(&rules) - .with_algorithm(&algorithm) - .with_hands(&player_hands) - .with_table(&table) - .eval(); + .rules(&rules) + .algorithm(&algorithm) + .hands(&player_hands) + .table(&table) + .eval() + .unwrap(); println!("Poker evaluator simulation\n"); for (i, hand) in player_hands.iter().enumerate() { diff --git a/src/evaluator.rs b/src/evaluator.rs index b3bd500..bd2c270 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,7 +1,7 @@ -use std::{collections::HashMap, rc::Rc}; +use std::collections::HashMap; use crate::{ - card::{Card, Cards}, + card::Cards, constants::{FLUSHES, HASH_ADJUST, HASH_VALUES, PERM7, UNIQUE5}, Algorithm, MyError, Rules, }; @@ -10,8 +10,8 @@ use crate::{ pub struct Evaluator { pub rules: Rules, algorithm: Algorithm, - hands: Vec>, - pub table: Rc, + hands: Vec, + pub table: Cards, } impl Default for Evaluator { @@ -40,72 +40,63 @@ impl Evaluator { self } - self.hands.push(Rc::new(hand.clone())); pub fn hand<'a>(&'a mut self, hand: &Cards) -> &'a mut Evaluator { + self.hands.push(hand.clone()); self } - for hand in hands { - self.hands.push(Rc::new(hand.clone())) - } pub fn hands<'a>(&'a mut self, hands: &Vec) -> &'a mut Evaluator { + self.hands.extend_from_slice(hands); self } - self.table = Rc::new(table.clone()); pub fn table<'a>(&'a mut self, table: &Cards) -> &'a mut Evaluator { + self.table = table.clone(); self } - pub fn eval(&mut self) -> Result, MyError> { - let hands: Vec = self - .hands - .clone() - .into_iter() - .map(|h| Cards(h.as_ref().0.clone())) - .collect(); - if hands.is_empty() { - return Err(MyError::NoHands); - } - let table = &self.table.as_ref().0.clone(); - if hands.len() == 5 && matches!(self.rules, Rules::Holdem) { - println!("Warning: card number in hands does not match the given Poker rules"); - println!("Switching rules to classic"); - self.rules = Rules::Classic; - } - let hand = &hands.get(0).unwrap().0; - if hand.len() == 2 && matches!(self.rules, Rules::Classic) { - println!("Warning: card number in hands does not match the given Poker rules"); - println!("Switching rules to holdem"); - self.rules = Rules::Holdem; - } - if matches!(self.rules, Rules::Holdem) && table.len() == 0 { - return Err(MyError::NoTable); + pub fn eval(&self) -> Result, MyError> { + fn parse_args(evaluator: &Evaluator) -> Result<(Vec>, Vec), MyError> { + let hands: Vec> = evaluator + .hands + .clone() + .into_iter() + .map(|cards| cards.into_inner()) + .collect(); + if hands.is_empty() { + return Err(MyError::NoHands); + } + let table = evaluator.table.clone().into_inner(); + if hands[0].len() == 5 && matches!(evaluator.rules, Rules::Holdem) { + println!("Warning: card number in hands does not match the given Poker rules"); + return Err(MyError::UnmatchedCardsTable); + } + if hands[0].len() == 2 && matches!(evaluator.rules, Rules::Classic) { + println!("Warning: card number in hands does not match the given Poker rules"); + return Err(MyError::UnmatchedCardsTable); + } + if matches!(evaluator.rules, Rules::Holdem) && table.len() == 0 { + return Err(MyError::NoTable); + } + Ok((hands, table)) } + + let (hands, table) = parse_args(self)?; + let result = match self.rules { - Rules::Classic => self - .hands + Rules::Classic => hands .iter() - .map(|player| match self.algorithm { - Algorithm::CactusKev => { - self.eval_5hand(&player.as_ref().0[..5].try_into().unwrap()) - } + .map(|h| match self.algorithm { + Algorithm::CactusKev => self.eval_5hand(h.as_slice()).unwrap(), }) .enumerate() .collect(), - Rules::Holdem => self - .hands - .iter() - .map(|h| { - h.as_ref() - .0 - .iter() - .chain(self.table.as_ref().0.iter()) - .cloned() - .collect() - }) - .map(|player: Vec| match self.algorithm { - Algorithm::CactusKev => self.eval_7hand(&player.try_into().unwrap()), + Rules::Holdem => hands + .into_iter() + .map(|h| match self.algorithm { + Algorithm::CactusKev => self + .eval_7hand([h, table.clone()].concat().as_slice()) + .unwrap(), }) .enumerate() .collect(), @@ -113,34 +104,37 @@ impl Evaluator { Ok(result) } - pub fn eval_5hand(&self, cards: &[Card; 5]) -> u16 { - let [c1, c2, c3, c4, c5]: &[u32; 5] = unsafe { std::mem::transmute(cards) }; - let q = (c1 | c2 | c3 | c4 | c5) >> 16; - if c1 & c2 & c3 & c4 & c5 & 0xF000 != 0 { - FLUSHES[q as usize] - } else if UNIQUE5[q as usize] != 0 { - UNIQUE5[q as usize] + pub fn eval_5hand(&self, cards: &[u32]) -> Result { + if let [c1, c2, c3, c4, c5] = cards[..5] { + let q = (c1 | c2 | c3 | c4 | c5) >> 16; + if c1 & c2 & c3 & c4 & c5 & 0xF000 != 0 { + Ok(FLUSHES[q as usize]) + } else if UNIQUE5[q as usize] != 0 { + Ok(UNIQUE5[q as usize]) + } else { + let q = (c1 & 0xff) * (c2 & 0xff) * (c3 & 0xff) * (c4 & 0xff) * (c5 & 0xff); + Ok(HASH_VALUES[Evaluator::find_fast(q) as usize]) + } } else { - let q = (c1 & 0xff) * (c2 & 0xff) * (c3 & 0xff) * (c4 & 0xff) * (c5 & 0xff); - HASH_VALUES[Evaluator::find_fast(q) as usize] + Err(MyError::ParseArrayError) } } /// Non-optimized method of determining the best five-card hand possible of seven cards. - pub fn eval_7hand(&self, cards: &[Card; 7]) -> u16 { - let mut subhand: [Card; 5] = [Card(0); 5]; + pub fn eval_7hand(&self, cards: &[u32]) -> Result { + let mut subhand: [u32; 5] = [0; 5]; let mut best = 9999; for i in 0..21 { for j in 0..5 { subhand[j] = cards[..][PERM7[i][j] as usize]; } - let q = self.eval_5hand(&subhand); + let q = self.eval_5hand(&subhand)?; if q < best { best = q; } } - best + Ok(best) } fn find_fast(mut u: u32) -> u16 { diff --git a/src/lib.rs b/src/lib.rs index 0297cc6..e56fa0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,8 @@ pub enum MyError { IndexError, NoHands, NoTable, + UnmatchedCardsTable, + ParseArrayError, } impl std::error::Error for MyError {} @@ -452,11 +454,9 @@ mod tests { #[test] fn evaluate_without_cards() { let mut deck = Deck::default(); - let player_hands = deck - .get_with_rules_and_player_nb(&Rules::Holdem, 4) - .unwrap(); + let player_hands = deck.deal_with_rules_and_player_nb(&Rules::Holdem, 4).unwrap(); let mut evaluator = Evaluator::new(); assert!(evaluator.eval().is_err()); - assert!(evaluator.with_hands(&player_hands).eval().is_err()); + assert!(evaluator.hands(&player_hands).eval().is_err()); } }