This is an R HTML document. When you click the Knit HTML button a web page will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:

#########################################################################
# Function sum_cards: sums the values of the cards in a blackjack hand. Aces
# are counted as either 11 or 1 depending on the sum total of non-ace cards in the hand
#
# Parameters:
# - hand: a blackjack hand that has been dealt to either a player or dealer. 
#
# NOTE: Blackjack hands in this simulation consist solely of integer values since the
#       cards do not need to be displayed graphically. Suits of cards also are irrelevant
#       for similar reasons
# ========================================================================
library(knitr)

sum_cards <- function(hand) {

  aces <- FALSE
  ace_locs <- numeric()
  sum <- 0

  # if the hand is only 2 cards, just sum and exit
  if (length(hand) == 2) {
    sum <- hand[1] + hand[2]
    return(sum)
  }

  # find aces
  for (k in 1:length(hand)) {
    if(hand[k] == 11) {
      aces <- TRUE
      ace_locs <- c(ace_locs, k)
    }
  }

  # if no aces were found, sum all items and return
  if (aces == FALSE) {
    for (i in hand) {
      sum <- sum + i
    }
    return(sum)
  }

  # otherwise aces were found so first sum items that are NOT aces
  not_aces <- subset(hand, !(hand %in% c(11)))
  for (i in not_aces) {
    sum <- sum + i
  }

  # if sum of non-aces > 10 all aces must be treated as 1's
  if (sum > 10) {
    for (i in ace_locs) {
      sum <- sum + 1
    }
    return(sum)
  }

  # if sum of non-aces <= 9, need to check count of aces. 
  # If > 1 one can be treated as 11 while other must be treated as 1
  if (sum <= 9 & length(ace_locs) > 1) {
    sum <- sum + 11
    for (i in 1:(length(ace_locs) - 1)) {
      sum <- sum + 1
    }
    return(sum)
  }

  # if sum of non-aces == 10, need to check count of aces.
  # if == 1, then add 11 to sum and exit. if > 1, treat all aces as 1's
  if (sum == 10 & length(ace_locs) == 1) {
    sum <- sum + 11
    return(sum)
  } else {
    for (i in 1:length(ace_locs)) {
      sum <- sum + 1
    }
    return(sum)
  }

}

#########################################################################
# Function player_h: implements standard blackjack rules logic for the player's hand
# Parameters:
# - p_hand: the initial 2 cards dealt to the player
# - cards: a vector containing a pre-shuffled set of cards
# - i: an index variable that indicates the position of the card most recently dealt
#
# NOTE: Player strategy will be to take another card any time hand is 15 or less. This 
#       implies that the player will stand with any hand of 16 or greater.
# ========================================================================

player_h <- function(p_hand, cards, i, p_stand){

  ncards <- length(cards)
  stand <- FALSE

  while (stand != TRUE) {
    # sum current hand
    current <- sum_cards(p_hand)

    # if card index == number of cards in decks, time to stop
    if (i == ncards) {
      stand <- TRUE

      # else if hand is < p_stand value, take another card  
    } else if (current < p_stand) {
      i <- i + 1
      p_hand <- c(p_hand, cards[i])
    } else {
      stand <- TRUE
    }

  } # end while stand != TRUE

  return(c(current, i))
}

#########################################################################
# Function dealer_h: implements standard blackjack rules logic for the dealer's hand
# Parameters:
# - d_hand: the initial 2 cards dealt to the dealer
# - cards: a vector containing a pre-shuffled set of cards
# - i: an index variable that indicates the position of the card most recently dealt
#
# NOTE: Dealer MUST stand on hand total of 17 or greater, so any time dealer hand is <= 16
#       another card must be drawn
# ========================================================================

dealer_h <- function(d_hand, cards, i){

  ncards <- length(cards)
  stand <- FALSE

  while (stand != TRUE) {
    # sum current hand
    current <- sum_cards(d_hand)

    # if card index == number of cards in decks, time to stop
    if (i == ncards) {
      stand <- TRUE

      # else if hand is <= 16, take another card
    } else if (current <= 16) {
      i <- i + 1
      d_hand <- c(d_hand, cards[i])
    } else {
      stand <- TRUE
    }

  } # end while stand != TRUE

  return(c(current, i))
}

#########################################################################
# Function play_bj: plays blackjack until 'cards' are exhausted and tallies player's
# winnings and losses
#
# Parameters:
# - cards: a pre-shuffled set of cards
# - bet: the dollar amount of bets placeable by the player for each blackjack hand
#
# NOTE: need to sample WITHOUT replacement from cards. Since they are 
# pre-shuffled before calling this function, just deal from top of deck and 
# use and index to keep track of which cards haven't been dealt
# ========================================================================

play_bj <- function(cards, bet, p_stand){

  # initialize the number of cards to be played
  ncards <- length(cards)

  # initialize index for cards
  i <- 0

  # initialize winnings accummulator
  winnings <- 0

  # now loop until all cards used
  while (i < ncards) {

    # New hand needed: check to see whether at least 4 cards remain unused
    if (i <= (ncards - 4)) {

      #### Deal 2 cards to player
      # increment card index
      i <- i + 2
      p_hand <- c(cards[i-1], cards[i])

      ### Deal 2 cards to dealer
      # increment card index
      i <- i + 2
      d_hand <- c(cards[i-1], cards[i])

    } else {
      # else cards are exhausted so exit
      return(winnings)
    }

    #### logic for player's hand
    p_res <- player_h(p_hand, cards, i, p_stand)

    # update card index
    i <- p_res[2]

    # if player didn't go over 21 then apply dealer hand logic
    if (p_res[1] <= 21) {
      d_res <- dealer_h(d_hand, cards, i)

      # update card index
      i <- d_res[2]
    }

    # ---------------------------------------------------------------
    # now compare player's hand to dealer's hand to find out who won

    # if player hand exceeded 21 subtract 2 from winnings
    if (p_res[1] > 21) {
      winnings <- winnings - 2

      # else if dealer went over 21 dealer has lost so add 2 to winnings  
    } else if (d_res[1] > 21) {
      winnings <- winnings + 2

      # else if player hand > dealer hand add 2 to winnings
    } else if (p_res[1] > d_res[1]) {
      winnings <- winnings + 2

      # else if player hand < dealer hand subtract 2 from winnings
    } else if (p_res[1] < d_res[1]) {
      winnings <- winnings - 2
    }


  } # end while (i < ncards)
  return(winnings)
}


#########################################################################
# This is the 'main' portion of the simulation

# NOTE: Suits of cards don't really matter at all. All we really need to track is the number remaining for each class of card, e.g., how many 2's, how many 3's, etc..

# set number of 2-deck sets to play
N <- 12

# set size of bets
bet <- 2

# set hand total for player to stand at
p_stand <- 16

# generate a deck of cards
deck <- rep(c(2:10, 10, 10, 10, 11), 4)

# create a 2 deck set of cards
two_d <- rep(deck,2)

# create a vector to store results of play
res <- data.frame(N = 1:12, winnings = numeric(N))

for (i in 1:N){
  # shuffle 2 new decks of cards
  s_decks <-sample(two_d,length(two_d), replace = FALSE)
  s_decks

  # play until 2 decks are exhausted
  res$winnings[i] <- play_bj(s_decks, bet, p_stand)
}

# create a table of winnings for each iteration
iteration <- 1:N
kable(data.frame(iteration, res$winnings))
iteration res.winnings
1 -6
2 0
3 -10
4 -6
5 0
6 -2
7 8
8 -2
9 4
10 0
11 0
12 8
# average the winnings and print to screen
avg_winnings <- mean(res$winnings)
message(paste("Average Winnings per Hand = ", avg_winnings))
## Average Winnings per Hand =  -0.5
# set number of simulation iterations to be run
iters <- 1000

# create a vector to store results of play
res2 <- data.frame(N = 1:iters, avg_winnings = numeric(iters))

for (k in 1:iters) {
  for (i in 1:N){
    # shuffle 2 new decks of cards
    s_decks <-sample(two_d,length(two_d), replace = FALSE)
    s_decks

    # play until 2 decks are exhausted
    res$winnings[i] <- play_bj(s_decks, bet, p_stand)
  } # for i

  # save the average the winnings to the results vector
  res2$avg_winnings[k] <- mean(res$winnings)

} # for k

set.seed(123)

# create a vector to store the average winnings for various player "stand" values
playw <- data.frame(stand = 11:20, avg_winnings = numeric(10))

p_stand <- 11

for (z in 1:10) {
  res4 <- data.frame(N = 1:iters, avg_winnings = numeric(iters))

  for (k in 1:iters) {
    for (i in 1:N){
      # shuffle 2 new decks of cards
      s_decks <-sample(two_d,length(two_d), replace = FALSE)
      s_decks

      # play until 2 decks are exhausted
      res$winnings[i] <- play_bj(s_decks, bet, p_stand)
    } # for i

    # average the winnings and print to screen
    res4$avg_winnings[k] <- mean(res$winnings)

  } # for k

  # save the average winnings to the results vector
  playw$avg_winnings[z] <- mean(res4$avg_winnings)

  # increase the player's stand value
  p_stand <- p_stand + 1

} # for z

kable(playw)
stand avg_winnings
11 -5.106333
12 -3.753833
13 -3.314667
14 -3.046000
15 -2.823500
16 -2.608333
17 -3.173500
18 -4.460333
19 -7.514000
20 -12.840333