chore!: moved card and evaluator to mods
authorMA Beaudet <ma@beaudet.xyz>
Mon, 8 Nov 2021 13:47:47 +0000 (14:47 +0100)
committerMA Beaudet <ma@beaudet.xyz>
Mon, 8 Nov 2021 13:47:47 +0000 (14:47 +0100)
examples/simulation.rs
src/card.rs [new file with mode: 0644]
src/evaluator.rs [new file with mode: 0644]
src/lib.rs

index e3a4ad1..c3cbeab 100644 (file)
@@ -1,4 +1,4 @@
-use poker_eval::{report, Algorithm, Deck, Evaluator, Rules};
+use poker_eval::{card::Deck, evaluator::Evaluator, report, Algorithm, Rules};
 
 fn main() {
     let rules = Rules::Holdem;
diff --git a/src/card.rs b/src/card.rs
new file mode 100644 (file)
index 0000000..98423ec
--- /dev/null
@@ -0,0 +1,250 @@
+use std::str::FromStr;
+
+use crate::{constants::PRIMES, MyError, Rules};
+
+#[derive(Debug, Default, Clone)]
+pub struct Cards(pub(crate) Vec<Card>);
+
+impl FromStr for Cards {
+    type Err = MyError;
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(Self(
+            s.trim()
+                .split_whitespace()
+                .map(|c| Card::from_str(c).expect("Could not parse card from string"))
+                .collect::<Vec<_>>(),
+        ))
+    }
+}
+
+impl std::fmt::Display for Cards {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "Cards:")?;
+        for card in &self.0 {
+            write!(f, "\t{}", card.to_string())?;
+        }
+        Ok(())
+    }
+}
+
+impl From<Cards> for Vec<u32> {
+    fn from(c: Cards) -> Self {
+        c.0.into_iter().map(|c| u32::from(c)).collect()
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct Deck(pub(crate) Cards);
+
+impl Deck {
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    pub fn get_with_rules(&mut self, rules: &Rules) -> Cards {
+        let v = match rules {
+            Rules::Classic => self.get(5),
+
+            Rules::Holdem => self.get(2),
+        };
+        v.unwrap()
+    }
+
+    pub fn get_with_rules_and_player_nb(&mut self, rules: &Rules, player_nb: usize) -> Vec<Cards> {
+        (0..player_nb)
+            .into_iter()
+            .map(|_| self.get_with_rules(rules))
+            .collect()
+    }
+
+    pub fn get(&mut self, n: usize) -> Option<Cards> {
+        if self.0 .0.len() < n {
+            None
+        } else {
+            Some(Cards(self.0 .0.split_off(self.0 .0.len() - n)))
+        }
+    }
+
+    #[cfg(feature = "random")]
+    pub fn shuffle<'a>(&'a mut self) -> &'a mut Deck {
+        use rand::prelude::*;
+        let mut rng = thread_rng();
+        self.0 .0.shuffle(&mut rng);
+        self
+    }
+}
+
+impl Default for Deck {
+    fn default() -> Self {
+        let mut deck = Vec::with_capacity(52);
+        let mut suit = 0x8000;
+        for _ in 0..4 {
+            for (j, prime) in PRIMES.iter().enumerate() {
+                let prime = *prime as u32;
+                deck.push(
+                    Card::try_from(prime | ((j as u32) << 8) | suit | (1 << (16 + j))).unwrap(),
+                );
+            }
+            suit >>= 1;
+        }
+        Self(Cards(deck))
+    }
+}
+
+/// ```
+/// use poker_eval::{Card};
+/// use std::str::FromStr;
+///
+/// assert_eq!(
+///     Card::from_str("Kd").unwrap().get(),
+///     0b00001000_00000000_01001011_00100101_u32
+/// );
+#[repr(transparent)]
+#[derive(Debug, Clone, Copy)]
+pub struct Card(pub(crate) u32);
+
+impl Card {
+    pub fn get(&self) -> u32 {
+        self.0
+    }
+
+    pub fn suit(&self) -> char {
+        match (self.0 >> 12) & 0xF {
+            0b1000 => 'c',
+            0b0100 => 'd',
+            0b0010 => 'h',
+            0b0001 => 's',
+            _ => unreachable!(),
+        }
+    }
+
+    /// ```
+    /// use poker_eval::{Card};
+    /// use std::str::FromStr;
+    ///
+    /// let card = Card::from_str("Kd").unwrap();
+    /// assert_eq!(card.rank(), 'K');
+    /// ```
+    pub fn rank(&self) -> char {
+        match (self.0 >> 8) & 0xF {
+            0 => '2',
+            1 => '3',
+            2 => '4',
+            3 => '5',
+            4 => '6',
+            5 => '7',
+            6 => '8',
+            7 => '9',
+            8 => 'T',
+            9 => 'J',
+            10 => 'Q',
+            11 => 'K',
+            12 => 'A',
+            _ => unreachable!(),
+        }
+    }
+}
+
+impl From<Card> for u32 {
+    fn from(c: Card) -> Self {
+        c.0
+    }
+}
+
+impl TryFrom<u32> for Card {
+    type Error = MyError;
+
+    fn try_from(value: u32) -> Result<Self, Self::Error> {
+        Ok(Self(value))
+    }
+}
+
+/// ```
+/// use poker_eval::{Card};
+/// use std::str::FromStr;
+///
+/// let card = Card::from_str("Kd").unwrap();
+/// let other = Card::from_str("Qd").unwrap();
+/// let other2 = Card::from_str("Ks").unwrap();
+/// assert!(card > other);
+/// assert!(card <= other2);
+/// ```
+impl PartialOrd for Card {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        ((self.0 >> 8) & 0xF).partial_cmp(&((other.0 >> 8) & 0xF))
+    }
+}
+
+/// ```
+/// use poker_eval::{Card};
+/// use std::str::FromStr;
+///
+/// let card = Card::from_str("Kd").unwrap();
+/// let other = Card::from_str("Ks").unwrap();
+/// assert!(card == other);
+/// ```
+impl PartialEq for Card {
+    fn eq(&self, other: &Self) -> bool {
+        (self.0 >> 8) & 0xF == (other.0 >> 8) & 0xF
+    }
+}
+
+impl FromStr for Card {
+    type Err = MyError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let mut input = s.chars();
+        let (r, s) = (input.next(), input.next());
+        match (r, s) {
+            (None, None) => Err(MyError::InvalidLength),
+            (None, Some(_)) => Err(MyError::InvalidLength),
+            (Some(_), None) => Err(MyError::InvalidLength),
+            (Some(r), Some(s)) => {
+                let r = match r {
+                    '2' => Some(0),
+                    '3' => Some(1),
+                    '4' => Some(2),
+                    '5' => Some(3),
+                    '6' => Some(4),
+                    '7' => Some(5),
+                    '8' => Some(6),
+                    '9' => Some(7),
+                    'T' => Some(8),
+                    'J' => Some(9),
+                    'Q' => Some(10),
+                    'K' => Some(11),
+                    'A' => Some(12),
+                    _ => None,
+                };
+                let s = match s {
+                    'c' => Some(0x8000),
+                    'd' => Some(0x4000),
+                    'h' => Some(0x2000),
+                    's' => Some(0x1000),
+                    _ => None,
+                };
+                match (r, s) {
+                    (None, None) => Err(MyError::InvalidLength),
+                    (None, Some(_)) => Err(MyError::InvalidRank),
+                    (Some(_), None) => Err(MyError::InvalidSuit),
+                    (Some(r), Some(s)) => Ok(Self(
+                        PRIMES[r as usize] as u32 | (r << 8) | s | (1 << (16 + r)),
+                    )),
+                }
+            }
+        }
+    }
+}
+
+/// ```
+/// use poker_eval::{Card};
+/// use std::str::FromStr;
+///
+/// let card = Card::from_str("Kd").unwrap();
+/// assert_eq!(card.to_string(), "Kd");
+/// ```
+impl std::fmt::Display for Card {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}{}", self.rank(), self.suit())
+    }
+}
diff --git a/src/evaluator.rs b/src/evaluator.rs
new file mode 100644 (file)
index 0000000..66efc98
--- /dev/null
@@ -0,0 +1,133 @@
+use std::{collections::HashMap, rc::Rc};
+
+use crate::{
+    card::{Card, Cards},
+    constants::{FLUSHES, HASH_ADJUST, HASH_VALUES, PERM7, UNIQUE5},
+    Algorithm, Rules,
+};
+
+#[derive(Debug)]
+pub struct Evaluator {
+    pub rules: Rules,
+    algorithm: Algorithm,
+    hands: Vec<Rc<Cards>>,
+    pub table: Rc<Cards>,
+}
+
+impl Default for Evaluator {
+    fn default() -> Self {
+        Self {
+            rules: Default::default(),
+            algorithm: Default::default(),
+            hands: Default::default(),
+            table: Default::default(),
+        }
+    }
+}
+
+impl Evaluator {
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    pub fn with_rules<'a>(&'a mut self, rules: &Rules) -> &'a mut Evaluator {
+        self.rules = *rules;
+        self
+    }
+
+    pub fn with_algorithm<'a>(&'a mut self, algorithm: &Algorithm) -> &'a mut Evaluator {
+        self.algorithm = *algorithm;
+        self
+    }
+
+    pub fn with_hand<'a>(&'a mut self, hand: &Cards) -> &'a mut Evaluator {
+        self.hands.push(Rc::new(hand.clone()));
+        self
+    }
+
+    pub fn with_hands<'a>(&'a mut self, hands: &Vec<Cards>) -> &'a mut Evaluator {
+        for hand in hands {
+            self.hands.push(Rc::new(hand.clone()))
+        }
+        self
+    }
+
+    pub fn with_table<'a>(&'a mut self, table: &Cards) -> &'a mut Evaluator {
+        self.table = Rc::new(table.clone());
+        self
+    }
+
+    pub fn eval(&self) -> HashMap<usize, u16> {
+        let result = match self.rules {
+            Rules::Classic => self
+                .hands
+                .iter()
+                .map(|player| match self.algorithm {
+                    Algorithm::CactusKev => {
+                        self.eval_5hand(&player.as_ref().0[..5].try_into().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()),
+                })
+                .enumerate()
+                .collect(),
+        };
+        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]
+        } else {
+            let q = (c1 & 0xff) * (c2 & 0xff) * (c3 & 0xff) * (c4 & 0xff) * (c5 & 0xff);
+            HASH_VALUES[Evaluator::find_fast(q) as usize]
+        }
+    }
+
+    /// 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];
+        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);
+            if q < best {
+                best = q;
+            }
+        }
+        best
+    }
+
+    pub fn find_fast(mut u: u32) -> u16 {
+        u += 0xe91aaa35;
+        u ^= u >> 16;
+        // u += u << 8;
+        u = u.wrapping_add(u << 8);
+        u ^= u >> 4;
+        let b = (u >> 8) & 0x1ff;
+        // let a = (u + (u << 2)) >> 19;
+        let a = (u.wrapping_add(u << 2)) >> 19;
+        u16::try_from(a).unwrap() ^ HASH_ADJUST[b as usize]
+    }
+}
index 270fe2c..b2f5ac9 100644 (file)
@@ -2,14 +2,13 @@
 //!
 //! Based on Kevin L. Suffecool poker hand evaluator.
 //! <http://suffe.cool/poker/evaluator.html>
+pub mod card;
 pub mod constants;
+pub mod evaluator;
 
 use std::{
     collections::HashMap,
     convert::{TryFrom, TryInto},
-    mem::transmute,
-    rc::Rc,
-    str::FromStr,
     sync::{Arc, Mutex},
     thread,
 };
@@ -60,366 +59,6 @@ impl Default for Algorithm {
     }
 }
 
-#[derive(Debug)]
-pub struct Evaluator {
-    pub rules: Rules,
-    algorithm: Algorithm,
-    hands: Vec<Rc<Cards>>,
-    pub table: Rc<Cards>,
-}
-
-impl Default for Evaluator {
-    fn default() -> Self {
-        Self {
-            rules: Default::default(),
-            algorithm: Default::default(),
-            hands: Default::default(),
-            table: Default::default(),
-        }
-    }
-}
-
-impl Evaluator {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    pub fn with_rules<'a>(&'a mut self, rules: &Rules) -> &'a mut Evaluator {
-        self.rules = *rules;
-        self
-    }
-
-    pub fn with_algorithm<'a>(&'a mut self, algorithm: &Algorithm) -> &'a mut Evaluator {
-        self.algorithm = *algorithm;
-        self
-    }
-
-    pub fn with_hand<'a>(&'a mut self, hand: &Cards) -> &'a mut Evaluator {
-        self.hands.push(Rc::new(hand.clone()));
-        self
-    }
-
-    pub fn with_hands<'a>(&'a mut self, hands: &Vec<Cards>) -> &'a mut Evaluator {
-        for hand in hands {
-            self.hands.push(Rc::new(hand.clone()))
-        }
-        self
-    }
-
-    pub fn with_table<'a>(&'a mut self, table: &Cards) -> &'a mut Evaluator {
-        self.table = Rc::new(table.clone());
-        self
-    }
-
-    pub fn eval(&self) -> HashMap<usize, u16> {
-        let result = match self.rules {
-            Rules::Classic => self
-                .hands
-                .iter()
-                .map(|player| match self.algorithm {
-                    Algorithm::CactusKev => {
-                        self.eval_5hand(&player.as_ref().0[..5].try_into().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()),
-                })
-                .enumerate()
-                .collect(),
-        };
-        result
-    }
-
-    pub fn eval_5hand(&self, cards: &[Card; 5]) -> u16 {
-        let [c1, c2, c3, c4, c5]: &[u32; 5] = unsafe { 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]
-        } else {
-            let q = (c1 & 0xff) * (c2 & 0xff) * (c3 & 0xff) * (c4 & 0xff) * (c5 & 0xff);
-            HASH_VALUES[find_fast(q) as usize]
-        }
-    }
-
-    /// 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];
-        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);
-            if q < best {
-                best = q;
-            }
-        }
-        best
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct Cards(Vec<Card>);
-
-impl FromStr for Cards {
-    type Err = MyError;
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        Ok(Self(
-            s.trim()
-                .split_whitespace()
-                .map(|c| Card::from_str(c).expect("Could not parse card from string"))
-                .collect::<Vec<_>>(),
-        ))
-    }
-}
-
-impl std::fmt::Display for Cards {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "Cards:")?;
-        for card in &self.0 {
-            write!(f, "\t{}", card.to_string())?;
-        }
-        Ok(())
-    }
-}
-
-impl From<Cards> for Vec<u32> {
-    fn from(c: Cards) -> Self {
-        c.0.into_iter().map(|c| u32::from(c)).collect()
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct Deck(Cards);
-
-impl Deck {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    pub fn get_with_rules(&mut self, rules: &Rules) -> Cards {
-        let v = match rules {
-            Rules::Classic => self.get(5),
-
-            Rules::Holdem => self.get(2),
-        };
-        v.unwrap()
-    }
-
-    pub fn get_with_rules_and_player_nb(&mut self, rules: &Rules, player_nb: usize) -> Vec<Cards> {
-        (0..player_nb)
-            .into_iter()
-            .map(|_| self.get_with_rules(rules))
-            .collect()
-    }
-
-    pub fn get(&mut self, n: usize) -> Option<Cards> {
-        if self.0 .0.len() < n {
-            None
-        } else {
-            Some(Cards(self.0 .0.split_off(self.0 .0.len() - n)))
-        }
-    }
-
-    #[cfg(feature = "random")]
-    pub fn shuffle<'a>(&'a mut self) -> &'a mut Deck {
-        use rand::prelude::*;
-        let mut rng = thread_rng();
-        self.0 .0.shuffle(&mut rng);
-        self
-    }
-}
-
-impl Default for Deck {
-    fn default() -> Self {
-        let mut deck = Vec::with_capacity(52);
-        let mut suit = 0x8000;
-        for _ in 0..4 {
-            for (j, prime) in PRIMES.iter().enumerate() {
-                let prime = *prime as u32;
-                deck.push(
-                    Card::try_from(prime | ((j as u32) << 8) | suit | (1 << (16 + j))).unwrap(),
-                );
-            }
-            suit >>= 1;
-        }
-        Self(Cards(deck))
-    }
-}
-
-/// ```
-/// use poker_eval::{Card};
-/// use std::str::FromStr;
-///
-/// assert_eq!(
-///     Card::from_str("Kd").unwrap().get(),
-///     0b00001000_00000000_01001011_00100101_u32
-/// );
-#[repr(transparent)]
-#[derive(Debug, Clone, Copy)]
-pub struct Card(u32);
-
-impl Card {
-    pub fn get(&self) -> u32 {
-        self.0
-    }
-
-    pub fn suit(&self) -> char {
-        match (self.0 >> 12) & 0xF {
-            0b1000 => 'c',
-            0b0100 => 'd',
-            0b0010 => 'h',
-            0b0001 => 's',
-            _ => unreachable!(),
-        }
-    }
-
-    /// ```
-    /// use poker_eval::{Card};
-    /// use std::str::FromStr;
-    ///
-    /// let card = Card::from_str("Kd").unwrap();
-    /// assert_eq!(card.rank(), 'K');
-    /// ```
-    pub fn rank(&self) -> char {
-        match (self.0 >> 8) & 0xF {
-            0 => '2',
-            1 => '3',
-            2 => '4',
-            3 => '5',
-            4 => '6',
-            5 => '7',
-            6 => '8',
-            7 => '9',
-            8 => 'T',
-            9 => 'J',
-            10 => 'Q',
-            11 => 'K',
-            12 => 'A',
-            _ => unreachable!(),
-        }
-    }
-}
-
-impl From<Card> for u32 {
-    fn from(c: Card) -> Self {
-        c.0
-    }
-}
-
-impl TryFrom<u32> for Card {
-    type Error = MyError;
-
-    fn try_from(value: u32) -> Result<Self, Self::Error> {
-        Ok(Self(value))
-    }
-}
-
-/// ```
-/// use poker_eval::{Card};
-/// use std::str::FromStr;
-///
-/// let card = Card::from_str("Kd").unwrap();
-/// let other = Card::from_str("Qd").unwrap();
-/// let other2 = Card::from_str("Ks").unwrap();
-/// assert!(card > other);
-/// assert!(card <= other2);
-/// ```
-impl PartialOrd for Card {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        ((self.0 >> 8) & 0xF).partial_cmp(&((other.0 >> 8) & 0xF))
-    }
-}
-
-/// ```
-/// use poker_eval::{Card};
-/// use std::str::FromStr;
-///
-/// let card = Card::from_str("Kd").unwrap();
-/// let other = Card::from_str("Ks").unwrap();
-/// assert!(card == other);
-/// ```
-impl PartialEq for Card {
-    fn eq(&self, other: &Self) -> bool {
-        (self.0 >> 8) & 0xF == (other.0 >> 8) & 0xF
-    }
-}
-
-impl FromStr for Card {
-    type Err = MyError;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let mut input = s.chars();
-        let (r, s) = (input.next(), input.next());
-        match (r, s) {
-            (None, None) => Err(MyError::InvalidLength),
-            (None, Some(_)) => Err(MyError::InvalidLength),
-            (Some(_), None) => Err(MyError::InvalidLength),
-            (Some(r), Some(s)) => {
-                let r = match r {
-                    '2' => Some(0),
-                    '3' => Some(1),
-                    '4' => Some(2),
-                    '5' => Some(3),
-                    '6' => Some(4),
-                    '7' => Some(5),
-                    '8' => Some(6),
-                    '9' => Some(7),
-                    'T' => Some(8),
-                    'J' => Some(9),
-                    'Q' => Some(10),
-                    'K' => Some(11),
-                    'A' => Some(12),
-                    _ => None,
-                };
-                let s = match s {
-                    'c' => Some(0x8000),
-                    'd' => Some(0x4000),
-                    'h' => Some(0x2000),
-                    's' => Some(0x1000),
-                    _ => None,
-                };
-                match (r, s) {
-                    (None, None) => Err(MyError::InvalidLength),
-                    (None, Some(_)) => Err(MyError::InvalidRank),
-                    (Some(_), None) => Err(MyError::InvalidSuit),
-                    (Some(r), Some(s)) => Ok(Self(
-                        PRIMES[r as usize] as u32 | (r << 8) | s | (1 << (16 + r)),
-                    )),
-                }
-            }
-        }
-    }
-}
-
-/// ```
-/// use poker_eval::{Card};
-/// use std::str::FromStr;
-///
-/// let card = Card::from_str("Kd").unwrap();
-/// assert_eq!(card.to_string(), "Kd");
-/// ```
-impl std::fmt::Display for Card {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}{}", self.rank(), self.suit())
-    }
-}
 pub fn report(result: &HashMap<usize, u16>) -> (&usize, &u16) {
     for (p, s) in result.into_iter() {
         println!("Player {}: Score {}", p, s);