feat!: remove Rc constraint in Evaluator and add simplification for evals
authorMA Beaudet <ma@beaudet.xyz>
Tue, 9 Nov 2021 10:19:44 +0000 (11:19 +0100)
committerMA Beaudet <ma@beaudet.xyz>
Tue, 9 Nov 2021 10:19:44 +0000 (11:19 +0100)
`cards` and `table` are not Rc anymore, u32 operations are cheap so it
should be ok.

examples/simulation.rs
src/evaluator.rs
src/lib.rs

index 0825ad3..55260bd 100644 (file)
@@ -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() {
index b3bd500..bd2c270 100644 (file)
@@ -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<Rc<Cards>>,
-    pub table: Rc<Cards>,
+    hands: Vec<Cards>,
+    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<Cards>) -> &'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<HashMap<usize, u16>, MyError> {
-        let hands: Vec<Cards> = 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<HashMap<usize, u16>, MyError> {
+        fn parse_args(evaluator: &Evaluator) -> Result<(Vec<Vec<u32>>, Vec<u32>), MyError> {
+            let hands: Vec<Vec<u32>> = 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<Card>| 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<u16, MyError> {
+        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<u16, MyError> {
+        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 {
index 0297cc6..e56fa0c 100644 (file)
@@ -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());
     }
 }