6 A Monopoly simulation
Now you will use to simulate simplified games of Monopoly (https://en.wikipedia.org/wiki/Monopoly_(game)). In addition, there are also many tutorials and guides on the Web describing how to produce computer simulations for Monopoly. You are welcome to read and use these examples to inspire your work.
6.1 Moving around the board
A Monopoly board has 40 spaces. Players take it in turns to roll two dice and traverse around the board according to the sum of the dice values.
Use the following code example to simulate turns of a single player:
<- 100000 # number of turns to take
num_turns
<- 0 # start on the GO space
current_board_position
<- rep(0, num_turns)
move_size <- rep(0, num_turns)
positions_visited
# use a for loop to simulate a number of turns
for (turn in 1:num_turns) {
# roll two dice
<- sample(c(1:6), 2, replace = TRUE)
die_values
# move player position
# number of positions to move
<- sum(die_values)
plus_move
# compute new board position
<- current_board_position + plus_move
new_board_position
# update board position (this corrects for the fact the board is circular)
<- (new_board_position %% 40)
current_board_position
# store position visited
<- current_board_position
positions_visited[turn]
}
By increasing the number of turns taken, what distribution does the set of simulated board positions converge towards? Show this graphically using the histogram function.
hist(positions_visited, breaks = seq(0, 40, len = 41), right = FALSE)
6.2 Going to Jail
If a player lands on to Go To Jail space they must move immediately to the Jail space. Extend your code to include the possibility of going to jail. Here, assume that once in jail, the player continues as normal on the next turn.
<- 100000 # number of turns to take
num_turns
<- 0 # start on the GO space
current_board_position <- 30 # the go to jail space
go_to_jail_position <- 10 # jail space
jail_position
<- rep(0, num_turns)
move_size <- rep(0, num_turns)
positions_visited
# use a for loop to simulate a number of turns
for (turn in 1:num_turns) {
# roll two dice
<- sample(c(1:6), 2, replace = TRUE)
die_values
# move player position
# number of positions to move
<- sum(die_values)
plus_move
# compute new board position
<- current_board_position + plus_move
new_board_position
# if land on GO TO JAIL square, then go backwards to the JAIL square
if (new_board_position == go_to_jail_position) {
<- jail_position
new_board_position
}
# update board position (this corrects for the fact the board is circular)
<- (new_board_position %% 40)
current_board_position
# store position visited
<- current_board_position
positions_visited[turn]
}
What is the distribution of board positions during a long game?
hist(positions_visited, breaks = seq(0, 40, len = 41), right = FALSE)
Can you explain this result qualitatively?
You can also go to jail, if you roll three doubles (both dice having the same value) in a row. Update your code to allow for the possibility of going to Jail with three doubles. How does the distribution of board positions change?
<- 100000 # number of turns to take
num_turns
<- 0 # start on the GO space
current_board_position <- 30 # the go to jail space
go_to_jail_position <- 10 # jail space
jail_position
<- rep(0, num_turns)
move_size <- rep(0, num_turns)
positions_visited
# use a for loop to simulate a number of turns
for (turn in 1:num_turns) {
# set double counter to zero
<- 0
double_counter
# roll (max) three times
for (j in 1:3){
# roll two dice
<- sample(c(1:6), 2, replace = TRUE)
die_values
# if we have rolled a double for the third time, we proceed straight to jail
if ((die_values[1] == die_values[2]) & (double_counter == 2 )) {
<- jail_position
current_board_position break
}
# otherwise
# move player position
# number of positions to move
<- sum(die_values)
plus_move
# compute new board position
<- current_board_position + plus_move
new_board_position
# if land on GO TO JAIL square, then go backwards to the JAIL square
if (new_board_position == go_to_jail_position) {
<- jail_position
new_board_position
}
# update board position (this corrects for the fact the board is circular)
<- (new_board_position %% 40)
current_board_position
# break out of loop if we roll a non-double
if (die_values[1] != die_values[2]) {
break
else { # increment double counter
} <- double_counter + 1
double_counter
}
}
# store final position visited
<- current_board_position
positions_visited[turn]
}
hist(positions_visited, breaks = seq(0, 40, len = 41), right = FALSE)
Adding the rolling doubles feature doesn’t seem to change much. We might expect this since rolling three doubles is a very unlikely event!
6.3 Further Exercises
Now consider building a more complex Monopoly simulation by incorporating more complex aspects of the game such as:
- the purchase of properties
- a ledger for each player
- chance and community cards
You will need to think carefully about the simplifying assumptions you will make to make the task achievable. Do not be over-ambitious. For example, you might initially assume that players will not build houses/hotels on properties.
Here are some questions to answer with your simulations:
- How many turns does it take before all properties are purchased?
- What are the best properties to buy?
- How long does it take for a winner to be determined?
For example, the following simple extension of the previous example adds some features to record properties being purchased. This simulation is constructed based on the assumption that a players always buys any free property that land on.
<- 1000 # number of games to play
num_games <- 1000 # number of turns to take
num_turns
<- 0 # start on the GO space
current_board_position <- 30 # the go to jail space
go_to_jail_position <- 10 # jail space
jail_position # vector of squares containing properties
<- c(1, 3, 5, 6, 8, 9, 11, 12, 13, 14, 15, 16,
properties_that_can_be_bought 18, 19, 21, 23, 24, 25, 26, 27, 28, 29, 31, 32, 34, 35, 37, 39)
# vector to store number of turns to buy all properties
<- rep(0, num_games)
time_to_buy_all_properties
# simulate multiple games
for (game in 1:num_games) {
<- rep(0, num_turns)
positions_visited <- rep(0, 40)
positions_purchased <- rep(0, num_turns)
properties_bought
# use a for loop to simulate a number of turns
for (turn in 1:num_turns) {
# roll two dice
<- sample(c(1:6), 2, replace = TRUE)
die_values
# move player position
# number of positions to move
<- sum(die_values)
plus_move
# compute new board position
<- current_board_position + plus_move
new_board_position
# if land on GO TO JAIL square, then go backwards to the JAIL square
if (new_board_position == go_to_jail_position) {
<- jail_position
new_board_position
}
# update board position (this corrects for the fact the board is circular)
<- (new_board_position %% 40)
current_board_position
# if we can on a square that can be purchased and which has not been
# purchased (note R uses 1-indexing for arrays)
if (positions_purchased[current_board_position+1] == 0) {
if (current_board_position %in% properties_that_can_be_bought) {
+ 1] <- 1
positions_purchased[current_board_position
}
}
# store position visited
<- current_board_position
positions_visited[turn]
# store number of properties bought
<- sum(positions_purchased)
properties_bought[turn]
# check if all properties are gone
if (properties_bought[turn] == length(properties_that_can_be_bought)) {
<- turn
time_to_buy_all_properties[game] break
}
}
}
hist(time_to_buy_all_properties, breaks = 20)