Skip to content

Commit 1965dc2

Browse files
Merge branch 'release/v1.9'
2 parents 1786084 + f81c2fa commit 1965dc2

11 files changed

+4644
-6657
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
### Change Log
22

3+
#### 1.9
4+
5+
* Reduce hash table by 30%, to under 140kB.
6+
37
#### 1.8.1
48

59
* Simplify flush rank key and look-up.

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ project(${PROJECT_NAME})
55

66
# Versioning.
77
set(SK_POKER_EVAL_VERSION_MAJOR 1)
8-
set(SK_POKER_EVAL_VERSION_MINOR 8)
9-
set(SK_POKER_EVAL_VERSION_PATCH 1)
8+
set(SK_POKER_EVAL_VERSION_MINOR 9)
9+
set(SK_POKER_EVAL_VERSION_PATCH 0)
1010

1111
# Get the current commit.
1212
execute_process(

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ The implementation being immutable is already thread-safe and there is no initia
2323

2424
## How does it work?
2525

26-
We exploit a key-scheme that gives us just enough uniqueness to correctly identify the integral rank of any 7-card hand, where the greater this rank is the better the hand we hold and two hands of the same rank always draw. We require a memory footprint of 240kB and typically six additions to rank a hand.
26+
We exploit a key-scheme that gives us just enough uniqueness to correctly identify the integral rank of any 7-card hand, where the greater this rank is the better the hand we hold and two hands of the same rank always draw. We require a memory footprint just shy of 140kB and typically six additions to rank a hand.
2727

2828
To start with we computed by brute force the first thirteen non-negative integers such that the formal sum of exactly seven with each taken at most four times is unique among all such sums: 0, 1, 5, 22, 98, 453, 2031, 8698, 22854, 83661, 262349, 636345 and 1479181. A valid sum might be 0+0+1+1+1+1+5 = 9 or 0+98+98+453+98+98+1 = 846, but invalid sum expressions include 0+262349+0+0+0+1 (too few summands), 1+1+5+22+98+453+2031+8698 (too many summands), 0+1+5+22+98+453+2031+8698 (again too many summands, although 1+5+22+98+453+2031+8698 is a legitimate expression) and 1+1+1+1+1+98+98 (too many 1's). We assign these integers as the card face values and add these together to generate a key for any non-flush 7-card hand. The largest non-flush key we see is 7825759, corresponding to any of the four quad-of-aces-full-of-kings.
2929

@@ -44,6 +44,7 @@ Taking v1.1 as the base line, the sampled relative throughput of random [SevenEv
4444
| [1.7.1](https://github.com/kennethshackleton/SKPokerEval/releases/tag/v1.7.1) |               1.57 | Reduce the rank hash table.      |
4545
| [1.8](https://github.com/kennethshackleton/SKPokerEval/releases/tag/v1.8) |               1.93 | Index cards by bytes.       |
4646
| [1.8.1](https://github.com/kennethshackleton/SKPokerEval/releases/tag/v1.8.1) |               2.04 | Simplify flush key. Smaller offset table. |
47+
| [1.9](https://github.com/kennethshackleton/SKPokerEval/releases/tag/v1.9) |               2.04 | Reduce the hash table. |
4748

4849
## I want to contribute, how might I profile my change?
4950

scripts/perfect_hash.py

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,44 +48,67 @@
4848
print "Key count is %i." % (len(keys),)
4949
print "Max key is %i." % (max_key,)
5050

51-
side = 128 # Power of 2 to ultimately optimise hash key calculation.
52-
print "Square will be of side %i." % (side,)
51+
M = 31
52+
power = 9
53+
side = (1 << power)
54+
bits = 23
55+
print "Table will be of side %i." % (side,)
5356

54-
square = [[-1]*(512*side) for i in xrange(side)]
57+
NOT_A_VALUE = -1
58+
59+
square = [[NOT_A_VALUE]*(600*side) for i in xrange(side)]
60+
61+
offset = [0]*(1 << (bits - power))
62+
ranks = [NOT_A_VALUE]*max_key
63+
64+
diffused_keys = {}
65+
66+
def diffuse(k):
67+
k *= M
68+
return k & ((1 << bits) - 1)
5569

5670
for k in keys:
57-
square[k % side][k / side] = rank(k)
71+
d = diffuse(k)
72+
assert d not in diffused_keys
73+
diffused_keys[diffuse(k)] = k
74+
75+
for k, v in diffused_keys.iteritems():
76+
r = k / side
77+
assert square[k % side][r] == NOT_A_VALUE
78+
square[k % side][r] = rank(v)
5879

59-
offset = [0]*((max_key / side) + 1)
60-
ranks = [-1]*max_key
6180
length = 0
6281

6382
for i in xrange(0, len(offset)):
64-
for j in xrange(0, len(ranks)-side):
83+
for j in xrange(0, len(ranks)):
6584
collision = False
6685
for k in xrange(0, side):
6786
s = square[k][i]
6887
h = ranks[j+k]
69-
collision = (s != -1 and h != -1 and s != h)
88+
collision = (s != NOT_A_VALUE and h != NOT_A_VALUE and s != h)
7089
if collision: break
7190
if not collision:
7291
offset[i] = j
7392
for k in xrange(0, side):
7493
s = square[k][i]
75-
if s != -1:
94+
if s != NOT_A_VALUE:
7695
n = j+k
7796
ranks[n] = s
78-
length = max(length, n)
97+
length = max(length, n+1)
7998
print "Offset of row %i is %i (length %i)." % (i, j, length)
8099
break
81100

101+
for k in keys:
102+
d = diffuse(k)
103+
assert rank(k) == ranks[offset[d / side] + (d % side)]
104+
82105
for i in xrange(0, length):
83-
if ranks[i] == -1: ranks[i] = 0
106+
if ranks[i] == NOT_A_VALUE: ranks[i] = 0
84107

85-
with open('./ranks_%s' % (side,), 'w') as f:
108+
with open('./ranks_%i_%i' % (side, M,), 'w') as f:
86109
f.write("%s\n" % (ranks[0:length],))
87110

88-
with open('./offset_%s' % (side,), 'w') as f:
111+
with open('./offset_%i_%i' % (side, M,), 'w') as f:
89112
f.write("%s\n" % (offset,))
90113

91-
print "Hash table has length %i." % (length,)
114+
print "Accepted hash table with length %i." % (length,)

scripts/rank.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
FACE_BIT_MASK = ((1 << FLUSH_BIT_SHIFT) - 1)
2727

2828
ranks = [
29-
7220, 7243, 5860, 7297, 7309, 7229, 7242, 7298, 7153, 7310, 7206, 7298, 7142,
29+
7220, 7243, 5860, 7297, 7309, 7229, 7242, 7298, 7153, 7310, 7206, 7298, 7142,
3030
7154, 7310, 7298, 7165, 7166, 7166, 7310, 7321, 7322, 7322, 7322, 7299, 7153,
3131
7311, 7194, 7299, 7141, 7153, 7311, 7299, 7142, 4161, 7154, 7311, 7165, 7165,
3232
7166, 7166, 7205, 7323, 7323, 7323, 7299, 7143, 7155, 7311, 7299, 7143, 4183,

src/Constants.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@
8686
#define MAX_SEVEN_FLUSH_KEY_INT (ACE_FLUSH|KING_FLUSH|QUEEN_FLUSH|JACK_FLUSH|\
8787
TEN_FLUSH|NINE_FLUSH|EIGHT_FLUSH)
8888

89-
#define RANK_OFFSET_SHIFT 7
90-
#define RANK_HASH_MOD 127
89+
#define RANK_OFFSET_SHIFT 9
90+
#define RANK_HASH_MOD ((1<<RANK_OFFSET_SHIFT) - 1)
9191

9292
#define MAX_FLUSH_CHECK_SUM (7*CLUB)
9393

0 commit comments

Comments
 (0)