Simulating Craps with Monte Carlo Simulation in Go

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.

Craps House Edge Problem

Input: An integer numTrials.

Output: An estimate of the house edge of craps resulting from running numTrials randomized simulations.

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: Set numTrials 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()

Page Contents
Scroll to Top