use std::cmp::Ordering;
use std::iter::FromIterator;
use crate::constants::*;
#[derive(Debug, Clone, Copy)]
pub struct HoleCards(pub u8, pub u8);
impl HoleCards {
pub fn to_string(&self) -> String {
let chars: Vec<char> = vec![
RANK_TO_CHAR[usize::from(self.0 >> 2)],
SUIT_TO_CHAR[usize::from(self.0 & 3)],
RANK_TO_CHAR[usize::from(self.1 >> 2)],
SUIT_TO_CHAR[usize::from(self.1 & 3)]
];
return String::from_iter(chars);
}
}
impl Ord for HoleCards {
fn cmp(&self, other: &Self) -> Ordering {
if (self.0 >> 2) != (other.0 >> 2) {
return (&self.0 >> 2).cmp(&(other.0 >> 2));
}
if (self.1 >> 2) != (other.1 >> 2) {
return (&self.1 >> 2).cmp(&(other.1 >> 2));
}
if (self.0 & 3) != (other.0 & 3) {
return (&self.0 & 3).cmp(&(other.0 & 3));
}
return (&self.1 & 3).cmp(&(other.1 & 3));
}
}
impl PartialOrd for HoleCards {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for HoleCards {
fn eq(&self, other: &Self) -> bool {
let h1: u16 = (u16::from(self.0) << 8) | u16::from(self.1);
let h2: u16 = (u16::from(other.0) << 8) | u16::from(other.1);
return h1 == h2;
}
}
impl Eq for HoleCards {}
#[derive(Debug, Clone)]
pub struct HandRange {
pub hands: Vec<HoleCards>,
pub char_vec: Vec<char>
}
impl HandRange {
fn new() -> Self {
HandRange {
hands: Vec::new(),
char_vec: Vec::new()
}
}
pub fn from_strings(arr: Vec<String>) -> Vec<Self> {
return arr
.iter()
.map(|s| HandRange::from_string(s.to_owned()))
.collect();
}
pub fn from_string(text: String) -> Self {
let mut range: HandRange = HandRange::new();
if text == "random" {
range.add_all();
} else {
range.char_vec = text.to_lowercase().chars().collect();
range.char_vec.push(' ');
let mut i: usize = 0;
while range.parse_hand(&mut i) && range.parse_char(&mut i, ',') {}
range.remove_duplicates();
}
return range;
}
fn parse_hand(&mut self, i: &mut usize) -> bool {
let backtrack = *i;
let explicit_suits: bool;
let mut r1: u8 = u8::MAX;
let mut r2: u8 = u8::MAX;
let mut s1: u8 = u8::MAX;
let mut s2: u8 = u8::MAX;
if !self.parse_rank(i, &mut r1) {
return false;
}
explicit_suits = self.parse_suit(i, &mut s1);
if !self.parse_rank(i, &mut r2) {
println!("HERE");
*i = backtrack;
return false;
}
if explicit_suits && !self.parse_suit(i, &mut s2) {
*i = backtrack;
return false;
} if explicit_suits {
let c1 = 4 * r1 + s1;
let c2 = 4 * r2 + s2;
if c1 == c2 {
*i = backtrack;
return false;
}
self.add_combo(c1, c2);
} else {
let mut suited = true;
let mut offsuited = true;
if self.parse_char(i, 'o') {
suited = false;
} else if self.parse_char(i, 's') {
offsuited = false;
}
if self.parse_char(i, '+') {
self.add_combos_plus(r1, r2, suited, offsuited);
} else {
self.add_combos(r1, r2, suited, offsuited);
}
}
return true;
}
fn parse_char(&mut self, i: &mut usize, c: char) -> bool {
if self.char_vec[*i] == c {
*i += 1;
return true;
} else {
return false;
}
}
fn parse_rank(&mut self, i: &mut usize, rank: &mut u8) -> bool {
*rank = char_to_rank(self.char_vec[*i]);
if *rank == u8::MAX {
return false;
}
*i += 1;
return true;
}
fn parse_suit(&mut self, i: &mut usize, suit: &mut u8) -> bool {
*suit = char_to_suit(self.char_vec[*i]);
if *suit == u8::MAX {
return false;
}
*i += 1;
return true;
}
fn add_combo(&mut self, c1: u8, c2: u8) {
if c1 > 51 || c2 > 51 {
return;
}
if c1 == c2 { return; }
let h: HoleCards;
if c1 >> 2 < c2 >> 2 || (c1 >> 2 == c2 >> 2 && (c1 & 3) < (c2 & 3)) {
h = HoleCards(c2, c1);
} else {
h = HoleCards(c1, c2);
}
self.hands.push(h);
}
fn add_combos_plus(&mut self, rank1: u8, rank2: u8, suited: bool, offsuited: bool) {
if rank1 == rank2 {
for r in rank1..13 {
self.add_combos(r, r, suited, offsuited);
}
} else {
for r in rank2..=rank1 {
self.add_combos(rank1, r, suited, offsuited);
}
}
}
fn add_combos(&mut self, rank1: u8, rank2: u8, suited: bool, offsuited: bool) {
if suited && rank1 != rank2 {
for suit in 0..4 {
self.add_combo(4 * rank1 + suit, 4 * rank2 + suit);
}
}
if offsuited {
for suit1 in 0..4 {
for suit2 in (suit1 + 1)..4 {
self.add_combo(4 * rank1 + suit1, 4 * rank2 + suit2);
if rank1 != rank2 {
self.add_combo(4 * rank1 + suit2, 4 * rank2 + suit1);
}
}
}
}
}
fn add_all(&mut self) {
for c1 in 0..CARD_COUNT {
for c2 in 0..c1 {
self.add_combo(c1, c2);
}
}
}
fn remove_duplicates(&mut self) {
self.hands.sort_by(|a, b| a.cmp(b));
self.hands.dedup();
}
}
pub fn char_to_rank(c: char) -> u8 {
let rank = match c {
'a' => 12,
'k' => 11,
'q' => 10,
'j' => 9,
't' => 8,
'9' => 7,
'8' => 6,
'7' => 5,
'6' => 4,
'5' => 3,
'4' => 2,
'3' => 1,
'2' => 0,
_ => u8::MAX
};
return rank;
}
pub fn char_to_suit(c: char) -> u8 {
let suit = match c {
's' => 0,
'h' => 1,
'd' => 2,
'c' => 3,
_ => u8::MAX
};
return suit;
}
pub fn get_card_mask(text: &str) -> u64 {
let char_vec: Vec<char> = text.to_lowercase().chars().collect();
let mut cards: u64 = 0;
let len = char_vec.len();
if len % 2 != 0 {
return 0;
}
for i in (0..len).step_by(2) {
let rank = char_to_rank(char_vec[i]);
let suit = char_to_suit(char_vec[i+1]);
if rank == u8::MAX || suit == u8::MAX {
return 0u64;
}
let card = (4 * rank) + suit;
cards |= 1u64 << card;
}
return cards;
}
pub fn mask_to_string(card_mask: u64) -> String {
let mut card_str = String::new();
for i in 0..CARD_COUNT {
if ((1u64 << i) & card_mask) != 0 {
let rank = i >> 2;
let suit = i & 3;
card_str.push(RANK_TO_CHAR[usize::from(rank)]);
card_str.push(SUIT_TO_CHAR[usize::from(suit)]);
}
}
return card_str;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_char_to_suit() {
assert_eq!(char_to_suit('s'), 0);
assert_eq!(char_to_suit('h'), 1);
assert_eq!(char_to_suit('x'), u8::MAX);
assert_eq!(char_to_suit(' '), u8::MAX);
}
#[test]
fn test_char_to_rank() {
assert_eq!(char_to_rank('a'), 12);
assert_eq!(char_to_rank('2'), 0);
assert_eq!(char_to_rank('x'), u8::MAX);
assert_eq!(char_to_rank(' '), u8::MAX);
}
#[test]
fn test_hand_range_new() {
let c = HandRange::new();
assert_eq!(c.hands.len(), 0);
}
#[test]
fn test_hand_range_remove_duplicates() {
let mut c = HandRange::new();
c.add_combos(1, 1, true, true);
c.add_combos(1, 1, true, true);
assert_eq!(c.hands.len(), 12);
c.remove_duplicates();
assert_eq!(c.hands.len(), 6);
c = HandRange::new();
c.add_combos(1, 0, true, false);
c.add_combos(1, 0, false, true);
assert_eq!(c.hands.len(), 16);
c.remove_duplicates();
assert_eq!(c.hands.len(), 16);
}
#[test]
fn test_hand_range_add_combo() {
let mut c = HandRange::new();
c.add_combo(52, 0);
assert_eq!(c.hands.len(), 0);
c = HandRange::new();
c.add_combo(0, 0);
assert_eq!(c.hands.len(), 0);
}
#[test]
fn test_hand_range_add_combos() {
let mut c = HandRange::new();
c.add_combos(1, 1, true, true);
assert_eq!(c.hands.len(), 6);
c = HandRange::new();
c.add_combos(1, 0, true, false);
assert_eq!(c.hands.len(), 4);
c = HandRange::new();
c.add_combos(1, 0, false, true);
assert_eq!(c.hands.len(), 12);
c = HandRange::new();
c.add_combos(1, 0, true, true);
assert_eq!(c.hands.len(), 16);
}
#[test]
fn test_hand_range_random() {
let c = HandRange::from_string("random".to_string());
assert_eq!(c.hands.len(), 1326);
}
#[test]
fn test_hand_range_from_str() {
let mut c = HandRange::from_string("33".to_string());
assert_eq!(c.hands.len(), 6);
c = HandRange::from_string("a2o".to_string());
assert_eq!(c.hands.len(), 12);
c = HandRange::from_string("a2s".to_string());
assert_eq!(c.hands.len(), 4);
c = HandRange::from_string("a2+".to_string());
assert_eq!(c.hands.len(), 198);
c = HandRange::from_string("a2o+".to_string());
assert_eq!(c.hands.len(), 150);
c = HandRange::from_string("a2s+".to_string());
assert_eq!(c.hands.len(), 48);
c = HandRange::from_string("22,a2s+".to_string());
assert_eq!(c.hands.len(), 54);
c = HandRange::from_string("a2s+,a4s+".to_string());
assert_eq!(c.hands.len(), 48);
c = HandRange::from_string("as2h,2h3d".to_string());
assert_eq!(c.hands.len(), 2);
}
}