Open Side Menu Go to the Top
Register
7 Card Hand Evaluators 7 Card Hand Evaluators

03-03-2007 , 03:37 PM
Quote:
Maybe drop to lower level routines, fread, read, or whatever is the native OS calls for the OS you are using, with an upped buffer size?
I don't think that will have any effect on speed either. Since the fread(...) function is just C's equivalent of C++'s inFile.read(...) .

I didn't know there could be a problem with the stack, thanks Yuk, I guess, I put the keyword static infront of my array. Not that I noticed any difference but it felt right
7 Card Hand Evaluators Quote
03-04-2007 , 09:34 AM
<font class="small">Code:</font><hr /><pre>
#include "stdio.h"
#include "time.h"

main( )
{
int duration = time(0);

static int HR[32487834];
FILE *fp;
fp = fopen("HandRanks.dat", "r");
fread(HR, sizeof(HR), 1, fp);
fclose(fp);

duration = time(0) - duration;
printf("it took %d seconds\n", duration);
}
</pre><hr />

Using c source and a c compiler, output is:
it took 115 seconds
7 Card Hand Evaluators Quote
03-04-2007 , 11:19 AM
Quote:
<font class="small">Code:</font><hr /><pre>
#include "stdio.h"
#include "time.h"

main( )
{
int duration = time(0);

static int HR[32487834];
FILE *fp;
fp = fopen("HandRanks.dat", "r");
fread(HR, sizeof(HR), 1, fp);
fclose(fp);

duration = time(0) - duration;
printf("it took %d seconds\n", duration);
}
</pre><hr />

Using c source and a c compiler, output is:
it took 115 seconds
Hi,
My routine is very much like this to read in the Handranks. Seems that your PC might be paging memory out to the disk (check to see your free memory). Keep in mind you need about 120MB free for this to work. This is my first guess... I have other thoughts if this isn't your issue...

Ray...
7 Card Hand Evaluators Quote
03-04-2007 , 05:34 PM

I think I found the problem, I tried running the code on my windows machine at home and this time it took 26 seconds. The other times I've tried it I've used the univesity's linux machines, and I think reading off a remote hard-drive has added some latency to the process. I'm pretty sure the whole file is in memory because when testing all 52C7 it takes a fraction of a second to complete.
7 Card Hand Evaluators Quote
03-07-2007 , 12:55 PM
Extending the discussion on hand evaluators, the next step is perhaps applications of hand evaluators.

Presenting an example with c++ source below, we pick up Kh9h on the preflop, we ask what the pot equities are against 2c2d, 2c2h, ..., AhAs. There are 1225 opponent combinations.

In the following code all pot equities are calculated and placed into the array "float playerEquities[1326]".

In an opponent modelling scheme, take the opponent probability distribution and multiply with the playerEquities.

SIGMA(w(i) * Eq(i))

That is the sum of all the opponent probability weights with the pot equities. On a random = flat distribution, all the weights are 1/1225, so we can sum the equities and do the division at the end.

You need Ray's "HandRanks.dat" to run the code.
And a file called "functions.cpp", code for this follows


<font class="small">Code:</font><hr /><pre>
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;ctime&gt;
#include "functions.cpp"

using namespace std;

unsigned int subDeck = 0;
unsigned int topDeck = 0;


int main(void)
{
//******* Read Ray's File
static int HR[32487834];

cout &lt;&lt; "loading cards, in some cases be more patient than others..." &lt;&lt; endl;
ifstream myFile ("HandRanks.dat", ios::in | ios::binary);
myFile.read((char*) HR, sizeof(HR));
myFile.close();

cout &lt;&lt; "loading completed" &lt;&lt; endl;

int duration = time(0);

//******** Assign Deck
subDeck = 0xffffffff;
topDeck = 0x000fffff;

//********** This example assigns {Kh, 9h} to the player
int playerCard1 = 46; //Kh
int playerCard2 = 30; //9h
extractCard(playerCard1);
extractCard(playerCard2);

//Save state of deck after player has been dealt.
int subDeckSP1 = subDeck; //SP = Save Point, "1" is 1 player dealt
int topDeckSP1 = topDeck;

//Need to build a 50 card array which contains all the cards except Kh and 9h.
int preFlopUnknowns[50];
buildNaturalArray(preFlopUnknowns);


int combinadic;

//this is where all the equity results are stored
//the lookup index is found from the 2 card combinadic
float playerEquities[1326];
memset(playerEquities, 0, sizeof(playerEquities));

//Entry 1 is constant for this calculation, so:
int entry1 = HR[HR[53 + playerCard1 + 1] + playerCard2 + 1]; //+1 -MOVE INTO RAY SYST.



//Now the strategy is to go through every preflop opponent possibility,
//go through them in combinadics natural order which is
//{0,1}, {0,2}, {1,2}, {0,3}, {1,4}, {2,4}, {3,4}, ... {48, 49}

for (int OC1 = 0; OC1 &lt;= 49; OC1++)
for (int OC0 = 0; OC0 &lt; OC1; OC0++)
{
//here OC0 is always going to represent the larger of the
//two cards



int opponentCard0 = preFlopUnknowns[OC0];
int opponentCard1 = preFlopUnknowns[OC1];

combinadic = (opponentCard1 * (opponentCard1-1)) / 2 + opponentCard0;

int entry2 = HR[HR[53 + opponentCard0 + 1] + opponentCard1 + 1];

cout &lt;&lt; "\rcombinadic: " &lt;&lt; combinadic;

//Make sure the deck is at the correct Save Point:
subDeck = subDeckSP1;
topDeck = topDeckSP1;
extractCard(opponentCard0);
extractCard(opponentCard1);
//It's called RAY array, because Ray doesn't use my system
//I call my system NATURAL, and I call Ray's system for RAY
int RC[48]; //RC = Remaining Cards
buildRayArray(RC);


int results[1712304];
int c0, c1, c2, c3, c4;
int u0, u1, u2, u3;

int count = 0;

for (c0 = 0; c0 &lt; 48; c0++) {
u0 = HR[entry1+RC[c0]];
for (c1 = c0+1; c1 &lt; 48; c1++) {
u1 = HR[u0+RC[c1]];
for (c2 = c1+1; c2 &lt; 48; c2++) {
u2 = HR[u1+RC[c2]];
for (c3 = c2+1; c3 &lt; 48; c3++) {
u3 = HR[u2+RC[c3]];
for (c4 = c3+1; c4 &lt; 48; c4++) {
results[count++] = HR[u3+RC[c4]];
}
}
}
}
}

count = 0;

for (c0 = 0; c0 &lt; 48; c0++) {
u0 = HR[entry2+RC[c0]];
for (c1 = c0+1; c1 &lt; 48; c1++) {
u1 = HR[u0+RC[c1]];
for (c2 = c1+1; c2 &lt; 48; c2++) {
u2 = HR[u1+RC[c2]];
for (c3 = c2+1; c3 &lt; 48; c3++) {
u3 = HR[u2+RC[c3]];
for (c4 = c3+1; c4 &lt; 48; c4++) {
results[count++] -= HR[u3+RC[c4]];
}
}
}
}
}

int equityCount = 0;
for (int i=0; i&lt;1712304; i++)
{
if (results[i] &gt; 0) equityCount += 2;
if (results[i] == 0) equityCount += 1;
}

float equity = equityCount / 3424608.0;
playerEquities[combinadic] = equity;
}

cout &lt;&lt; endl;


duration = time(0) - duration;
cout &lt;&lt; "It took " &lt;&lt; duration &lt;&lt; " seconds" &lt;&lt; endl;


//CHECK: Sum the player equities and divide by 50C2 = 1225

float sumOfEquities = 0;

for (int i=0; i&lt;1326; i++)
{
sumOfEquities += playerEquities[i];
}



float meanEquity = sumOfEquities / 1225;

cout &lt;&lt; "Kh9h equity against a random hand is: " &lt;&lt; meanEquity &lt;&lt; endl;


return 0;
}

</pre><hr />

//***************** functions.cpp

<font class="small">Code:</font><hr /><pre>
extern unsigned int subDeck;
extern unsigned int topDeck;


int extractCard(int cardNumber)
{
if (cardNumber &lt; 32)
{
subDeck ^= 1 &lt;&lt; cardNumber;
}
else
{
cardNumber -= 32;
topDeck ^= 1 &lt;&lt; cardNumber;

}
}



void buildRayArray(int* remainingCards)
{
int positionCounter = 0;

for (int i=0; i&lt;32; i++)
if (subDeck &amp; (1 &lt;&lt; i))
remainingCards[positionCounter++] = i + 1;


for (int i=0; i&lt;20; i++)
if (topDeck &amp; (1 &lt;&lt; i))
remainingCards[positionCounter++] = i + 32 + 1;
}


void buildNaturalArray(int* remainingCards)
{
int positionCounter = 0;

for (int i=0; i&lt;32; i++)
if (subDeck &amp; (1 &lt;&lt; i))
remainingCards[positionCounter++] = i;


for (int i=0; i&lt;20; i++)
if (topDeck &amp; (1 &lt;&lt; i))
remainingCards[positionCounter++] = i + 32;
}


/*
* Use below print methods for purposes of testing, for numerical calculations
* only these may be removed.
*
*/



const char cardsNatural[][3] =
{"2c", "2d", "2h", "2s", "3c", "3d", "3h", "3s", "4c", "4d", "4h", "4s", "5c",
"5d", "5h", "5s", "6c", "6d", "6h", "6s", "7c", "7d", "7h", "7s", "8c", "8d",
"8h", "8s", "9c", "9d", "9h", "9s", "Tc", "Td", "Th", "Ts", "Jc", "Jd", "Jh",
"Js", "Qc", "Qd", "Qh", "Qs", "Kc", "Kd", "Kh", "Ks", "Ac", "Ad", "Ah", "As"
};


void printDeck()
{
for (int i=0; i&lt;32; i++)
if (subDeck &amp; (1 &lt;&lt; i))
std::cout &lt;&lt; cardsNatural[i] &lt;&lt; std::endl;



for (int i=0; i&lt;20; i++)
if (topDeck &amp; (1 &lt;&lt; i))
std::cout &lt;&lt; cardsNatural[i+32] &lt;&lt; std::endl;

}
</pre><hr />


OUTPUT IS:

loading cards, in some cases be more patient than others...
loading completed
combinadic: 1325
It took 45 seconds
Kh9h equity against a random hand is: 0.599885
7 Card Hand Evaluators Quote
03-07-2007 , 08:57 PM
Sorry for those who are hurting their heads analysing the above, there are two mistakes in the comments.

The natural order of the combinadics should be:
{0,1}, {0,2}, {1,2}, {0,3}, {1,3}, {2,3}, {0.4}, {1,4}, {2,4}, {3,4}, ... {48, 49}

hopefully I got it right this time... let these be ={c0, c1} we can check this by running it throught the combinadics formula for two indexes
(c1 * (c1-1)) / 2 + c0. Should produce

0, 1, 2, 3, 4, 5,..., 1224

Let's see - yes, is good.

This leads me to the second error where I point out just below the double for loop that OC0 is always the largest, it is in fact OC1 which is always the largest. I got it right in the code anyway...

well, enjoy
7 Card Hand Evaluators Quote
03-30-2007 , 01:17 AM
I last visited these forums quite a few months ago. After reading a few of the ideas here I began coding my own evaluator in Java. It was based upon the tabled lookup ideas from the forum. After returning here a little while ago I was amazed at the amount of progress that had been made. Though my algorithm was quite similar to some of the ones presented here it was not nearly as fast by almost a factor of two. In any case, after looking at some of the code I have made some improvements. There was some some unnecessary code in various places. For instance, the "suit iterator" part of Ray's C code was not necessary. It is fine (and also semantically correct) to leave the undefined suit with a value of zero. The bitwise ANDs used in Cactus Kev's evaluator take care of that case. The other changes are apparent upon inspection. In any case, here is a trimmed and somewhat optimized Java version. I placed the Flushes, etc. lookup tables in static arrays in their own wrapper classes. Java's constructor size limitation forced this.
<font class="small">Code:</font><hr /><pre>
public class Evaluator {

/* Card to integer conversions:

2c = 1 2d = 14 2h = 27 2s = 40
3c = 2 3d = 15 3h = 28 3s = 41
4c = 3 4d = 16 4h = 29 4s = 42
5c = 4 5d = 17 5h = 30 5s = 43
6c = 5 6d = 18 6h = 31 6s = 44
7c = 6 7d = 19 7h = 32 7s = 45
8c = 7 8d = 20 8h = 33 8s = 46
9c = 8 9d = 21 9h = 34 9s = 47
Tc = 9 Td = 22 Th = 35 Ts = 48
Jc = 10 Jd = 23 Jh = 36 Js = 49
Qc = 11 Qd = 24 Qh = 37 Qs = 50
Kc = 12 Kd = 25 Kh = 38 Ks = 51
Ac = 13 Ad = 26 Ah = 39 As = 52

*/

public final static int NO_SUIT = 0;
public final static int CLUBS = 1;
public final static int DIAMONDS = 2;
public final static int HEARTS = 3;
public final static int SPADES = 4;

public final static int BAD_CARD = -1;
public final static int DEUCE = 2;
public final static int THREE = 3;
public final static int FOUR = 4;
public final static int FIVE = 5;
public final static int SIX = 6;
public final static int SEVEN = 7;
public final static int EIGHT = 8;
public final static int NINE = 9;
public final static int TEN = 10;
public final static int JACK = 11;
public final static int QUEEN = 12;
public final static int KING = 13;
public final static int ACE = 14;

public final static int NUM_SUITS = 4;
public final static int NUM_RANKS = 13;
public final static int NUM_CARDS = 52;

public static int[] handRanks = new int[32487834]; // array to hold hand rank lookup table
public static boolean verbose = true; // toggles verbose mode

private static int[] hand; // re-usable array to hold cards in a hand
private static long[] keys = new long[612978]; // array to hold key lookup table
private static int numKeys = 1; // counter for number of defined keys in key array
private static long maxKey = 0; // holds current maximum key value
private static int numCards = 0; // re-usable counter for number of cards in a hand
private static int cardIndex = 0; // re-usable index for cards in a hands
private static int maxHandRankIndex = 0;

private static long startTimer;
private static long stopTimer;


// Inserts a key into the key array and returns the insertion index.
public static int insertKey(long key) {

// check to see if key is valid
if (key == 0) {
return 0;
}

// short circuit insertion for most common cases
if (key &gt;= maxKey) {
if (key &gt; maxKey) {
keys[numKeys++] = key; // appends the new key to the key array
maxKey = key;
}
return numKeys - 1;
}

// use binary search to find insertion point for new key
int low = -1;
int high = numKeys;
int pivot;
long difference;

while (high - low &gt; 1) {
pivot = (low + high) &gt;&gt;&gt; 1;
difference = keys[pivot] - key;
if (difference &gt; 0) {
high = pivot;
}
else if (difference &lt; 0) {
low = pivot;
}
else {
return pivot; // key already exists
}
}

// key does not exist so must be inserted
System.arraycopy(keys, high, keys, high + 1, numKeys - high);
keys[high] = key;

numKeys++;
return high;

} // END insertKey method



// Returns a key for the hand created by adding a new card to the hand
// represented by the given key. Returns 0 if new card already appears in hand.
private static long makeKey(long baseKey, int newCard) {

int[] suitCount = new int[NUM_SUITS + 1]; // number of times a suit appears in a hand
int[] rankCount = new int[NUM_RANKS + 1]; // number of times a rank appears in a hand
hand = new int[8];

// extract the hand represented by the key value
for (cardIndex = 0; cardIndex &lt; 6; cardIndex++) {

// hand[0] is used to hold the new card
hand[cardIndex + 1] = (int)((baseKey &gt;&gt;&gt; (8 * cardIndex)) &amp; 0xFF);
}

hand[0] = formatCard8bit(newCard);

// examine the hand to determine number of cards and rank/suit counts
for (numCards = 0; hand[numCards] != 0; numCards++) {
suitCount[hand[numCards] &amp; 0xF]++;
rankCount[(hand[numCards] &gt;&gt;&gt; 4) &amp; 0xF]++;

// check to see if new card is already contained in hand (rank and suit considered)
if (numCards != 0 &amp;&amp; hand[0] == hand[numCards]) {
return 0;
}
}

// check to see if we already have four of a particular rank
if (numCards &gt; 4) {
for (int rank = 1; rank &lt; 14; rank++) {
if (rankCount[rank] &gt; 4) return 0;
}
}

// determine the minimum number of suits required for a flush to be possible
int minSuitCount = numCards - 2;

// check to see if suit is significant
if (minSuitCount &gt; 1) {
// examine each card in the hand
for (cardIndex = 0; cardIndex &lt; numCards; cardIndex++) {
// if the suit is not significant then strip it from the card
if (suitCount[hand[cardIndex] &amp; 0xF] &lt; minSuitCount) {
hand[cardIndex] &amp;= 0xF0;
}
}
}

sortHand();

long key = 0;
for (int i = 0; i &lt; 7; i++) {
key += (long)hand[i] &lt;&lt; (i * 8);
}

return key;

} // END makeKey method



// Formats and returns a card in 8-bit packed representation.
private static int formatCard8bit(int card) {

// 8-Bit Packed Card Representation
// +--------+
// |rrrr--ss|
// +--------+
// r = rank of card (deuce = 1, trey = 2, four = 3, five = 4,..., ace = 13)
// s = suit of card (suits are arbitrary, can take value from 0 to 3)

card--;
return (((card &gt;&gt;&gt; 2) + 1) &lt;&lt; 4) + (card &amp; 3) + 1;

} // END formatCard8bit method



// Sorts the hand using Bose-Nelson Sorting Algorithm (N = 7).
private static void sortHand() {
swapCard(0, 4);
swapCard(1, 5);
swapCard(2, 6);
swapCard(0, 2);
swapCard(1, 3);
swapCard(4, 6);
swapCard(2, 4);
swapCard(3, 5);
swapCard(0, 1);
swapCard(2, 3);
swapCard(4, 5);
swapCard(1, 4);
swapCard(3, 6);
swapCard(1, 2);
swapCard(3, 4);
swapCard(5, 6);
} // End sortHand method



// Swaps card i with card j.
private static void swapCard(int i, int j) {
if (hand[i] &lt; hand[j]) {
hand[i] ^= hand[j];
hand[j] ^= hand[i];
hand[i] ^= hand[j];
}
} // END swapCard method



// Determines the relative strength of a hand (the hand is given by its unique key value).
private static int getHandRank(long key) {

// The following method implements a modified version of "Cactus Kev's Five-Card
// Poker Hand Evaluator" to determine the relative strength of two five-card hands.
// Reference: http://www.suffecool.net/poker/evaluator.html

hand = new int[8];
int currentCard;
int rank;
int handRank = 9999;
int holdrank = 9999;
int suit = 0;
int numCards = 0;

final int[] primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};

if (key != 0) {

for (cardIndex = 0; cardIndex &lt; 7; cardIndex++) {

currentCard = (int)((key &gt;&gt;&gt; (8 * cardIndex)) &amp; 0xFF);
if (currentCard == 0) break;
numCards++;

// Cactus Kev Card Representation
// +--------+--------+--------+--------+
// |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp|
// +--------+--------+--------+--------+
// p = prime number of rank (deuce = 2, trey = 3, four = 5, five = 7,..., ace = 41)
// r = rank of card (deuce = 0, trey = 1, four = 2, five = 3,..., ace = 12)
// cdhs = suit of card
// b = bit turned on depending on rank of card

// extract suit and rank from 8-bit packed representation
rank = (currentCard &gt;&gt;&gt; 4) - 1;
suit = currentCard &amp; 0xF;

// change card representation to Cactus Kev Representation
hand[cardIndex] = primes[rank] | (rank &lt;&lt; 8) | (1 &lt;&lt; (suit + 11)) | (1 &lt;&lt; (16 + rank));
}

switch (numCards) {
case 5 :

holdrank = eval_5hand(hand[0],hand[1],hand[2],hand[3],hand[4]);
break;

case 6 :

// Cactus Kev's Evaluator ranks hands from 1 (Royal Flush) to 7462 (Seven High Card)
holdrank = eval_5hand( hand[0],hand[1],hand[2],hand[3],hand[4]);
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[3],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[3],hand[4],hand[5]));
break;

case 7 :

holdrank = eval_5hand( hand[0],hand[1],hand[2],hand[3],hand[4]);
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[3],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[3],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[4],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[3],hand[4],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[3],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[3],hand[4],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[3],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[3],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[3],hand[4],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[3],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[3],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[2],hand[3],hand[4],hand[5],hand[6]));
break;

default :

System.out.println("ERROR: Invalid hand in GetRank method.");
break;

}

// Hand Rank Representation
// +--------+--------+
// |hhhheeee|eeeeeeee|
// +--------+--------+
// h = poker hand (1 = High Card, 2 = One Pair, 3 = Two Pair,..., 9 = Straight Flush)
// e = equivalency class (Rank of equivalency class relative to base hand)

// +-----------------------------------+----------------------------------+-----------------+
// 5-Card Equivalency Classes 7-Card Equivalency Classes
// +-----------------------------------+----------------------------------+-----------------+
// 1277 407 High Card
// 2860 1470 One Pair
// 858 763 Two Pair
// 858 575 Three of a Kind
// 10 10 Straight
// 1277 1277 Flush
// 156 156 Full House
// 156 156 Four of a Kind
// 10 10 Straight Flush
// +----------+------------------------+----------------------------------+-----------------+
// Total: 7462 4824
// +----------+------------------------+----------------------------------+-----------------+

handRank = 7463 - holdrank; // Invert ranking metric (1 is now worst hand)

if (handRank &lt; 1278) handRank = handRank - 0 + 4096 * 1; // High Card
else if (handRank &lt; 4138) handRank = handRank - 1277 + 4096 * 2; // One Pair
else if (handRank &lt; 4996) handRank = handRank - 4137 + 4096 * 3; // Two Pair
else if (handRank &lt; 5854) handRank = handRank - 4995 + 4096 * 4; // Three of a Kind
else if (handRank &lt; 5864) handRank = handRank - 5853 + 4096 * 5; // Straight
else if (handRank &lt; 7141) handRank = handRank - 5863 + 4096 * 6; // Flush
else if (handRank &lt; 7297) handRank = handRank - 7140 + 4096 * 7; // Full House
else if (handRank &lt; 7453) handRank = handRank - 7296 + 4096 * 8; // Four of a Kind
else handRank = handRank - 7452 + 4096 * 9; // Straight Flush

}
return handRank;

} // END getHandRank method



private static int getIndex(int key) {

// use binary search to find key
int low = -1;
int high = 4888;
int pivot;

while (high - low &gt; 1) {
pivot = (low + high) &gt;&gt;&gt; 1;
if (Products.table[pivot] &gt; key) {
high = pivot;
}
else if (Products.table[pivot] &lt; key) {
low = pivot;
}
else {
return pivot;
}
}
return -1;

} // END getIndex method



private static int eval_5hand(int c1, int c2, int c3, int c4, int c5) {
int q = (c1 | c2 | c3 | c4 | c5) &gt;&gt; 16;
short s;

// check for Flushes and Straight Flushes
if ((c1 &amp; c2 &amp; c3 &amp; c4 &amp; c5 &amp; 0xF000) != 0) return Flushes.table[q];

// check for Straights and High Card hands
if ((s = Unique.table[q]) != 0) return s;

q = (c1 &amp; 0xFF) * (c2 &amp; 0xFF) * (c3 &amp; 0xFF) * (c4 &amp; 0xFF) * (c5 &amp; 0xFF);
q = getIndex(q);

return Values.table[q];

} // END eval_5hand method



public static void generateTables() {

int card;
int handRank;
int keyIndex;
long key;

if (verbose) {
System.out.print("\nGenerating and sorting keys...");
startTimer = System.currentTimeMillis();
}

for (keyIndex = 0; keys[keyIndex] != 0 || keyIndex == 0; keyIndex++) {

for (card = 1; card &lt; 53; card++) { // add a card to each previously calculated key
key = makeKey(keys[keyIndex], card); // create the new key

if (numCards &lt; 7) insertKey(key); // insert the new key into the key lookup table
}
}

if (verbose) {
stopTimer = System.currentTimeMillis();
System.out.printf("done.\n\n%35s %d\n", "Number of Keys Generated:", (keyIndex + 1));
System.out.printf("%35s %f seconds\n\n", "Time Required:", ((stopTimer - startTimer) / 1000.0));
System.out.print("Generating hand ranks...");
startTimer = System.currentTimeMillis();
}

for (keyIndex = 0; keys[keyIndex] != 0 || keyIndex == 0; keyIndex++) {

for (card = 1; card &lt; 53; card++) {
key = makeKey(keys[keyIndex], card);

if (numCards &lt; 7) {
handRank = insertKey(key) * 53 + 53; // if number of cards is &lt; 7 insert key
}
else {
handRank = getHandRank(key); // if number of cards is 7 insert hand rank
}

maxHandRankIndex = keyIndex * 53 + card + 53; // calculate hand rank insertion index
handRanks[maxHandRankIndex] = handRank; // populate hand rank lookup table with appropriate value
}

if (numCards == 6 || numCards == 7) {
// insert the hand rank into the hand rank lookup table
handRanks[keyIndex * 53 + 53] = getHandRank(keys[keyIndex]);
}
}

if (verbose) {
stopTimer = System.currentTimeMillis();
System.out.printf("done.\n\n%35s %f seconds\n\n", "Time Required:", ((stopTimer - startTimer) / 1000.0));
}

} // END generateTables method



public static void main (String [] args) {
generateTables();

int c0, c1, c2, c3, c4, c5, c6;
int u0, u1, u2, u3, u4, u5;
int numHands = 0;
int handRank;
int[] handEnumerations = new int[10];
int[][] equivalencyEnumerations = new int[10][3000];
String[] handDescriptions = {"Invalid Hand", "High Card", "One Pair", "Two Pair", "Three of a Kind",
"Straight", "Flush", "Full House", "Four of a Kind", "Straight Flush"};

if (verbose) {
System.out.print("Enumerating hand frequencies and equivalency classes...");
startTimer = System.currentTimeMillis();
}

for (c0 = 1; c0 &lt; 53; c0++) {
u0 = handRanks[53 + c0];
for (c1 = c0 + 1; c1 &lt; 53; c1++) {
u1 = handRanks[u0 + c1];
for (c2 = c1 + 1; c2 &lt; 53; c2++) {
u2 = handRanks[u1 + c2];
for (c3 = c2 + 1; c3 &lt; 53; c3++) {
u3 = handRanks[u2 + c3];
for (c4 = c3 + 1; c4 &lt; 53; c4++) {
u4 = handRanks[u3 + c4];
for (c5 = c4 + 1; c5 &lt; 53; c5++) {
u5 = handRanks[u4 + c5];
for (c6 = c5 + 1; c6 &lt; 53; c6++) {
handRank = handRanks[u5 + c6];
handEnumerations[handRank &gt;&gt;&gt; 12]++;
equivalencyEnumerations[handRank &gt;&gt;&gt; 12][handRank &amp; 0xFFF]++;
numHands++;
}
}
}
}
}
}
}

if (verbose) {
stopTimer = System.currentTimeMillis();
System.out.printf("done.\n\n%35s %f seconds\n\n", "Time Required:", ((stopTimer - startTimer) / 1000.0));
}

System.out.println("SEVEN-CARD POKER HAND FREQUENCIES AND EQUIVALENCY CLASSES\n");
System.out.printf(" %-17s %15s %15s\n", "HAND", "FREQUENCY", "CLASSES");
System.out.println(" -------------------------------------------------");

int sumEquivalency;
int numClasses = 0;
for (int i = handEnumerations.length - 1; i &gt;= 0; i--) {
sumEquivalency = 0;
for (int j = 0; j &lt; equivalencyEnumerations[i].length; j++) {
if (equivalencyEnumerations[i][j] != 0) sumEquivalency++;
}
numClasses += sumEquivalency;
System.out.printf(" %-17s %15d %15d\n", handDescriptions[i], handEnumerations[i], sumEquivalency);
}
System.out.println(" -------------------------------------------------");
System.out.printf(" %-17s %15d %15d\n", "TOTAL", numHands, numClasses);
}

} // END class Evaluator
</pre><hr />
7 Card Hand Evaluators Quote
03-30-2007 , 02:37 AM
Forgot to mention. I stuck in everything statically so I could post it more easily. If you're going to use it it's probably not a bad idea to alter it so it adheres to OO design.
7 Card Hand Evaluators Quote
03-30-2007 , 05:47 PM
And the top should read:
<font class="small">Code:</font><hr /><pre>
2c = 1 2d = 2 2h = 3 2s = 4
3c = 5 3d = 6 3h = 7 3s = 8
4c = 9 4d = 10 4h = 11 4s = 12
5c = 13 5d = 14 5h = 15 5s = 16
6c = 17 6d = 18 6h = 19 6s = 20
7c = 21 7d = 22 7h = 23 7s = 24
8c = 25 8d = 26 8h = 27 8s = 28
9c = 29 9d = 30 9h = 31 9s = 32
Tc = 33 Td = 34 Th = 35 Ts = 36
Jc = 37 Jd = 38 Jh = 39 Js = 40
Qc = 41 Qd = 42 Qh = 43 Qs = 44
Kc = 45 Kd = 46 Kh = 47 Ks = 48
Ac = 49 Ad = 50 Ah = 51 As = 52
</pre><hr />
7 Card Hand Evaluators Quote
05-10-2007 , 01:01 AM
I have been trying to implement some of the suggestions found here in VB, but I realized that I can never achieve high performance because of the way VB handles array accesses (the SAFEARRAY type protects you, but it also slows the code a bit).

I've tried large tables and hash arrays, but the memory overhead is ridiculous. Using VB classes isn't a solution either because of memory and process overhead (I kind of anticipated this, but tried anyway). While VB makes most of the speed-insensitive coding tasks easier, I'm turning to assembly code to do the hand evaluations in an external library (sort of a compromise).

The way I'm doing it now seems relatively efficient and straightforward, though I'm sure there are more complicated (and faster) ways. Here is the outline for the process:

VB has preprocessed the cards into an array Card(1 to 7). VB passes a pointer to Card(1) to the external optimized library. Each card is represented by a 64-bit structure containing one 16-bit integer per suit as recommended by several people. Bits 0-12 are set for each suit to represent the card ranks contained in the hand. The library can easily address the card data using this format.

The evaluator first uses each 16-bit rank mask in an 8K lookup table to determine if a flush is present. It then combines all four rank masks using the OR operation. A different 8K table lookup determines if the hand is a straight or high card hand. The only hand types that remain undetected so far are rank-count-based (pair, 2 pair, trip, full house, quad).

This is where optimization is critical (at least in my case). I have seen methods of using boolean operations on the rank masks to figure out which ranks are paired, triped, etc. However, there is still additional processing to identify the true hand type and final hand rank. Rather than working with the rank masks in this way, I am trying to find assembly code methods that more directly lead to a table access or hand rank.

Idea: If assembly code can quickly sort the ranks from high-low or vice versa accross all four rank masks, then the table size for a 7-vector rank set lookup is reduced from 60M+ to around 50K. I am testing using the BSR (Bit Scan Reverse; finds the index of the high bit in a register) instruction followed by BTR (Bit Test with Reset) to quickly extract the sorted rank bits. The BSR/BTR instructions only take one cycle on modern processors, and so they seem relatively efficient for this purpose. There are two factors I see that may allow for optimization tricks: (1) Each rank mask contains at most 4 set bits (because a flush has been ruled out in an earlier test). (2) All rank masks together contain at most 6 set bits (because a high card hand was also ruled out).

Problem: The high bits may be in different rank masks and the bits are not implicitly ordered between the masks (only within each individual mask). I can extract all seven card ranks, but they would not always be in order.

Could someone with assembly knowledge tell me how they would solve this problem (or anyone who has an alternative)? It may require a specialized trick. I can get the population count of each rank mask using an 8K table lookup if it helps with the solution. Array memory lookups for Core processors probably aren't too bad. The IMUL instruction if needed for other purposes takes about three cycles. The CMOV instruction takes about one cycle and might avoid some JMPs. If seven bytes (the bit indexes) contained in registers can be sorted/swapped quickly, then it alleviates the problem quite a bit - the hand rank is simply the sorted bytes representing the card ranks (4 bits are needed per card index for the rank; all seven cards will fit in a 32-bit register). The problem might come with the fact that VB code could displace the data cache, so I will assume that the 8K tables will not be resident in the L1 cache, therefor I will not expect maximum performance attained by some people. However, I think I can do much better than 100K comparisons per second (should hit at least several million per second using optimized library code).

Once the hand ranking algorithm is fast enough, I can work on my simulation code trying to get 10 bots at a table to converge on optimal play (most people want maximal play, but in limit poker you can leverage time and probabilities to be profitable with lower risk and bankroll volatility; no-limit poker is different altogether as you have a risk of ruin at Sit-n-Go tables that can't be ignored - that's a separate problem I will tackle later).
7 Card Hand Evaluators Quote
05-10-2007 , 10:14 AM
does anyone know a java implementation of an equity calculator for given hands and a board? optional features: allows Ranges like TT+ and AQs+, is fast

i know that there is a poker-eval project, but i have no clue how to compile a DLL that i can call out of java OR use the java native interface to call c functions directly (im too stupid to compile those ;D)

thx in advance
7 Card Hand Evaluators Quote
05-10-2007 , 10:19 AM
Quote:
i know that there is a poker-eval project, but i have no clue how to compile a DLL that i can call out of java OR use the java native interface to call c functions directly (im too stupid to compile those ;D)
The JNI is incredibly easy to use and has a very good tutorial to follow. It pretty much writes all the code stubs and creates the DLL project for you - all you have to do is fill in the functionality and then hit built and you're done. If if you are a Java programmer and not a C/C++ programmer, your knowledge of Java syntax and a quick read of some FAQ about the differences should be enough to interface some C/C++ code via the JNI.

Juk
7 Card Hand Evaluators Quote
05-10-2007 , 10:34 AM
thanks for the reply, i know the JNI isnt that difficult to use (tutorial etc) but the problem is the compilation itself (when i use functions defined in the poker-eval library).

i would spend some hours figuring it all out but first i was gonna ask if something that is ready-to-use is available already

http://svn.gna.org/viewcvs/pokersour...ker-eval/java/
here are some java to pokereval bindings via JNI but when i tried to get it running real quick i failed (not keen with make scripts).
the java code there also has support for hand ranges etc. which is pretty neat - so if nobody has something for me already, i might invest some time and make it work

(i think it could improve the AllinCalc performance a lot if i dont have to run pokenum.exe and parse its output )
7 Card Hand Evaluators Quote
05-10-2007 , 12:26 PM
Quote:
does anyone know a java implementation of an equity calculator for given hands and a board? optional features: allows Ranges like TT+ and AQs+, is fast
Sorry, no optional features:
http://www.stevebrecher.com/Software/software.html
7 Card Hand Evaluators Quote
05-10-2007 , 02:27 PM
Hello Steve,

Thanks for your Code
Problem: Your Enumerator Class has Package visibility and thus isnt directly usable for other java programs. I did copy your source into my own files for this little Test:

http://nopaste.byte-welt.de/view.php?id=357

output:
Quote:
WINS: [1140, 186]
TIED: [0, 0]
PaPo: [0.0, 0.0]
while pokerstove and pokenum give me other results:
Quote:
990 games 0.005 secs 198,000 games/sec

Board: 2c 7d Ts
Dead:

equity win tie pots won pots tied
Hand 0: 91.616% 91.62% 00.00% 907 0.00 { AcAs }
Hand 1: 08.384% 08.38% 00.00% 83 0.00 { KdKh }
Quote:
Holdem Hi: 990 enumerated boards containing Ts 2c 7d
cards win %win lose %lose tie %tie EV
As Ac 907 91.62 83 8.38 0 0.00 0.916
Kd Kh 83 8.38 907 91.62 0 0.00 0.084
did I do anything wrong in my little piece of code?
and why do the printed results change if I change the instance and number of thread parameters for Enumerator??
7 Card Hand Evaluators Quote
05-10-2007 , 03:14 PM
Quote:
... I did copy your source into my own files for this little Test:

http://nopaste.byte-welt.de/view.php?id=357

output:
Quote:
WINS: [1140, 186]
TIED: [0, 0]
PaPo: [0.0, 0.0]
while pokerstove and pokenum give me other results:
[...]
did I do anything wrong in my little piece of code?
Sorry, I can't go over your code just now. Here's the output from the code downloaded from my site:
<font class="small">Code:</font><hr /><pre>
Known hole cards; two per player: acaskdkh
Number of players with unknown hole cards (0 to 2) [0]:
Known board cards [none]: 2c7dts
Dead/exposed cards [none]:
990 deals required. Start dealing? (y/n) [y]:

AcAs KdKh
% chance of outright win 91.616162 8.383838
% chance of win or split 91.616162 8.383838
expected return, % of pot 91.616162 8.383838
fair pot odds:1 0.091510 10.927711
pots won: 907.00 83.00
</pre><hr />
Quote:
and why do the printed results change if I change the instance and number of thread parameters for Enumerator??
Because your parameters are "lies"? The parameters must reflect the number of threads; each of n enumeration threads does 1/n of the work. For thread creation and use, see Showdown.java.
7 Card Hand Evaluators Quote
05-10-2007 , 06:27 PM
thx for the reply...
if you would have taken a quick look at the 22 lines of code i posted, u could have told me earlier that i have to remove the used cards from the deck

nevermind it seems to work well now do you plan to upload a newer version with a public Enumerator class or am i allowed to "copy" this part of your sourcecode ?
7 Card Hand Evaluators Quote
05-10-2007 , 06:32 PM
Quote:
do you plan to upload a newer version with a public Enumerator class or am i allowed to "copy" this part of your sourcecode ?
Copy at will!
7 Card Hand Evaluators Quote
05-10-2007 , 08:22 PM
thank you my program is almost 2 times faster now
7 Card Hand Evaluators Quote
05-20-2007 , 03:07 PM
The number of unique 6 card IDs can be reduced from about 350,000 to about 280,000 by setting the rank of cards that won't factor into the final hand to 0. If the hand is already a flush, for example, any non-suited cards will be irrelevant. This is the single biggest reduction, but smaller ones can be made for four of a kind, full houses, flushes and straights.
7 Card Hand Evaluators Quote
06-18-2007 , 05:25 PM
The reason to register. Thank you. I'd like to thank the guys at the UofA, poker source developers and the contributors in this thread.

Ran Ray Wotton's nested loop hand type enumeration test (TestHandRank.cpp) on a dual-core 2.00 GHz windows laptop. Compiled as a console application and with Maximize speed and Favor Fast Code compiler options in VS.Net 2005. Got some weird results:

BAD!! = 0
High Card = 23294460
Pair = 58627800
Two Pair = 31433400
Three of a Kind = 6461620
Straight = 6180020
Flush = 4047644
Full House = 3473184
Four of a Kind = 224848
Straight Flush = 41584
Total Hands = 133784560

Validation seconds = 1.0940
Total HighPrecision Clocks = 3923457
HighPrecision clocks per lookup = 0.029327

This is quite far from what I should be getting. Ideas?
7 Card Hand Evaluators Quote
06-20-2007 , 07:06 PM
Hi,
I see that you have the right answers, well that is a good start. Since you have the 2005 version, try to instrument the code and optimize it. That should speed it up. You might want to select the proper CPU and other optimizations in the project settings (I am using the AMD64 dual processor). Honestly getting the 130+ million hands evaled in a second is a great accomplishment in itself!

Ray
7 Card Hand Evaluators Quote
08-05-2007 , 04:43 AM
Hi friends, I'm new round here!

I've written an extremely simple and fast 7 card evaluator in PHP which can be seen in action at texas.holdem.poker.probability.cal.culator.org

The evaluator function takes an un-ordered hand array('AH', '5H', '4H', '9C', 'TC', '2D', '3H') runs five very simple lines of code and returns an integer ("1" Royal Flush thru "7414" Nine High "98754" which is the worst hand it is possible to form from 7 cards). There are a couple of simple look-up tables and the RAM footprint is in the region of 4.3 megabytes.

I personally don't know C/C++ but would be very interested in sharing my code with anyone that would like to port it into a CLI in C/C++ along with the simulate function.

My email
7 Card Hand Evaluators Quote
08-07-2007 , 11:52 AM
been trying to get Ray's code to work as part of my Msc project (very useful!)
but am failing horribly.
wirth a few subtle modifications to get it to run on visual studio 2005 i got the HandRankSetup to work fine.

Getting Card IDs!
ID - 612976
Setting HandRanks!
ID - 612976
Number IDs = 612977
maxHR = 32487833
Training seconds = 61.54

BAD!! = 0
High Card = 23294460
Pair = 58627800
Two Pair = 31433400
Three of a Kind = 6461620
Straight = 6180020
Flush = 4047644
Full House = 3473184
Four of a Kind = 224848
Straight Flush = 41584
Total Hands = 133784560

Validation seconds = 0.6250
Total HighPrecision Clocks = 2225124
HighPrecision clocks per lookup = 0.016632


however, i cannot get TestHandRank to run properly.
i've converted:

FILE * fin = fopen("..\\HandRanks.dat", "rb");

to

FILE * fin;
fopen_s(&amp;fin,"..\\HandRanks.dat", "rb");

but this just doesnt find the file (in the same directory)
so i change the ..\\ to .\\ and i get:


BAD!! = 133784560
High Card = 0
Pair = 0
Two Pair = 0
Three of a Kind = 0
Straight = 0
Flush = 0
Full House = 0
Four of a Kind = 0
Straight Flush = 0
Total Hands = 133784560

Validation seconds = 0.5000
Total HighPrecision Clocks = 1787357
HighPrecision clocks per lookup = 0.013360

which is just entirely confusing.
any ideas anyone?
7 Card Hand Evaluators Quote
08-08-2007 , 06:55 PM
Quote:
been trying to get Ray's code to work as part of my Msc project (very useful!)
but am failing horribly.
wirth a few subtle modifications to get it to run on visual studio 2005 i got the HandRankSetup to work fine.

Getting Card IDs!
ID - 612976
Setting HandRanks!
ID - 612976
Number IDs = 612977
maxHR = 32487833
Training seconds = 61.54

BAD!! = 0
High Card = 23294460
Pair = 58627800
Two Pair = 31433400
Three of a Kind = 6461620
Straight = 6180020
Flush = 4047644
Full House = 3473184
Four of a Kind = 224848
Straight Flush = 41584
Total Hands = 133784560

Validation seconds = 0.6250
Total HighPrecision Clocks = 2225124
HighPrecision clocks per lookup = 0.016632


however, i cannot get TestHandRank to run properly.
i've converted:

FILE * fin = fopen("..\\HandRanks.dat", "rb");

to

FILE * fin;
fopen_s(&amp;fin,"..\\HandRanks.dat", "rb");

but this just doesnt find the file (in the same directory)
so i change the ..\\ to .\\ and i get:


BAD!! = 133784560
High Card = 0
Pair = 0
Two Pair = 0
Three of a Kind = 0
Straight = 0
Flush = 0
Full House = 0
Four of a Kind = 0
Straight Flush = 0
Total Hands = 133784560

Validation seconds = 0.5000
Total HighPrecision Clocks = 1787357
HighPrecision clocks per lookup = 0.013360

which is just entirely confusing.
any ideas anyone?
Well, by the looks of it you have the hard part done. I use the VS2005 myself, so you shouldn't have too much trouble. Obviously it can't find the file... Perhaps if you look to see where it is on your hard drive and point to it directly. instead of the :
FILE * fin = fopen("..\\HandRanks.dat", "rb");
use:
FILE * fin = fopen("c:\\directory\\path\\you\\founditin\\HandRa nks.dat", "rb");
if (!fin) printf("I Missed it Again!!"); // if you see this message, you still aren't opening the file!

Have a Great Day!!
Ray...
7 Card Hand Evaluators Quote

      
m