Skip to content

Commit

Permalink
Merge branch 'release/v1.9'
Browse files Browse the repository at this point in the history
  • Loading branch information
kennethshackleton committed Oct 1, 2017
2 parents 1786084 + f81c2fa commit 1965dc2
Show file tree
Hide file tree
Showing 11 changed files with 4,644 additions and 6,657 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
### Change Log

#### 1.9

* Reduce hash table by 30%, to under 140kB.

#### 1.8.1

* Simplify flush rank key and look-up.
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Expand Up @@ -5,8 +5,8 @@ project(${PROJECT_NAME})

# Versioning.
set(SK_POKER_EVAL_VERSION_MAJOR 1)
set(SK_POKER_EVAL_VERSION_MINOR 8)
set(SK_POKER_EVAL_VERSION_PATCH 1)
set(SK_POKER_EVAL_VERSION_MINOR 9)
set(SK_POKER_EVAL_VERSION_PATCH 0)

# Get the current commit.
execute_process(
Expand Down
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -23,7 +23,7 @@ The implementation being immutable is already thread-safe and there is no initia

## How does it work?

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.
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.

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.

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

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

Expand Down
51 changes: 37 additions & 14 deletions scripts/perfect_hash.py
Expand Up @@ -48,44 +48,67 @@
print "Key count is %i." % (len(keys),)
print "Max key is %i." % (max_key,)

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

square = [[-1]*(512*side) for i in xrange(side)]
NOT_A_VALUE = -1

square = [[NOT_A_VALUE]*(600*side) for i in xrange(side)]

offset = [0]*(1 << (bits - power))
ranks = [NOT_A_VALUE]*max_key

diffused_keys = {}

def diffuse(k):
k *= M
return k & ((1 << bits) - 1)

for k in keys:
square[k % side][k / side] = rank(k)
d = diffuse(k)
assert d not in diffused_keys
diffused_keys[diffuse(k)] = k

for k, v in diffused_keys.iteritems():
r = k / side
assert square[k % side][r] == NOT_A_VALUE
square[k % side][r] = rank(v)

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

for i in xrange(0, len(offset)):
for j in xrange(0, len(ranks)-side):
for j in xrange(0, len(ranks)):
collision = False
for k in xrange(0, side):
s = square[k][i]
h = ranks[j+k]
collision = (s != -1 and h != -1 and s != h)
collision = (s != NOT_A_VALUE and h != NOT_A_VALUE and s != h)
if collision: break
if not collision:
offset[i] = j
for k in xrange(0, side):
s = square[k][i]
if s != -1:
if s != NOT_A_VALUE:
n = j+k
ranks[n] = s
length = max(length, n)
length = max(length, n+1)
print "Offset of row %i is %i (length %i)." % (i, j, length)
break

for k in keys:
d = diffuse(k)
assert rank(k) == ranks[offset[d / side] + (d % side)]

for i in xrange(0, length):
if ranks[i] == -1: ranks[i] = 0
if ranks[i] == NOT_A_VALUE: ranks[i] = 0

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

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

print "Hash table has length %i." % (length,)
print "Accepted hash table with length %i." % (length,)
2 changes: 1 addition & 1 deletion scripts/rank.py
Expand Up @@ -26,7 +26,7 @@
FACE_BIT_MASK = ((1 << FLUSH_BIT_SHIFT) - 1)

ranks = [
7220, 7243, 5860, 7297, 7309, 7229, 7242, 7298, 7153, 7310, 7206, 7298, 7142,
7220, 7243, 5860, 7297, 7309, 7229, 7242, 7298, 7153, 7310, 7206, 7298, 7142,
7154, 7310, 7298, 7165, 7166, 7166, 7310, 7321, 7322, 7322, 7322, 7299, 7153,
7311, 7194, 7299, 7141, 7153, 7311, 7299, 7142, 4161, 7154, 7311, 7165, 7165,
7166, 7166, 7205, 7323, 7323, 7323, 7299, 7143, 7155, 7311, 7299, 7143, 4183,
Expand Down
4 changes: 2 additions & 2 deletions src/Constants.h
Expand Up @@ -86,8 +86,8 @@
#define MAX_SEVEN_FLUSH_KEY_INT (ACE_FLUSH|KING_FLUSH|QUEEN_FLUSH|JACK_FLUSH|\
TEN_FLUSH|NINE_FLUSH|EIGHT_FLUSH)

#define RANK_OFFSET_SHIFT 7
#define RANK_HASH_MOD 127
#define RANK_OFFSET_SHIFT 9
#define RANK_HASH_MOD ((1<<RANK_OFFSET_SHIFT) - 1)

#define MAX_FLUSH_CHECK_SUM (7*CLUB)

Expand Down

0 comments on commit 1965dc2

Please sign in to comment.