Learning objectives
In the core text, we introduced the following problem as a simple illustration of Monte Carlo simulation. We also introduced the rules of a simplified form of the game craps and defined a game’s house edge is the amount that a player will lose on average per unit of currency wagered, which led us to the following computational problem.
In particular, we introduced the following pseudocode function ComputeCrapsHouseEdge()
for solving this problem, along with its subroutine PlayCrapsOnce()
. This subroutine relies on a function SumTwoDice()
that we implemented in the previous code along and then generalized into a more general function SumDice()
that can take as input an arbitrary number of dice.
ComputeCrapsHouseEdge(numTrials) count ← 0 for numTrials total trials outcome ← PlayCrapsOnce() if outcome = true count ← count + 1 else count ← count − 1 return count/numTrials PlayCrapsOnce() firstRoll ← SumTwoDice() if firstRoll = 2, 3, or 12 return false (player loses) else if firstRoll = 7 or 11 return true (player wins) else while true newRoll ← SumTwoDice() if newRoll = firstRoll return true else if newRoll = 7 return false
In this code along, we will implement these two functions in Go and then run a large number of trials to estimate the house edge of craps.
Setup
We will continue editing the main.go
file that we created in the previous code along within the go/src/dice
source code directory. This file should contain SumDice()
and RollDie()
functions as follows; note that we have simplified func main()
down to a single line.
import ( "fmt" "rand" ) func main() { fmt.Println("Rolling dice.") } //SumDice takes as input an integer numDice. //It returns the sum of numDice simulated fair six-sided dice. func SumDice(numDice int) int { sum := 0 for i := 0; i < numDice; i++ { sum += RollDie() } return sum } //RollDie takes no inputs and returns a pseudorandom integer between 1 and 6, inclusively. func RollDie() int { return rand.Intn(6) + 1 }
Code along video
Beneath the video, we provide a detailed summary of the topics and code covered in the code along.
At the bottom of this page, you will have the opportunity to validate your work via auto-graded assessments that evaluate the functions covered in the code along.
Although we strongly suggest completing the code along on your own, you can find completed code from the code along in our course code repository.
Code along summary
Simulating craps
The details of pseudorandom number generation are contained in the lowest subroutine, RollDie()
. Implementing PlayCrapsOnce()
and ComputeCrapsHouseEdge()
is just a matter of converting our pseudocode functions to the syntax of Go. We begin with PlayCrapsOnce()
; remember that this function contains a nerve-racking for true
statement ensuring that we continue to loop for as long as possible, since it is technically possible to stand at a craps table rolling dice forever. Go’s standard way of representing such a statement is by using for {
.
//PlayCrapsOnce takes no input parameters and returns true or false depending on outcome of a single simulated game of craps. func PlayCrapsOnce() bool { firstRoll := SumTwoDice() if firstRoll == 7 || firstRoll == 11 { return true // winner! } else if firstRoll == 2 || firstRoll == 3 || firstRoll == 12 { return false // loser! } else { //roll again until we hit a 7 or our original roll for { newRoll := SumTwoDice() if newRoll == firstRoll { return true // winner! :) } else if newRoll == 7 { return false //loser :( } } } }
We next implement ComputeCrapsHouseEdge()
, which calls PlayCrapsOnce()
a total of numTrials
times and then returns the average per-trial amount won (as a positive number) or lost (as a negative number) by the player.
//ComputeCrapsHouseEdge takes an integer numTrials and returns an estimate of the house edge of craps (or whatever binary game) played over numTrials simulated games. func ComputeCrapsHouseEdge(numTrials int) float64 { // we use count to keep track of money won/lost count := 0 for i := 0; i < numTrials; i++ { outcome := PlayCrapsOnce() // did we win or lose? if outcome == true { count++ //win } else { count-- //lost } } // we want to return the average won/lost return float64(count) / float64(numTrials) }
Estimating the house edge of craps
We are ready to run the craps simulation in func main()
. Let’s estimate the house edge first for a value of numTrials
equal to 1000.
func main() { fmt.Println("Generating pseudorandom numbers and simulating craps.") // ... numTrials := 1000 fmt.Println(ComputeCrapsHouseEdge(numTrials)) }
In our own run, we obtained a printed value of -0.028, meaning that for every dollar the player wagers in our simulation, they lose 2.8 cents. However, results will differ because of (pseudo)random chance, and so we should run our simulation for more trials to hone in on a value.
STOP: SetnumTrials
equal to 100,000 and then 10 million and compile and run the simulation again for each value. What value do you obtain?
When we run our simulation with numTrials
equal to 10 million, the simulation takes a few seconds to finish, but we obtain a more accurate estimation of approximately -0.014. This is a more accurate value and the published house edge of craps; for every dollar the player wagers, the house nets 1.4 cents. It may take many trials, but the house always wins.
Check your work from the code along
We now provide autograders in the window below (or via a direct link) allowing you to check your work for the following functions:
PlayCrapsOnce()
ComputeCrapsHouseEdge()