Now let’s turn our attention to the card game of Poker. There are methods to calculate the probability of drawing the various types of hands (see the Wikipedia Poker Probability Page for explanation). For our next example, we will examine one such type of hand with the following question:
If you are dealt a random hand of 5 cards, what is the probability that four of the cards each have a different suit?
To answer this question, we simulate shuffling a deck of cards and drawing a hand of cards.
For this code, we have separate versions for Windows, which uses rand(), and linux, which uses rand_r() as the random number generators.
Linux | |
---|---|
sequential version: | drawFourSuits_seq.cpp |
OpenMP version: | drawFourSuits_omp.cpp |
Windows | |
---|---|
sequential version: | drawFourSuits_seq.cpp |
OpenMP version: | drawFourSuits_omp.cpp |
We represent the deck of cards as an array of integers. Our function for simulating deck shuffling is not the most efficient, but it tries to capture how a traditional “fan” shuffle actually works. We also have helper functions for initializing a deck, drawing a hand, and checking if the hand has four cards of the same suit. Download the appropriate sequential code file for your environment and study it. Note all the places where random numbers are generated for two aspects of the problem: shuffling the deck and picking cards from the deck to form a hand.
Using these helper functions, it was straightforward to write testOneHand, which initializes a deck, shuffles it, draws a hand, and then checks if all four suits are represented.
/************************************************
***************************** testOneHand *********/
bool testOneHand(unsigned int *seed_ptr){
//Create a deck...sort it...pick 4 cards...test 4 suits
int deck[MAX_CARDS]; //std deck
int hand[CARDS_IN_HAND]; //card hand
initDeck(deck, seed_ptr); //create & shuffle a new deck
drawHand(deck, hand, seed_ptr); //go pick cards from deck
return isFourSuits(hand); //test if 4 suits
}//testOneHand
Converting our sequential code to use OpenMP is quite simple. We add a pragma compiler directive to the main simulation loop to run the loop simultaneously on multiple CPUs. The directive tells OpenMP to give each thread a different copy of i since each thread needs to keep track of its own loop iterations. numTests is shared because the total number of tests to run is doubled only once per iteration of the out while loop. (If each thread doubled it, we would go up by more than a factor of two.) Finally, the directive reduction (+:total) tells OpenMP to combine each of the threads’ partial results by summing to find the total number of hands that contained all four suits.
/************************* 2.0 Simulation Loop **/
while (numTests < MAX) {
total = 0; //reset counter
#pragma omp parallel for num_threads(nThreads) default(none) \
private (i, seed) \
shared (numTests) \
reduction (+:total) \
schedule(dynamic)
for (i=0; i<numTests; i++) { //make new deck - pick hand - test for 4 suits
if (testOneHand(&seed)) //returns TRUE iff 4-suits hand
total ++; //tally hands with 4-suits
}
//calc % of 4-suit hands & report results...
percentage = 100.0*( (double)total)/numTests;
cout<<setw(12)<<numTests<<setw(14)<<setprecision(3 )<<fixed<<percentage<<endl;
numTests += numTests; //double #tests for next round
} //while
Note that the above example is for the linux version of the code, which uses the thread-safe rand_r() function.