use constants::{
STRAIGHTS, WORST_FLUSH, WORST_PAIR, WORST_STRAIGHT, WORST_STRAIGHT_FLUSH, WORST_THREE_OF_A_KIND,
};
use rustc_hash::{FxBuildHasher, FxHashMap};
use crate::{
constants::{INT_RANKS, PRIMES},
evaluate::{
eval::Eval,
poker_type::ThreeCard,
utils::{self, BitSequence},
},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LookupTable {
pub flush_lookup: FxHashMap<i32, Eval<ThreeCard>>,
pub unsuited_lookup: FxHashMap<i32, Eval<ThreeCard>>,
}
impl LookupTable {
pub fn new() -> Self {
let mut lookup_table = Self {
flush_lookup: FxHashMap::with_capacity_and_hasher(286, FxBuildHasher),
unsuited_lookup: FxHashMap::with_capacity_and_hasher(455, FxBuildHasher),
};
lookup_table.flushes();
lookup_table.multiples();
lookup_table
}
fn flushes(&mut self) {
const NOT_STRAIGHTS: [i16; 274] = constants::not_straights();
let mut rank_suited: i16 = 1;
let mut rank_unsuited: i16 = WORST_THREE_OF_A_KIND + 1;
let mut rank_flags;
let mut prime_product;
for straight in STRAIGHTS {
prime_product = utils::prime_product_from_rank_bits(straight);
rank_flags = utils::high_rank_from_rank_bits_three(straight);
self.flush_lookup
.insert(prime_product, Eval::new(rank_suited, rank_flags));
self.unsuited_lookup
.insert(prime_product, Eval::new(rank_unsuited, rank_flags));
rank_suited = rank_suited.wrapping_add(1);
rank_unsuited = rank_unsuited.wrapping_add(1);
}
rank_suited = WORST_STRAIGHT + 1;
rank_unsuited = WORST_PAIR + 1;
for bits in NOT_STRAIGHTS.into_iter().rev() {
prime_product = utils::prime_product_from_rank_bits(bits);
rank_flags = utils::high_rank_from_rank_bits_three(bits);
self.flush_lookup
.insert(prime_product, Eval::new(rank_suited, rank_flags));
self.unsuited_lookup
.insert(prime_product, Eval::new(rank_unsuited, rank_flags));
rank_suited = rank_suited.wrapping_add(1);
rank_unsuited = rank_unsuited.wrapping_add(1);
}
}
fn multiples(&mut self) {
let mut product;
let mut hand_rank = WORST_STRAIGHT_FLUSH + 1;
for trips in INT_RANKS.rev() {
product = PRIMES[trips as usize].wrapping_pow(3);
self.unsuited_lookup.insert(
product,
Eval::new(hand_rank, 1i16.wrapping_shl(trips as u32)),
);
hand_rank = hand_rank.wrapping_add(1);
}
hand_rank = WORST_FLUSH + 1;
for pair in INT_RANKS.rev() {
let kickers = INT_RANKS.rev().filter(|&i| i != pair);
for kicker in kickers {
product = PRIMES[pair as usize]
.wrapping_pow(2)
.wrapping_mul(PRIMES[kicker as usize]);
self.unsuited_lookup.insert(
product,
Eval::new(hand_rank, 1i16.wrapping_shl(pair as u32)),
);
hand_rank = hand_rank.wrapping_add(1);
}
}
}
}
impl Default for LookupTable {
fn default() -> Self { Self::new() }
}
pub mod constants {
use super::*;
pub const WORST_STRAIGHT_FLUSH: i16 = 12;
pub const WORST_THREE_OF_A_KIND: i16 = 25;
pub const WORST_STRAIGHT: i16 = 37;
pub const WORST_FLUSH: i16 = 311;
pub const WORST_PAIR: i16 = 467;
pub const WORST_HIGH_CARD: i16 = 741;
pub const STRAIGHTS: [i16; 12] = [
0b1_1100_0000_0000,
0b0_1110_0000_0000,
0b0_0111_0000_0000,
0b0_0011_1000_0000,
0b0_0001_1100_0000,
0b0_0000_1110_0000,
0b0_0000_0111_0000,
0b0_0000_0011_1000,
0b0_0000_0001_1100,
0b0_0000_0000_1110,
0b0_0000_0000_0111,
0b1_0000_0000_0011,
];
pub const fn not_straights() -> [i16; 274] {
let mut not_straights = [0; 274];
let mut generator = BitSequence::new(0b111);
let mut i = 0;
let mut cur = 0;
while i < 285 {
let bits = generator.get_next();
let mut is_straight = false;
let mut j = 0;
while j < STRAIGHTS.len() {
let straight = STRAIGHTS[j];
if bits ^ straight == 0 {
is_straight = true;
break;
}
j += 1;
}
if !is_straight {
not_straights[cur] = bits;
cur += 1;
}
i += 1;
}
not_straights
}
}
#[cfg(test)]
mod tests {
use super::{constants::*, *};
#[test]
fn lookup_table_creation() {
let table = LookupTable::new();
assert_eq!(table.flush_lookup.len(), 286);
assert_eq!(table.unsuited_lookup.len(), 455);
}
#[test]
fn all_lookup_table_entries_have_valid_rankings() {
let table = LookupTable::new();
for &eval in table.flush_lookup.values() {
let rank = eval.hand_rank();
assert!((1..=WORST_FLUSH).contains(&rank));
}
for &eval in table.unsuited_lookup.values() {
let rank = eval.hand_rank();
assert!(rank > WORST_STRAIGHT_FLUSH && rank <= WORST_HIGH_CARD);
}
}
#[test]
fn straight_constants() {
assert_eq!(STRAIGHTS.len(), 12);
assert_eq!(STRAIGHTS[0], 0b1_1100_0000_0000); assert_eq!(STRAIGHTS[11], 0b1_0000_0000_0011); }
#[test]
fn not_straights_generation() {
let not_straights = constants::not_straights();
assert_eq!(not_straights.len(), 274);
}
#[test]
fn default_implementation() {
let table1 = LookupTable::new();
let table2 = LookupTable::default();
assert_eq!(table1, table2);
}
}