Search code examples
rmontecarlo

R- Monte Carlo Poker Hand Counter


I'm trying to throw together a project for practice. I'm trying to count poker hands in a Monte Carlo simulation using R.

I'm getting stuck on how to handle my straights, straight flushes, and royal flushes. Code for the straights and straight flushes are not included, but I did attempt to "pseudo-code" a royal flush. I'm hoping that if I can get some direction on my royal flush, that I can figure out how to handle straight flushes and straights. The code ran as intended before I added the code for the royalflush. Code below:

poker.sim <- function (Msim=10000,n=5) {
# This is a function to simulate Poker draws from a standard card deck
# We will be using a Grand Loop approach 
#
# Create the card deck
denom = rep(c("A",2:10,"J","Q","K"),4)
suit = rep(c("S","H","D","C"),each=13)
carddeck = data.frame(denom,suit)


# Initialize the twosuit, onepair, twopair, threeofakind, flush, fullhouse, and fourofakind as counters and for storage later...

count.twosuit = 0
count.onepair = 0
count.twopair = 0
count.threeofakind = 0
count.flush = 0
count.fullhouse = 0
count.fourofakind = 0
count.royalflush = 0


# Begin the Grand Loop
for(i in 1:Msim) 
    {
    # determine card numbers for this hand
    select = sample(nrow(carddeck),n)
    # select rows from the card deck for this hand
    hand = carddeck[select,]


        # Check for TWOSUIT and increment the counter if twosuit occurs
        # This statement is counting every instance in which only two suits occur in a five-card hand.

        if(length(unique(hand[,2]))==2) count.twosuit = count.twosuit+1


        # Check for ONEPAIR and increment the counter if onepair occurs
        # What this loop is doing is setting the length of the hand to 4 possible cards.
        # The first, second, and third cards can be any cards occurring only once.
        # The last two cards must be the same.
        tab = sort(table(as.vector(hand[,1])))

        if(length(tab) ==4)
            {
            if(all(tab == c(1,1,1,2))) count.onepair = count.onepair+1
            }


        # Check for TWOPAIR and increment the counter if twopair occurs
        # What this loop is doing is setting the length of the hand to be 3 possible cards.
        # The first card will occur only once.
        # The last two cards will occur twice each.

        if(length(tab) ==3)
            {
            if(all(tab == c(1,2,2))) count.twopair = count.twopair+1
            }


        # Check for THREEOFAKIND and increment the counter if threeofakind occurs.
        # What this function is doing is setting the length of the hand to 3 possible cards.
        # The first and second cards occur once each and can be any card.
        # The last three cards must be the same.

        if(length(tab) ==3) 
            {
            if(all(tab == c(1,1,3))) count.threeofakind = count.threeofakind+1
            }


        # Check for FLUSH and increment the counter if a flush occurs.
        # This statement is counting every instance in which only one suit occurs in a five-card hand.

        if (length(unique(hand[,2]))==1) count.flush = count.flush+1


        #Check for a FULLHOUSE and increment the counter if fullhouse occurs
        # What this loop is doing is setting the length of the hand to 2 possible cards.
        # The first occurring twice, and the second occurring 3 times.  Or vice versa.

        if(length(tab) ==2)
            {
            if(all(tab == c(2,3))) count.fullhouse = count.fullhouse+1
            }


        # Check for FOUROFAKIND and increment the counter if fourofakind occurs.
        # What this loop is doing is setting the length of the hand to 2 possible cards.
        # The first occurring only once, and the second occurring 4 times.

        if(length(tab) ==2)
            {
            if(all(tab == c(1,4))) count.fourofakind = count.fourofakind+1  
            }   


        # Check for ROYALFLUSH and increment the counter if royalflush occurs.
        # This will be restricted to only one suit AND when a run of 10,J,Q,K,A occurs.

        if(length(unique(hand[,2]))==1) 
        {
        if(tab == c(10,J,Q,K,A) count.royalflush = count.royalflush+1 
        }


    } # Close the Grand Loop


# Then, we will count the twosuits, onepairs, twopairs, threeofakinds, flushes, fullhouses, and fourofakinds 
# and divide them by the number of iterations of the simulation to get their respective probabilities.

p.twosuit = count.twosuit/Msim
p.onepair = count.onepair/Msim
p.twopair = count.twopair/Msim
p.threeofakind = count.threeofakind/Msim
p.flush = count.flush/Msim
p.fullhouse = count.fullhouse/Msim
p.fourofakind = count.fourofakind/Msim
p.royalflush = count.royalflush/Msim



# To output the results
out = list(Msim,p.twosuit,p.onepair,p.twopair,p.threeofakind,p.flush,p.fullhouse,p.fourofakind,p.royalflush)
names(out) = c("Msim","p.twosuit","p.onepair","p.twopair","p.threeofakind","p.flush","p.fullhouse","p.fourofakind","p.royalflush")
out
}

Solution

  • Focusing only on the royal flush part, I think your logic of checking if the cards are 10,J,Q,K,A doesn't work. I would instead use setqual to check if the elements of hand[, 1] are the same as the vector c("10","J","Q","K","A"). Thus we have:

    if(length(unique(hand[ , 2])) == 1) 
    {
      if(setequal(hand[ , 1], c("10","J","Q","K","A")))
        count.royalflush = count.royalflush + 1 
    }
    

    For checking if we have a straight, we'll need a bit more complex condition. We'll create a function that checks for a straight

    is_straight <- function(hand) {
      # first we want to encode "J", "Q", "K" to 11, 12, 13, so we can use diff later
      # we take the denominators from the hand as a character vector
      hand_denom <- as.character(hand[ , 1])
      # and then map acording values
      hand_denom[hand_denom == "J"] <- 11
      hand_denom[hand_denom == "Q"] <- 12
      hand_denom[hand_denom == "K"] <- 13
    
      # we need to check whether we treat ace as high or low and encode it accordingly
      # if any of the cards is a two, then ace will be treated as 1 if it's a straight
      # otherwise, we'll treat it as 14
      hand_denom[hand_denom == "A"] <- ifelse(any(hand_denom == "2"), 1, 14)
    
      # now we just check if the numeric values are successive (i.e. diff is always 1)
      all(diff(sort(as.numeric(hand_denom))) == 1)
    }
    

    and you obviously use this in your code as if (is_straight(hand)) <straight handling>.