Skip to content

Commit

Permalink
Merge branch 'release/v1.7.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
kennethshackleton committed Sep 23, 2017
2 parents bc2a3e6 + 09bd06c commit 8fd084b
Show file tree
Hide file tree
Showing 11 changed files with 5,880 additions and 6,447 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
@@ -1,13 +1,18 @@
### Change Log

#### 1.7.1

* Reduce rank hash table by 9%.
* Slight performance gains.

#### 1.7.0

* Reduce potential memory footprint by 40%.
* Reduce required memory footprint by 40%.
* Slight performance gains.

#### 1.6.0

* Optimise ranking flushes.
* Optimise ranking flushes by removing branching in this case.

#### 1.5.0

Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Expand Up @@ -6,7 +6,7 @@ project(${PROJECT_NAME})
# Versioning.
set(SK_POKER_EVAL_VERSION_MAJOR 1)
set(SK_POKER_EVAL_VERSION_MINOR 7)
set(SK_POKER_EVAL_VERSION_PATCH 0)
set(SK_POKER_EVAL_VERSION_PATCH 1)

# Get the current commit.
execute_process(
Expand Down
20 changes: 17 additions & 3 deletions README.md
Expand Up @@ -19,15 +19,29 @@ int main() {
}
```

The implementation being immutable is already thread-safe and there is no initialisation time.

## 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 250kB 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 of 240kB 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.

Similarly, we assign the integer values 0, 1, 8 and 57 for spade, heart, diamond and club respectively. Any sum of exactly seven values taken from {0, 1, 8, 57} is unique among all such sums. We add up the suits of a 7-card hand to produce a "flush check" key and use this to find a pre-calculated flush suit value (in the case we're looking at a flush) or otherwise a defined non-flush constant. The largest flush key we see is 7999, corresponding to any of the four 7-card straight flushes with ace high, and the largest suit key is 399.
Similarly, we assign the integer values 0, 1, 8 and 57 for spade, heart, diamond and club respectively. Any sum of exactly seven values taken from {0, 1, 8, 57} is unique among all such sums. We add up the suits of a 7-card hand to produce a "flush check" key and use this to look up the flush suit value if any. The largest flush key we see is 7999, corresponding to any of the four 7-card straight flushes with ace high, and the largest suit key is 399.

The extraordinarily lucky aspect of this is that the maximum non-flush key we have, 7825759, is a 23-bit integer (note 2^23 = 8388608) and the largest suit key we find, 57*7 = 399, is a 9-bit integer (note 2^9 = 512). If we bit-shift each card's flush check and add to this its non-flush face value to make a card key in advance, when we aggregate the resulting card keys over a given 7-card hand we generate a 23+9 = 32-bit integer key for the whole hand. This integer key can only just be accommodated on a 32-bit machine and yet still carries enough information to decide if we're looking at a flush and if not to then look up the rank of the hand.

## How has the project evolved?

Taking v1.1 as the base line, the sampled relative throughput of random [SevenEval](https://github.com/kennethshackleton/SKPokerEval/blob/develop/src/SevenEval.h) access has been seen to have changed as follows (a higher multiple is better).

The extraordinarily lucky aspect of this is that the maximum non-flush key we have, 7825759, is a 23-bit integer (note 2^23 = 8388608) and the largest suit key we find, 57*7 = 399, is a 9-bit integer (note 2^9 = 512). If we bit-shift a card's non-flush face value and add to this its flush check to make a card key in advance, when we aggregate the resulting card keys over a given 7-card hand we generate a 23+9 = 32-bit integer key for the whole hand. This integer key can only just be accommodated on a 32-bit machine and yet still carries enough information to decide if we're looking at a flush and if not to then look up the rank of the hand.
| Version | Relative throughput | Reason                            |
| ------- | ------------------: | :-------------------------------- |
| [1.1](https://github.com/kennethshackleton/SKPokerEval/releases/tag/v1.1)   |               1.00 |                                   |
| [1.4.2](https://github.com/kennethshackleton/SKPokerEval/releases/tag/v1.4.2) |               1.18 | Hashing.                          |
| [1.6](https://github.com/kennethshackleton/SKPokerEval/releases/tag/v1.6)     |               1.50 | Remove branching from flush case. |
| [1.7](https://github.com/kennethshackleton/SKPokerEval/releases/tag/v1.7)     |               1.53 | Reduce the hash table.            |
| [1.7.1](https://github.com/kennethshackleton/SKPokerEval/releases/tag/v1.7.1) |               1.56 | Reduce the rank hash table.      |

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

Expand Down
9 changes: 3 additions & 6 deletions src/Constants.h
Expand Up @@ -34,6 +34,7 @@
#define INDEX_HEART 1
#define INDEX_DIAMOND 2
#define INDEX_CLUB 3
#define NOT_A_SUIT (-1)

#define TWO_FIVE 0
#define THREE_FIVE 1
Expand Down Expand Up @@ -96,12 +97,8 @@

#define MAX_FLUSH_CHECK_SUM (7*CLUB)

// Used in flush checking. These must be distinct from each of the suits.
#define UNVERIFIED (-1)
#define NOT_A_FLUSH (-2)

// Bit masks
#define NON_FLUSH_BIT_SHIFT 9
#define SUIT_BIT_MASK ((1<<NON_FLUSH_BIT_SHIFT)-1)
#define FLUSH_BIT_SHIFT 23
#define FACE_BIT_MASK ((1 << FLUSH_BIT_SHIFT) - 1)

#endif // SKPOKEREVAL_CONSTANTS_H_
128 changes: 64 additions & 64 deletions src/Deckcards.h
Expand Up @@ -24,70 +24,70 @@
#include <cstdint>

uint_fast32_t const card[DECK_SIZE] = {
(ACE << NON_FLUSH_BIT_SHIFT) + SPADE,
(ACE << NON_FLUSH_BIT_SHIFT) + HEART,
(ACE << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(ACE << NON_FLUSH_BIT_SHIFT) + CLUB,

(KING << NON_FLUSH_BIT_SHIFT) + SPADE,
(KING << NON_FLUSH_BIT_SHIFT) + HEART,
(KING << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(KING << NON_FLUSH_BIT_SHIFT) + CLUB,

(QUEEN << NON_FLUSH_BIT_SHIFT) + SPADE,
(QUEEN << NON_FLUSH_BIT_SHIFT) + HEART,
(QUEEN << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(QUEEN << NON_FLUSH_BIT_SHIFT) + CLUB,

(JACK << NON_FLUSH_BIT_SHIFT) + SPADE,
(JACK << NON_FLUSH_BIT_SHIFT) + HEART,
(JACK << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(JACK << NON_FLUSH_BIT_SHIFT) + CLUB,

(TEN << NON_FLUSH_BIT_SHIFT) + SPADE,
(TEN << NON_FLUSH_BIT_SHIFT) + HEART,
(TEN << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(TEN << NON_FLUSH_BIT_SHIFT) + CLUB,

(NINE << NON_FLUSH_BIT_SHIFT) + SPADE,
(NINE << NON_FLUSH_BIT_SHIFT) + HEART,
(NINE << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(NINE << NON_FLUSH_BIT_SHIFT) + CLUB,

(EIGHT << NON_FLUSH_BIT_SHIFT) + SPADE,
(EIGHT << NON_FLUSH_BIT_SHIFT) + HEART,
(EIGHT << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(EIGHT << NON_FLUSH_BIT_SHIFT) + CLUB,

(SEVEN << NON_FLUSH_BIT_SHIFT) + SPADE,
(SEVEN << NON_FLUSH_BIT_SHIFT) + HEART,
(SEVEN << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(SEVEN << NON_FLUSH_BIT_SHIFT) + CLUB,

(SIX << NON_FLUSH_BIT_SHIFT) + SPADE,
(SIX << NON_FLUSH_BIT_SHIFT) + HEART,
(SIX << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(SIX << NON_FLUSH_BIT_SHIFT) + CLUB,

(FIVE << NON_FLUSH_BIT_SHIFT) + SPADE,
(FIVE << NON_FLUSH_BIT_SHIFT) + HEART,
(FIVE << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(FIVE << NON_FLUSH_BIT_SHIFT) + CLUB,

(FOUR << NON_FLUSH_BIT_SHIFT) + SPADE,
(FOUR << NON_FLUSH_BIT_SHIFT) + HEART,
(FOUR << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(FOUR << NON_FLUSH_BIT_SHIFT) + CLUB,

(THREE << NON_FLUSH_BIT_SHIFT) + SPADE,
(THREE << NON_FLUSH_BIT_SHIFT) + HEART,
(THREE << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(THREE << NON_FLUSH_BIT_SHIFT) + CLUB,

(TWO << NON_FLUSH_BIT_SHIFT) + SPADE,
(TWO << NON_FLUSH_BIT_SHIFT) + HEART,
(TWO << NON_FLUSH_BIT_SHIFT) + DIAMOND,
(TWO << NON_FLUSH_BIT_SHIFT) + CLUB
ACE + (SPADE << FLUSH_BIT_SHIFT),
ACE + (HEART << FLUSH_BIT_SHIFT),
ACE + (DIAMOND << FLUSH_BIT_SHIFT),
ACE + (CLUB << FLUSH_BIT_SHIFT),

KING + (SPADE << FLUSH_BIT_SHIFT),
KING + (HEART << FLUSH_BIT_SHIFT),
KING + (DIAMOND << FLUSH_BIT_SHIFT),
KING + (CLUB << FLUSH_BIT_SHIFT),

QUEEN + (SPADE << FLUSH_BIT_SHIFT),
QUEEN + (HEART << FLUSH_BIT_SHIFT),
QUEEN + (DIAMOND << FLUSH_BIT_SHIFT),
QUEEN + (CLUB << FLUSH_BIT_SHIFT),

JACK + (SPADE << FLUSH_BIT_SHIFT),
JACK + (HEART << FLUSH_BIT_SHIFT),
JACK + (DIAMOND << FLUSH_BIT_SHIFT),
JACK + (CLUB << FLUSH_BIT_SHIFT),

TEN + (SPADE << FLUSH_BIT_SHIFT),
TEN + (HEART << FLUSH_BIT_SHIFT),
TEN + (DIAMOND << FLUSH_BIT_SHIFT),
TEN + (CLUB << FLUSH_BIT_SHIFT),

NINE + (SPADE << FLUSH_BIT_SHIFT),
NINE + (HEART << FLUSH_BIT_SHIFT),
NINE + (DIAMOND << FLUSH_BIT_SHIFT),
NINE + (CLUB << FLUSH_BIT_SHIFT),

EIGHT + (SPADE << FLUSH_BIT_SHIFT),
EIGHT + (HEART << FLUSH_BIT_SHIFT),
EIGHT + (DIAMOND << FLUSH_BIT_SHIFT),
EIGHT + (CLUB << FLUSH_BIT_SHIFT),

SEVEN + (SPADE << FLUSH_BIT_SHIFT),
SEVEN + (HEART << FLUSH_BIT_SHIFT),
SEVEN + (DIAMOND << FLUSH_BIT_SHIFT),
SEVEN + (CLUB << FLUSH_BIT_SHIFT),

SIX + (SPADE << FLUSH_BIT_SHIFT),
SIX + (HEART << FLUSH_BIT_SHIFT),
SIX + (DIAMOND << FLUSH_BIT_SHIFT),
SIX + (CLUB << FLUSH_BIT_SHIFT),

FIVE + (SPADE << FLUSH_BIT_SHIFT),
FIVE + (HEART << FLUSH_BIT_SHIFT),
FIVE + (DIAMOND << FLUSH_BIT_SHIFT),
FIVE + (CLUB << FLUSH_BIT_SHIFT),

FOUR + (SPADE << FLUSH_BIT_SHIFT),
FOUR + (HEART << FLUSH_BIT_SHIFT),
FOUR + (DIAMOND << FLUSH_BIT_SHIFT),
FOUR + (CLUB << FLUSH_BIT_SHIFT),

THREE + (SPADE << FLUSH_BIT_SHIFT),
THREE + (HEART << FLUSH_BIT_SHIFT),
THREE + (DIAMOND << FLUSH_BIT_SHIFT),
THREE + (CLUB << FLUSH_BIT_SHIFT),

TWO + (SPADE << FLUSH_BIT_SHIFT),
TWO + (HEART << FLUSH_BIT_SHIFT),
TWO + (DIAMOND << FLUSH_BIT_SHIFT),
TWO + (CLUB << FLUSH_BIT_SHIFT)
};

uint_fast8_t const suit[DECK_SIZE] = {
Expand Down
26 changes: 13 additions & 13 deletions src/FlushCheck.h
Expand Up @@ -23,19 +23,19 @@
#include <cstdint>

int_fast8_t const flush_check[400] = {
0, 0, 0, -2, -2, 1, 1, 1, 0, 0, -2, -2, -2, 1, 1, -1, 0, -2, -2, -2, -2, 1,
-1, -1, -2, -2, -2, -2, -2, -1, -1, -1, -2, -2, -2, -2, -1, -1, -1, -1, 2, 2,
2, -1, -1, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, 2, 0, 0, -2, -2, -2, 1, 1,
-1, 0, -2, -2, -2, -2, 1, -1, -1, -2, -2, -2, -2, -2, -1, -1, -1, -2, -2, -2,
-2, -1, -1, -1, -1, -2, -2, -2, -1, -1, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1,
-1, 2, -1, -1, -1, -1, -1, -1, -1, -1, 0, -2, -2, -2, -2, 1, -1, -1, -2, -2,
-2, -2, -2, -1, -1, -1, -2, -2, -2, -2, -1, -1, -1, -1, -2, -2, -2, -1, -1, -1,
-1, -1, -2, -2, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1, -1, -2, -2, -2, -2, -1,
-1, -1, -1, -2, -2, -2, -1, -1, -1, -1, -1, -2, -2, -1, -1, -1, -1, -1, -1, -2,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -2, -2, -2, -2, -1, -1, -1, -1, -2, -2, -2, -1, -1, -1, -1, -1,
-2, -2, -1, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 0, 0, -1, -1, 1, 1, 1, 0, 0, -1, -1, -1, 1, 1, -1, 0, -1, -1, -1, -1, 1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2,
2, -1, -1, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, 2, 0, 0, -1, -1, -1, 1, 1,
-1, 0, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1,
-1, 2, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, 1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 3, 3, 3, -1, -1, -1, -1, -1, 3, 3, -1, -1, -1, -1, -1, -1, 3, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
Expand Down

0 comments on commit 8fd084b

Please sign in to comment.