use crate::hand_evaluator::Hand;
use crate::hand_range::HandRange;
use rand::seq::SliceRandom;
use rand::Rng;
const MAX_PLAYERS: usize = 6;
const MAX_SIZE: usize = 10000;
#[derive(Debug, Copy, Clone)]
pub struct Combo {
pub mask: u64,
pub hands: [Hand; MAX_PLAYERS],
pub hole_cards: [(u8, u8, u8); MAX_PLAYERS],
}
impl Combo {
fn new() -> Self {
Combo {
mask: 0,
hands: [Hand::default(); MAX_PLAYERS],
hole_cards: [(52, 52, 0); MAX_PLAYERS],
}
}
}
#[derive(Debug)]
pub struct CombinedRange {
player_count: usize,
players: [usize; MAX_PLAYERS],
combos: Vec<Combo>,
size: usize,
}
impl Default for CombinedRange {
fn default() -> Self {
CombinedRange {
player_count: 0,
players: [0; MAX_PLAYERS],
combos: Vec::new(),
size: 0,
}
}
}
impl CombinedRange {
fn from_range(range: &HandRange, player_idx: usize) -> CombinedRange {
let mut c_range = CombinedRange::default();
c_range.player_count = 1;
c_range.players[0] = player_idx;
for r in &range.hands {
let mut c = Combo::new();
c.mask = (1u64 << r.0) | (1u64 << r.1);
c.hands[0] = Hand::from_hole_cards(r.0, r.1);
c.hole_cards[0] = (r.0, r.1, r.2);
c_range.combos.push(c);
}
c_range.size = c_range.combos.len();
c_range
}
pub fn from_ranges(ranges: &[HandRange]) -> Vec<CombinedRange> {
let mut c_ranges: Vec<CombinedRange> = ranges
.iter()
.enumerate()
.map(|(i, r)| CombinedRange::from_range(r, i))
.collect();
loop {
let mut best_size = u64::MAX;
let mut best_i: usize = 0;
let mut best_j: usize = 0;
for i in 0..c_ranges.len() {
for j in 0..i {
let s = c_ranges[i].estimate_join_size(&c_ranges[j]);
if s < best_size {
best_size = s;
best_i = i;
best_j = j;
}
}
}
if best_size < MAX_SIZE as u64 {
let (head, tail) = c_ranges.split_at_mut(best_j + 1);
head[best_j] = head[best_j].join(&mut tail[best_i - best_j - 1]);
c_ranges.remove(best_i);
} else {
break;
}
}
c_ranges
}
fn join(&mut self, other: &mut CombinedRange) -> CombinedRange {
let mut c_range = CombinedRange::default();
c_range.player_count = self.player_count + other.player_count;
for i in 0..self.player_count {
c_range.players[i] = self.players[i];
}
for i in 0..other.player_count {
c_range.players[self.player_count + i] = other.players[i];
}
for c1 in &self.combos {
for c2 in &other.combos {
if (c1.mask & c2.mask) != 0 {
continue;
}
let mut combo = Combo::new();
combo.mask = c1.mask | c2.mask;
for i in 0..self.player_count {
combo.hole_cards[i] = c1.hole_cards[i];
}
for i in 0..other.player_count {
combo.hole_cards[self.player_count + i] = c2.hole_cards[i];
}
for i in 0..c_range.player_count {
combo.hands[i] =
Hand::from_hole_cards(combo.hole_cards[i].0, combo.hole_cards[i].1);
}
c_range.combos.push(combo);
}
}
c_range.size = c_range.combos.len();
c_range
}
fn estimate_join_size(&self, other: &CombinedRange) -> u64 {
let mut size = 0u64;
for c1 in &self.combos {
for c2 in &other.combos {
if (c1.mask & c2.mask) == 0 {
size += 1;
}
}
}
size
}
pub const fn player_count(&self) -> usize {
self.player_count
}
pub const fn players(&self) -> &[usize] {
&self.players
}
pub const fn combos(&self) -> &Vec<Combo> {
&self.combos
}
pub const fn size(&self) -> usize {
self.size
}
pub fn shuffle<R: Rng>(&mut self, rng: &mut R) {
self.combos.shuffle(rng);
}
}