-use poker_eval::{init_deck, shuffle_deck, Evaluator, Hand, Rules};
+use poker_eval::{report, Algorithm, Cards, Evaluator, Rules};
fn main() {
- let rules = Rules::Classic;
- let deck = init_deck();
- let mut deck = shuffle_deck(deck);
- let mut table = Hand::new();
+ let rules = Rules::Holdem;
+ let algorithm = Algorithm::CactusKev;
+ let mut deck = Cards::init_deck();
+ deck.shuffle_cards();
- let player_hands: Vec<Hand> = (0..4)
+ let player_hands: Vec<Cards> = (0..4)
.into_iter()
- .map(|_| Hand::new().with_rules(&rules, &mut deck).clone())
+ .map(|_| Cards::new().with_rules(&rules, &mut deck).clone())
.collect();
- table.cards = deck.split_off(deck.len() - 5);
+ let mut table = Cards::new();
+ table.with_cards(deck.removen(5).unwrap());
let result = Evaluator::new()
.with_rules(&rules)
+ .with_algorithm(&algorithm)
.with_hands(&player_hands)
.with_table(&table)
.eval();
- println!("Table: {:?}", table.cards);
- println!("Results: {:?}", result);
- let winner = result.iter().min_by(|a, b| a.1.cmp(&b.1)).unwrap();
- println!("Winner: {:?}", winner);
+ println!("Poker evaluator simulation\n");
+ for (i, hand) in player_hands.iter().enumerate() {
+ println!("Player {}: {}", i, hand);
+ }
+ match rules {
+ Rules::Holdem => println!("Table: {}", table),
+ Rules::Classic => (),
+ }
+ println!();
+ // println!("Results: {:?}", result);
+ let _winner = report(&result);
+ // let winner = result.iter().min_by(|a, b| a.1.cmp(&b.1)).unwrap();
+ // println!("Winner: Player {} with score {}", winner.0, winner.1);
}
use constants::{
EXPECTED_FREQ, FLUSH, FLUSHES, FOUR_OF_A_KIND, FULL_HOUSE, HASH_ADJUST, HASH_VALUES, HIGH_CARD,
ONE_PAIR, PERM7, PRIMES, STRAIGHT, STRAIGHT_FLUSH, THREE_OF_A_KIND, TWO_PAIR, UNIQUE5,
+ VALUE_STR,
};
use wasm_bindgen::prelude::*;
#[derive(Debug)]
pub enum MyError {
InvalidLength,
+ InvalidRank,
+ InvalidSuit,
}
impl std::error::Error for MyError {}
#[derive(Debug, Clone, Copy)]
pub enum Algorithm {
- Suffecool,
+ CactusKev,
}
impl Default for Algorithm {
fn default() -> Self {
- Self::Suffecool
+ Self::CactusKev
}
}
pub struct Evaluator {
pub rules: Rules,
algorithm: Algorithm,
- hands: Vec<Rc<Hand>>,
- pub table: Rc<Hand>,
+ hands: Vec<Rc<Cards>>,
+ pub table: Rc<Cards>,
}
impl Default for Evaluator {
self
}
- pub fn with_hand<'a>(&'a mut self, hand: &Hand) -> &'a mut Evaluator {
+ 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<Hand>) -> &'a mut Evaluator {
+ 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: &Hand) -> &'a mut Evaluator {
+ 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> {
- // match cards.len() {
- // 7 => Ok(eval_7hand(&cards.try_into().unwrap())),
- // 5 => Ok(eval_5hand(&cards.try_into().unwrap())),
- // _ => Err(MyError::InvalidLength),
- // }
let result = match self.rules {
Rules::Classic => self
.hands
.iter()
.map(|player| match self.algorithm {
- Algorithm::Suffecool => {
- self.eval_5hand(player.as_ref().cards[..].try_into().unwrap())
- }
+ Algorithm::CactusKev => self.eval_5hand(player.as_ref().clone()),
})
.enumerate()
.collect(),
.iter()
.map(|h| {
h.as_ref()
- .cards
+ .0
.iter()
- .chain(self.table.as_ref().cards.iter())
+ .chain(self.table.as_ref().0.iter())
.cloned()
.collect()
})
- .map(|player: Vec<u32>| match self.algorithm {
- Algorithm::Suffecool => self.eval_7hand(&player[..].try_into().unwrap()),
+ .map(|player: Vec<Card>| match self.algorithm {
+ Algorithm::CactusKev => self.eval_7hand(Cards(player)),
})
.enumerate()
.collect(),
result
}
- pub fn eval_5hand(&self, cards: &[u32; 5]) -> u16 {
+ pub fn eval_5hand(&self, cards: Cards) -> u16 {
+ let cards: Vec<u32> = cards.0.into_iter().map(|c| u32::from(c)).collect();
if let [c1, c2, c3, c4, c5] = cards[..5] {
let mut q = (c1 | c2 | c3 | c4 | c5) >> 16;
if c1 & c2 & c3 & c4 & c5 & 0xF000 != 0 {
}
/// Non-optimized method of determining the best five-card hand possible of seven cards.
- pub fn eval_7hand(&self, cards: &[u32; 7]) -> u16 {
- let mut subhand: [u32; 5] = [0; 5];
+ pub fn eval_7hand(&self, cards: Cards) -> 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];
+ subhand[j] = cards.0[..][PERM7[i][j] as usize];
}
- let q = eval_5hand(&subhand);
+ let q = self.eval_5hand(Cards(subhand.into()));
if q < best {
best = q;
}
}
#[derive(Debug, Default, Clone)]
-pub struct Hand {
- pub cards: Vec<u32>,
-}
+pub struct Cards(Vec<Card>);
-impl Hand {
+impl Cards {
pub fn new() -> Self {
- Self { cards: Vec::new() }
+ Self(Vec::new())
}
- pub fn with_cards<'a>(&'a mut self, cards: Vec<u32>) -> &'a mut Hand {
- for card in cards {
- self.cards.push(card.clone());
+ pub fn with_cards<'a>(&'a mut self, cards: Cards) -> &'a mut Cards {
+ for card in cards.0 {
+ self.0.push(card.clone());
}
self
}
- pub fn with_rules<'a>(&'a mut self, rules: &Rules, deck: &mut Vec<u32>) -> &'a mut Hand {
+ pub fn with_rules<'a>(&'a mut self, rules: &Rules, deck: &mut Cards) -> &'a mut Cards {
match rules {
Rules::Classic => {
- self.with_cards(deck.split_off(deck.len() - 5));
+ self.with_cards(deck.removen(5).unwrap());
}
Rules::Holdem => {
- self.with_cards(deck.split_off(deck.len() - 2));
+ self.with_cards(deck.removen(2).unwrap());
}
};
self
}
+
+ pub fn removen(&mut self, n: usize) -> Option<Cards> {
+ if self.0.len() < n {
+ None
+ } else {
+ Some(Cards(self.0.split_off(self.0.len() - n)))
+ }
+ }
+
+ pub fn init_deck() -> Cards {
+ let mut deck = Vec::new();
+ 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 | (u32::try_from(j).unwrap() << 8) | suit | (1 << (16 + j)),
+ )
+ .unwrap(),
+ );
+ }
+ suit >>= 1;
+ }
+ Cards(deck)
+ }
+
+ #[cfg(feature = "random")]
+ pub fn shuffle_cards<'a>(&'a mut self) -> &'a mut Cards {
+ use rand::prelude::*;
+ let mut rng = thread_rng();
+ self.0.shuffle(&mut rng);
+ self
+ }
}
-impl FromStr for Hand {
+impl FromStr for Cards {
type Err = MyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- Ok(Self {
- cards: s
- .trim()
+ Ok(Self(
+ s.trim()
.split_whitespace()
- .map(|c| parse_card(c).expect("Could not parse card from string"))
+ .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(())
+ }
+}
+
+/// ```
+/// use poker_eval::{Card};
+/// use std::str::FromStr;
+///
+/// assert_eq!(
+/// Card::from_str("Kd").unwrap().get(),
+/// 0b00001000_00000000_01001011_00100101_u32
+/// );
+#[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 u32 | (u32::try_from(r).unwrap() << 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);
+ println!("Hand value: {}", VALUE_STR[hand_rank(*s) as usize]);
+ println!();
}
+ let winner = result.iter().min_by(|a, b| a.1.cmp(&b.1)).unwrap().clone();
+ println!("Winner: Player {} with score {}", winner.0, winner.1);
+ winner
}
/// Initializes a deck of 52 cards as an integer vector of lenght 52.