This blog is aimed at helping sharp freshmen understand Roulette Strategies using computer simulation.
This is clear in the assignment instruction.
The Roulette Strategies discussed today is Martingale Strategy.Martingale betting strategy begins by wagering $1 on red. The wagers for the second third and all following games depend on the question was the last outcome red. If YES,the wager $1 on the next play. If the answer is no, the last outcome was not red, then the wager twice the amount of the previous wager. This is how Martingale strategy works. Students could open the following link to get an elementary understanding of Martingale: https://www.statisticshowto.com/martingale-definition/
The following code simulates the Martingale strategy:
#' A single play of the Martingale strategy
#'
#' Takes a state list, spins the roulette wheel, returns the state list with updated values (for example, budget, plays, etc)
#' @param state A list with the following entries:
#' B number, the budget
#' W number, the budget threshold for successfully stoping
#' L number, the maximum number of plays
#' M number, the casino wager limit
#' plays integer, the number of plays executed
#' previous_wager number, the wager in the previous play (0 at first play)
#' previous_win TRUE/FALSE, indicator if the previous play was a win (TRUE at first play)
#' @return The updated state list
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.3.6 ✔ purrr 0.3.4
## ✔ tibble 3.1.8 ✔ dplyr 1.0.9
## ✔ tidyr 1.2.0 ✔ stringr 1.4.0
## ✔ readr 2.1.2 ✔ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
one_play <- function(state){
# Wager
proposed_wager <- ifelse(state$previous_win, 1, 2*state$previous_wager)
wager <- min(proposed_wager, state$M, state$B)
# Spin of the wheel
red <- rbinom(1,1,18/38)
# Update state
state$plays <- state$plays + 1
state$previous_wager <- wager
if(red){
# WIN
state$B <- state$B + wager
state$previous_win <- TRUE
}else{
# LOSE
state$B <- state$B - wager
state$previous_win <- FALSE
}
state
}
#' Stopping rule
#'
#' Takes the state list and determines if the gambler has to stop
#' @param state A list. See one_play
#' @return TRUE/FALSE
stop_play <- function(state){
if(state$B <= 0) return(TRUE)
if(state$plays >= state$L) return(TRUE)
if(state$B >= state$W) return(TRUE)
FALSE
}
#' Play roulette to either bankruptcy, success, or play limits
#'
#' @param B number, the starting budget
#' @param W number, the budget threshold for successfully stoping
#' @param L number, the maximum number of plays
#' @param M number, the casino wager limit
#' @return A vector of budget values calculated after each play.
one_series <- function(
B = 200
, W = 300
, L = 1000
, M = 100
){
# initial state
state <- list(
B = B
, W = W
, L = L
, M = M
, plays = 0
, previous_wager = 0
, previous_win = TRUE
)
# vector to store budget over series of plays
budget <- rep(NA, L)
# For loop of plays
for(i in 1:L){
new_state <- state %>% one_play
budget[i] <- new_state$B
if(new_state %>% stop_play){
return(budget[1:i])
}
state <- new_state
}
budget
}
single_spin <- function(){
possible_outcomes <- c(rep("red",18), rep("black",18), rep("green",2))
sample(possible_outcomes, 1)
}
martingale_wager <- function(
previous_wager
, previous_outcome
, max_wager
, current_budget
){
if(previous_outcome == "red") return(1)
min(2*previous_wager, max_wager, current_budget)
}
one_play_ <- function(previous_ledger_entry, max_wager){
# Create a copy of the input object that will become the output object
out <- previous_ledger_entry
out[1, "game_index"] <- previous_ledger_entry[1, "game_index"] + 1
out[1, "starting_budget"] <- previous_ledger_entry[1, "ending_budget"]
out[1, "wager"] <- martingale_wager(
previous_wager = previous_ledger_entry[1, "wager"]
, previous_outcome = previous_ledger_entry[1, "outcome"]
, max_wager = max_wager
, current_budget = out[1, "starting_budget"]
)
out[1, "outcome"] <- single_spin()
out[1, "ending_budget"] <- out[1, "starting_budget"] +
ifelse(out[1, "outcome"] == "red", +1, -1)*out[1, "wager"]
return(out)
}
one_series_ <- function(
max_games, starting_budget, winning_threshold, max_wager
){
# Initialize ledger
ledger <- data.frame(
game_index = 0:max_games
, starting_budget = NA_integer_
, wager = NA_integer_
, outcome = NA_character_
, ending_budget = NA_integer_
)
ledger[1, "wager"] <- 1
ledger[1, "outcome"] <- "red"
ledger[1, "ending_budget"] <- starting_budget
for(i in 2:nrow(ledger)){
#browser()
ledger[i,] <- one_play_(ledger[i-1,], max_wager)
if(stopping_rule(ledger[i,], winning_threshold)) break
}
# Return non-empty portion of ledger
ledger[2:i, ]
}
stopping_rule <- function(
ledger_entry
, winning_threshold
){
ending_budget <- ledger_entry[1, "ending_budget"]
if(ending_budget <= 0) return(TRUE)
if(ending_budget >= winning_threshold) return(TRUE)
FALSE
}
profit <- function(ledger){
n <- nrow(ledger)
profit <- ledger[n, "ending_budget"] - ledger[1, "starting_budget"]
return(profit)
}
# helper function
get_last <- function(x) x[length(x)]
#repeat 10000 times:
walk_out_money <- rep(NA, 10000)
for(j in seq_along(walk_out_money)){
walk_out_money[j] <- one_series(B = 200, W = 300, L = 1000, M = 100) %>% get_last
}
# Walk out money distribution
hist(walk_out_money, breaks = 100)
# Estimated probability of walking out with extra cash
mean(walk_out_money > 200)
## [1] 0.5218
# Estimated earnings
mean(walk_out_money - 200)
## [1] -43.4496
From above we could see that when times are 10,000, the frequencies of winning 100 and losing 200 is approaching 0.5 and the mean of the earnings is about -50
Now consider a gambler in a series of wagers when he starts with $200 at the begining. How the earnings evolve over a series of wagers at the roulette wheel?
require(magrittr)
## Loading required package: magrittr
##
## Attaching package: 'magrittr'
## The following object is masked from 'package:purrr':
##
## set_names
## The following object is masked from 'package:tidyr':
##
## extract
#svg(filename = "loser.svg", width=16, height =9)
par(cex.axis=2, cex.lab = 2, mar = c(8,8,2,2), bg = rgb(222, 235, 247, max = 255))
set.seed(2)
ledger <- one_series_(200,200,300,500)
plot(ledger[,c(1,5)], type = "l", lwd = 5, xlab = "Game Index", ylab = "Budget")
Lets change the input parameters: Fisrt change start money from 100 to 1000, given other parameters unchanged, to see how the average earning change
b_vec<-c(100,200,300,400,500,600,700,800,900,1000)
earning_mean<-rep(NA,length(b_vec))
i<-1
for (bb in b_vec){
walk_out_money <- rep(NA,bb)
for(j in seq_along(walk_out_money)){
walk_out_money[j] <- one_series(B = bb, W = 300, L = 1000, M = 100) %>% get_last
}
# Estimated probability of walking out with extra cash
earning_mean[i]<-mean(walk_out_money - bb)
i<-i+1
}
b_df<-data.frame(b_vec,earning_mean)
plot(b_df, type = "l", lwd = 5, xlab = "Starting Money", ylab = "Average earnings")
Then change stop thresh hold from 300 to 1000, given other parameters unchanged.
t_vec<-c(300,400,500,600,700,800,900,1000)
earning_mean<-rep(NA,length(t_vec))
i<-1
for (tt in t_vec){
walk_out_money <- rep(NA,bb)
for(j in seq_along(walk_out_money)){
walk_out_money[j] <- one_series(B = 200, W = tt, L = 1000, M = 100) %>% get_last
}
# Estimated probability of walking out with extra cash
earning_mean[i]<-mean(walk_out_money - 200)
i<-i+1
}
t_df<-data.frame(t_vec,earning_mean)
plot(t_df, type = "l", lwd = 5, xlab = "Thresh Hold", ylab = "Average earnings")
The more you want, the less you are likely to gain, The following code
shows how to calculate average number of plays till game ends: Lets
repeat 10,000 times:
times <- rep(NA,10000)
for (tm in 1:10000){
times[tm]<-length(one_series(200,200,300,500))
}
mean(times)
## [1] 2.5815
In this blog, the definition of Roulette Strategy is first displayed. Then the code is pulled to simulate the gamble gaming. By changing parameters, the average earnings also vary. In the long run, the house always wins because the expectation is minus, so do not risk all your money on gambling.
In some complex problems, the correlations of a large number of variables maybe be quite difficult to identify. In this case, logic may be disordered, resulting in meaningless simulation results.
Simulation might not be able to produce the best choice or result and for the real world situation,it is hard to simulate the uncertainty because conditions are too complex.