use super::hand;
use read_write::VecIO;
use std::env;
use std::fs::File;
use std::num::Wrapping;
const PERF_HASH_ROW_SHIFT: usize = 12;
#[inline(always)]
pub fn evaluate(hand: &hand::Hand) -> u16 {
LOOKUP_TABLE.evaluate(hand)
}
#[inline(always)]
pub fn evaluate_without_flush(hand: &hand::Hand) -> u16 {
LOOKUP_TABLE.evaluate_without_flush(hand)
}
lazy_static! {
static ref LOOKUP_TABLE: Evaluator = Evaluator::load();
}
struct Evaluator {
rank_table: Vec<u16>,
flush_table: Vec<u16>,
perf_hash_offsets: Vec<u32>,
}
impl Evaluator {
pub fn load() -> Self {
let perf_hash_file = concat!(env!("OUT_DIR"), "/h_eval_offsets.dat");
let flush_table_file = concat!(env!("OUT_DIR"), "/h_eval_flush_table.dat");
let rank_table_file = concat!(env!("OUT_DIR"), "/h_eval_rank_table.dat");
Self {
rank_table: File::open(rank_table_file)
.unwrap()
.read_vec_from_file::<u16>()
.unwrap(),
flush_table: File::open(flush_table_file)
.unwrap()
.read_vec_from_file::<u16>()
.unwrap(),
perf_hash_offsets: File::open(perf_hash_file)
.unwrap()
.read_vec_from_file::<u32>()
.unwrap(),
}
}
#[inline(always)]
pub fn evaluate_without_flush(&self, hand: &hand::Hand) -> u16 {
self.rank_table[self.perf_hash(hand.get_rank_key())]
}
#[inline(always)]
pub fn evaluate(&self, hand: &hand::Hand) -> u16 {
if hand.has_flush() {
self.flush_table[hand.get_flush_key()]
} else {
self.rank_table[self.perf_hash(hand.get_rank_key())]
}
}
#[inline(always)]
fn perf_hash(&self, key: usize) -> usize {
(Wrapping(key as u32) + Wrapping(self.perf_hash_offsets[key >> PERF_HASH_ROW_SHIFT])).0
as usize
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::constants::HAND_CATEGORY_SHIFT;
use test::Bencher;
#[bench]
fn bench_lookup(b: &mut Bencher) {
let hand = hand::Hand::default() + hand::CARDS[0] + hand::CARDS[1];
b.iter(|| evaluate(&hand));
}
#[test]
fn test_four_of_a_kind() {
let hand = hand::Hand::default()
+ hand::CARDS[0]
+ hand::CARDS[1]
+ hand::CARDS[2]
+ hand::CARDS[3];
assert_eq!(8, evaluate(&hand) >> HAND_CATEGORY_SHIFT);
assert_eq!(32769, evaluate(&hand));
}
#[test]
fn test_trips() {
let hand = hand::Hand::default() + hand::CARDS[0] + hand::CARDS[1] + hand::CARDS[2];
assert_eq!(4, evaluate(&hand) >> HAND_CATEGORY_SHIFT);
}
#[test]
fn test_pair() {
let hand = hand::Hand::default() + hand::CARDS[0] + hand::CARDS[1];
assert_eq!(2, evaluate(&hand) >> HAND_CATEGORY_SHIFT);
}
#[test]
fn test_highcard() {
let hand = hand::Hand::default() + hand::CARDS[0] + hand::CARDS[5];
assert_eq!(1, evaluate(&hand) >> HAND_CATEGORY_SHIFT);
}
}