Hello guys,
I am pretty new to all this stuff (not poker, but poker programming etc.). I recently decided to test my programming skills (I have been learning c#) and create an Omaha Hand Evaluator (and later maybe something more with it).
I decided to start with Keith Rule's library. The concepts of look up tables and storing cards as bitmasks and even working with bitmasks was pretty new to me, but I think I got the basic idea of it.
So after spending several hours on it, I finally managed to write a function called HandOddsOmaha, which is basically a copy of the function HoldemHand.HandOdds (stored in HandAnalysis.cs), but works for Omaha.
It basically just splits the 4-card starting hand into 6 2-card combinations and then when iterating through all possible boards using Keith's code ( foreach (ulong boardhand in Hands(boardmask, deadcards_mask, 5)) ), it also splits every 5-card board into several 3 card boards and combines each 2-card starting hand with each 3-card board, ie. 60 combinations for each 5-card board iteration.
It works just fine, but the problem is that it is too slow now, at least for empty board calculations (if there is a flop then its pretty much instant). It takes around 20s to iterate through those 1M+ possible boards.
Now I am a little stuck and would like your help in optimizing it. I know Odds Oracle can do the same calculations in less than one second, so more than 20 times faster.
Here is the whole code of the HandOddsOmaha function:
Code:
public static void HandOddsOmaha(string[] pockets, string board, string dead, long[] wins, long[] ties, long[] losses, ref long totalHands)
{
ulong[][] pocketmasks = new ulong[pockets.Length][];
ulong[][] pockethands = new ulong[pockets.Length][];
int count = 0, bestcount = 0;
ulong boardmask = 0UL;
ulong deadcards_mask = 0UL, deadcards = Hand.ParseHand(dead, ref count);
totalHands = 0;
deadcards_mask |= deadcards;
// Split 4 Omaha holecards into 6 combinations of Holdem 2 cards
string[][] splitPockets = new string[pockets.Length][];
for (int k = 0; k < splitPockets.Length; k++)
{
int index = 0;
splitPockets[k] = new string[6];
for (int i = 0; i < 4; i++)
{
for (int j = i + 1; j < 4; j++)
{
splitPockets[k][index++] = pockets[k].Substring(i * 2, 2) + pockets[k].Substring(j * 2, 2);
}
}
}
// Read pocket cards
for (int i = 0; i < pockets.Length; i++)
{
count = 0;
Hand.ParseHand(pockets[i], "", ref count);
if (count != 4)
throw new ArgumentException("There must be four pocket cards."); // Must have 4 cards in each pocket card set.
pocketmasks[i] = new ulong[6];
pockethands[i] = new ulong[6];
for (int j = 0; j < 6; j++)
{
pocketmasks[i][j] = Hand.ParseHand(splitPockets[i][j], "", ref count);
deadcards_mask |= pocketmasks[i][j];
}
wins[i] = ties[i] = losses[i] = 0;
}
// Read board cards
count = 0;
boardmask = Hand.ParseHand("", board, ref count);
/*
#if DEBUG
Debug.Assert(count >= 0 && count <= 5); // The board must have zero or more cards but no more than a total of 5
// Check pocket cards, board, and dead cards for duplicates
if ((boardmask & deadcards) != 0)
throw new ArgumentException("Duplicate between cards dead cards and board");
// Validate the input
for (int i = 0; i < pockets.Length; i++)
{
for (int j = i + 1; j < pockets.Length; j++)
{
if ((pocketmasks[i] & pocketmasks[j]) != 0)
throw new ArgumentException("Duplicate pocket cards");
}
if ((pocketmasks[i] & boardmask) != 0)
throw new ArgumentException("Duplicate between cards pocket and board");
if ((pocketmasks[i] & deadcards) != 0)
throw new ArgumentException("Duplicate between cards pocket and dead cards");
}
#endif
*/
// Iterate through all board possibilities that don't include any pocket cards.
foreach (ulong boardhand in Hands(boardmask, deadcards_mask, 5))
{
ulong bestpocket = 0;
int n = 0;
ulong[] cards = new ulong[5];
int index = 0;
while (n < 64 && index < 5)
{
if ((boardhand & 1UL << n) != 0) // if n-th bit is 1, then assign card
cards[index++] = 1UL << n;
n++;
}
ulong[] boards = null;
boards = new ulong[] { cards[0] | cards[1] | cards[2], cards[0] | cards[1] | cards[3], cards[0] | cards[1] | cards[4],
cards[0] | cards[2] | cards[3], cards[0] | cards[2] | cards[4], cards[0] | cards[3] | cards[4], cards[1] | cards[2] | cards[3],
cards[1] | cards[2] | cards[4], cards[1] | cards[3] | cards[4], cards[2] | cards[3] | cards[4] };
ulong[] result = new ulong[pockets.Length];
int actualBestPlayer = 0;
for (int i = 0; i < pockets.Length; i++)
{
result[i] = 0;
foreach (ulong p in pocketmasks[i])
{
foreach (ulong b in boards)
{
if (result[i] < Evaluate(p | b))
result[i] = Evaluate(p | b);
if (bestpocket < result[i])
{
bestpocket = result[i];
bestcount = 1;
actualBestPlayer = i;
}
else if (bestpocket == result[i] && i != actualBestPlayer)
{
bestcount++;
}
}
}
}
// Calculate wins/ties/loses for each pocket + board combination.
for (int i = 0; i < pockets.Length; i++)
{
if (result[i] == bestpocket)
{
if (bestcount > 1)
{
ties[i]++;
}
else
{
wins[i]++;
}
}
else if (result[i] < bestpocket)
{
losses[i]++;
}
}
totalHands++;
}
}
Thanks for any help.
Joe