-use poker_eval::{report, Algorithm, Deck, Evaluator, Rules};
+use poker_eval::{card::Deck, evaluator::Evaluator, report, Algorithm, Rules};
fn main() {
let rules = Rules::Holdem;
--- /dev/null
+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())
+ }
+}
--- /dev/null
+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]
+ }
+}
//!
//! 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,
};
}
}
-#[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);