From 28b6f43cad7d013aeb21f9b631815db23fab259b Mon Sep 17 00:00:00 2001 From: MA Beaudet Date: Sat, 6 Nov 2021 13:02:18 +0100 Subject: [PATCH] feat: add evaluator and hand structs (WIP) Evaluation should be done using these structs. I will probably let some functions available freely for wasm builds. Hand should probably be changed to `struct Hand(Vec) where Card is `struct Card(u32)` that impl `std::str::FromStr`, `std::fmt::Display`, PartialEq`, `PartialOrd` and others... --- src/lib.rs | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 226c49c..93b7342 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,10 @@ pub mod constants; use std::{ + collections::HashMap, convert::{TryFrom, TryInto}, + rc::Rc, + str::FromStr, sync::{Arc, Mutex}, thread, }; @@ -17,6 +20,208 @@ use constants::{ use wasm_bindgen::prelude::*; +#[derive(Debug)] +pub enum MyError { + InvalidLength, +} + +impl std::error::Error for MyError {} + +impl std::fmt::Display for MyError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Invalid number of cards") + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Rules { + Classic, + Holdem, +} + +impl Default for Rules { + fn default() -> Self { + Self::Classic + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Algorithm { + Suffecool, +} + +impl Default for Algorithm { + fn default() -> Self { + Self::Suffecool + } +} + +#[derive(Debug)] +pub struct Evaluator { + pub rules: Rules, + algorithm: Algorithm, + hands: Vec>, + pub table: Rc, +} + +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: &Hand) -> &'a mut Evaluator { + self.hands.push(Rc::new(hand.clone())); + self + } + + pub fn with_hands<'a>(&'a mut self, hands: &Vec) -> &'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 { + self.table = Rc::new(table.clone()); + self + } + + pub fn eval(&self) -> HashMap { + // 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()) + } + }) + .enumerate() + .collect(), + Rules::Holdem => self + .hands + .iter() + .map(|h| { + h.as_ref() + .cards + .iter() + .chain(self.table.as_ref().cards.iter()) + .cloned() + .collect() + }) + .map(|player: Vec| match self.algorithm { + Algorithm::Suffecool => self.eval_7hand(&player[..].try_into().unwrap()), + }) + .enumerate() + .collect(), + }; + result + } + + pub fn eval_5hand(&self, cards: &[u32; 5]) -> u16 { + 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 { + FLUSHES[q as usize] + } else if UNIQUE5[q as usize] != 0 { + UNIQUE5[q as usize] + } else { + q = (c1 & 0xff) * (c2 & 0xff) * (c3 & 0xff) * (c4 & 0xff) * (c5 & 0xff); + HASH_VALUES[find_fast((q as usize).try_into().unwrap()) as usize] + } + } else { + 9999 + } + } + + /// 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]; + let mut best = 9999; + + for i in 0..21 { + for j in 0..5 { + subhand[j] = cards[..][PERM7[i][j] as usize]; + } + let q = eval_5hand(&subhand); + if q < best { + best = q; + } + } + best + } +} + +#[derive(Debug, Default, Clone)] +pub struct Hand { + pub cards: Vec, +} + +impl Hand { + pub fn new() -> Self { + Self { cards: Vec::new() } + } + + pub fn with_cards<'a>(&'a mut self, cards: Vec) -> &'a mut Hand { + for card in cards { + self.cards.push(card.clone()); + } + self + } + + pub fn with_rules<'a>(&'a mut self, rules: &Rules, deck: &mut Vec) -> &'a mut Hand { + match rules { + Rules::Classic => { + self.with_cards(deck.split_off(deck.len() - 5)); + } + + Rules::Holdem => { + self.with_cards(deck.split_off(deck.len() - 2)); + } + }; + self + } +} + +impl FromStr for Hand { + type Err = MyError; + fn from_str(s: &str) -> Result { + Ok(Self { + cards: s + .trim() + .split_whitespace() + .map(|c| parse_card(c).expect("Could not parse card from string")) + .collect::>(), + }) + } +} + /// Initializes a deck of 52 cards as an integer vector of lenght 52. /// /// The order in which the cards are returned is not random. -- 2.20.1