1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use super::hand;
use read_write::VecIO;
use std::num::Wrapping;
use std::fs::File;
use std::env;
const PERF_HASH_ROW_SHIFT: usize = 12;
pub fn evaluate(hand: &hand::Hand) -> u16 {
LOOKUP_TABLE.evaluate(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(),
}
}
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())]
}
}
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::empty() + hand::CARDS[0] + hand::CARDS[1];
b.iter(|| evaluate(&hand));
}
#[test]
fn test_four_of_a_kind() {
let hand =
hand::Hand::empty() + 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::empty() + 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::empty() + hand::CARDS[0] + hand::CARDS[1];
assert_eq!(2, evaluate(&hand) >> HAND_CATEGORY_SHIFT);
}
#[test]
fn test_highcard() {
let hand = hand::Hand::empty() + hand::CARDS[0] + hand::CARDS[5];
assert_eq!(1, evaluate(&hand) >> HAND_CATEGORY_SHIFT);
}
}