For this lab you will design solution to the Monty Hall Problem.
When the mathematical solution is beyond your reach, a good tractable way to approach these types of analytical problems is to write a program to solve it for you. In this case, we can create a simulation that allows us to play the game thousands of times, and record the results for the two strategies – staying with the initial door or switching (we already know that switching is superior).
You will need to design a virtual game by creating a function for each step of the game. The virtual game will be played by running the functions in order in a script (which we will use next week to create a simulation to test strategies in the game).
The five functions form the basic steps of the virtual game.
For your homework, submit your knitted markdown file with the following:
Note that you will need to think carefully about input arguments and return values for each function. Some function do not need arguments.
The first function that creates a new game, for example, does not require any additional information other than a new game is required. It will randomly assign the two goats and one car to the three doors in the game and return the game set-up.
For the final function, however, in order to determine if the player has won we need information about the door they have selected and the game set-up in order to evaluate whether they win the car or the goat.
Unit Tests: (some helpful vocabulary)
A UNIT TEST is code that is written to check if your code is running properly.
If you are building new functions or conducting complex analysis it is never sufficient to check that the code runs without producing an error. You need to check that the code is producing the correct or expected results.
This requires actually knowing what a right answer looks like.
Unit tests typically test the code using multiple examples of input data that is well-understood and the answers are known.
It will also include edge cases, scenarios where the data may contain extreme or unexpected values or data types.
Here is some code to get you started.
This function sets up a new game.
Psuedocode
# step 1: create a vector of 3 doors: 2 goats and 1 car
# step 2: randomize the position of the car for a new game
# step 3: return the new game vector
# note that no external information is needed
# so no arguments are passed to the vector
create_game <- function( )
{
a.game <- sample( x=c("goat","goat","car"), size=3, replace=F )
return( a.game )
}
Test of Function:
Note that when functions utilize randomization it is helpful to test functions multiple times to make sure randomization is working properly. In this case we are randomizing the position of the car in each new game.
The contestant makes their first selection. Write a function to select one door at random.
Psuedocode
# step 1: create a vector of doors numbered 1,2,3
# step 2: randomly select ONE of the doors
# step 3: return the selection
# since the contestant will not know the position
# of the car when they select a door we do not
# need to share information about the game set-up
# before the selection is made
Note to call this function you need information from previous functions.
The host will always open a door with a goat behind it. But it can’t be a door the contestant has already selected. So it must be a door that is not a car and not a current contestant selection.
Note that if the contestant selects the car on the first guess the host can open either door, but if the contestant selects a goat the host only has one option.
Psuedocode
# describe your logic here
The contestant is given the option to change from their initial selection to the other door that is still closed. The function will represent the game-playing strategy as the argument stay=TRUE or stay=FALSE.
Psuedocode
# describe your logic here
change_door <- function( stay=T, opened.door, a.pick )
{
# YOUR CODE HERE...
return( final.pick ) # number between 1 and 3
}
# test it
opened.door <-
open_goat_door( game=this.game,
a.pick=my.initial.pick )
change_door( stay=T,
opened.door=opened.door,
a.pick=my.initial.pick )
change_door( stay=F,
opened.door=opened.door,
a.pick=my.initial.pick )
my.final.pick <-
change_door( stay=F,
opened.door=opened.door,
a.pick=my.initial.pick )
this.game
my.initial.pick
my.final.pick
Psudocode
# describe your logic here
determine_winner <- function( final.pick, game )
{
if( ...YOUR CODE HERE... )
{
return( "WIN" )
}
if( ...YOUR CODE HERE... )
{
return( "LOSE" )
}
}
# test code
this.game
my.initial.pick
my.final.pick <-
change_door( stay=T,
opened.door=opened.door,
a.pick=my.initial.pick )
determine_winner( final.pick=my.final.pick,
game=this.game )
my.final.pick <-
change_door( stay=F,
opened.door=opened.door,
a.pick=my.initial.pick )
determine_winner( final.pick=my.final.pick,
game=this.game )
Once all of your steps are implemented (your functions are all working), you can use the function below to inspect your code performance:
STRATEGY = STAY
--------------------------------------
| | | | | |
| GOAT | | CAR | | GOAT |
| | | | | |
--------------------------------------
1st Pick
Opened
Final
--------------------------------------
! WIN !
STRATEGY = SWITCH
--------------------------------------
| | | | | |
| GOAT | | CAR | | GOAT |
| | | | | |
--------------------------------------
1st Pick
Opened
Final
--------------------------------------
! LOSE !
Include the code chunk below in your lab submission. It will add three game printouts to your file.
The argument echo=F omits the code and just shows the results for readability.
```{r, echo=F}
show_game <- function() {
this.game <- create_game()
my.initial.pick <- select_door()
opened.door <- open_goat_door( this.game, my.initial.pick )
my.final.pick.stay <- change_door( stay=T, opened.door, my.initial.pick )
my.final.pick.switch <- change_door( stay=F, opened.door, my.initial.pick )
game.outcome.stay <- determine_winner( my.final.pick.stay, this.game )
game.outcome.switch <- determine_winner( my.final.pick.switch, this.game )
this.game[ this.game == "car" ] <- "car "
this.game <- toupper( this.game )
first.pick <- c(" "," "," ")
first.pick[ my.initial.pick ] <- "1st Pick"
open.door <- c(" "," "," ")
open.door[ opened.door ] <- " Opened "
final <- c(" "," "," ")
final[ my.final.pick.stay ] <- " Final "
win.lose <- final
win.lose[ my.final.pick.stay ] <-
ifelse( game.outcome.stay=="WIN",
paste0( " ! WIN !" ),
paste0( "! LOSE !" ) )
r <- NULL
r[1] <- "\n\nSTRATEGY = STAY"
r[2] <- "--------------------------------------"
r[3] <- paste0( " | | | | | | " )
r[4] <- paste0( " | ", this.game[1], " | | ", this.game[2], " | | ", this.game[3], " | " )
r[5] <- paste0( " | | | | | | " )
r[6] <- "--------------------------------------"
r[7] <- paste0( " ", first.pick[1], " ", first.pick[2], " ", first.pick[3], " " )
r[8] <- paste0( " ", open.door[1], " ", open.door[2], " ", open.door[3], " " )
r[9] <- paste0( " ", final[1], " ", final[2], " ", final[3], " " )
r[10] <- "--------------------------------------\n"
r[11] <- paste0( " ", win.lose[1], " ", win.lose[2], " ", win.lose[3], " \n\n" )
final <- c(" "," "," ")
final[ my.final.pick.switch ] <- " Final "
win.lose <- final
win.lose[ my.final.pick.switch ] <-
ifelse( game.outcome.switch=="WIN",
paste0( " ! WIN !" ),
paste0( "! LOSE !" ) )
r[12] <- "\nSTRATEGY = SWITCH"
r[13] <- "--------------------------------------"
r[14] <- paste0( " | | | | | | " )
r[15] <- paste0( " | ", this.game[1], " | | ", this.game[2], " | | ", this.game[3], " | " )
r[16] <- paste0( " | | | | | | " )
r[17] <- "--------------------------------------"
r[18] <- paste0( " ", first.pick[1], " ", first.pick[2], " ", first.pick[3], " " )
r[19] <- paste0( " ", open.door[1], " ", open.door[2], " ", open.door[3], " " )
r[20] <- paste0( " ", final[1], " ", final[2], " ", final[3], " " )
r[21] <- "--------------------------------------\n"
r[22] <- paste0( " ", win.lose[1], " ", win.lose[2], " ", win.lose[3], " \n\n" )
cat( paste0( r, collapse="\n" ), sep = "" )
}
show_game()
show_game()
show_game()
```
Try these out. If you can figure out a solution include it with your submission and post it to YellowDig to get bonus admiration points from your classmates.
Let’s change the rules a little to make outcomes more interesting. Create a board with 5 doors and 2 cars. After the contestant makes an initial selection the host will open one car door and one goat door. If the contestant decides to switch they then have to select from the two remaining doors.
How does this new board change the pay-off from the game? Is switching still the best strategy?
We are building functions to play a game in a static world. There are always three doors, one car, and two goats.
What happens if we are in a dynamic world?
The game can have three or more doors (in a game with two doors there would be no switching so there is no strategy to study).
And we can also have one or more cars up to N-2 (N being the number of doors, there always need to be at least two goats so that the host can open a goat door, even if the contestant selected a goat in the first round).
How would you change the code to build this game?
Note that in the first game the user would only have to specify their strategy (stay or switch). Here the user would have to specify the game size, the number of cars, and their strategy. So game size and number of cars would be added as arguments to the build_game() function.
Each chunk includes some testing code afterwards to demonstrate proper use.
Add unit tests as well. Instead of randomly selecting the game set-up and initial selection, assign these values so that you know the current state of the game.
With this information you can also determine the outcome under each strategy (stay and switch). So given any game set-up you can evaluate if your functions produce the correct answer.
Create variables that stores the correct answers and use them to evaluate your code:
# UNIT TEST A
this.game <- c("goat","car","goat")
my.initial.pick <- 2
A.STAY <- "WIN"
A.SWITCH <- "LOSE"
my.final.pick <-
change_door( stay=T,
opened.door=opened.door,
a.pick=my.initial.pick )
outcome.stay <-
determine_winner( final.pick=my.final.pick,
game=this.game )
my.final.pick <-
change_door( stay=F,
opened.door=opened.door,
a.pick=my.initial.pick )
outcome.switch <-
determine_winner( final.pick=my.final.pick,
game=this.game )
case.stay <- outcome.stay == A.STAY
case.switch <- outcome.switch == A.SWITCH
passes <- all( case.stay & case.switch )
Define several unit tests to inspect the efficacy of your code.
Include several in your file after Step 5.
**Unit Test A:**
Game Setup: **`r paste0( toupper(this.game), collapse=" " )`**
Initial selection: **`r my.initial.pick`**
Passes unit test: **`r passes`**
When you have completed your assignment, knit your RMD file to generate your rendered HTML file.
Login to Canvas at http://canvas.asu.edu and navigate to the assignments tab in the course repository. Upload your RMD and HTML files.
Remember to:
See Google’s R Style Guide for examples.
Markdown Trouble?
If you are having problems with your RMD file, visit the RMD File Styles and Knitting Tips manual.
Notes on Knitting
Note that when you knit a file, it starts from a blank slate. You might have packages loaded or datasets active on your local machine, so you can run code chunks fine. But when you knit you might get errors that functions cannot be located or datasets don’t exist. Be sure that you have included chunks to load these in your RMD file.
Your RMD file will not knit if you have errors in your code. If you
get stuck on a question, just add eval=F
to the code chunk
and it will be ignored when you knit your file. That way I can give you
credit for attempting the question and provide guidance on fixing the
problem.