In 1903, Elizabeth J. Magie Phillips, who was an anti-monopolist, created the game in order to show the negative aspects of concentrating land in private monopolies. She filed a patent and her game “Landlord’s Game” was self-published in 1906. In the 1930s Parker Brothers issued a game called “Monopoly”. Over the years “Monopoly” evolved and many variations in the layout of the board game and the game rules were created. Today a large number of different variations per country exists. There are game layouts with 40 and game layouts with 52 fields. Specific editions for selected cities and countries exist.
This article series explains how you can create a monte carlo simulation or digital twin for the Monopoly(r) board game using SAS Viya. The article series features the following SAS components
Figure 26.2 shows a generic simulation architecture that can be used to simulate the Monopoly board game. This simulation architecture, however, not only applies to a Monopoly board game simulation, but might be generically used.
The simulation core is implemented as a SAS DATA step. All steps except the last two steps are implemented in a single DATA step. The DO loops for the GAMES, the ROUNDS, and the PLAYERS are nested in this DATA step. This is important for performance considerations and is considered as a best practice. Compare also Wicklin [11] and Wicklin [12].
Random number generators are used:
The game instructions as well as players’ behavior is implemented using different code statements and value assignments.
The values and outcomes for each player’s turn are stored and updated in the respective variables and the respective record is output to the simulation data.
In the next step, the simulation data are prepared for the particular analyses. Aggregations, transpositions, and further data management take place to prepare the final analysis tables.
These tables are then used to calculate the output statistics, generate graphical and tabular output, and create the result data sets.
In the first step you define the macro variables for your simulation:
%let cnt_players = 5;
%let cnt_games = 1000;
%let cnt_rounds = 100;
%let seed = 1;
*** not relevant for this simulation, will only be used in the next articles;
%let ConsiderJail = no;
%let Doublet3Jail = no;
%let ConsiderChance = no;
%let ScenarioName = "1. Basic Game - Dice only";
data work.MNP_Sim1(where=(Round ne 0)) ;
format Game Round Player Dice1 Dice2 DiceSum 8.;
ARRAY PlayerPos {&cnt_players.} PlayerPos1 - PlayerPos&cnt_players. ;
call streaminit(&seed);
do Game = 1 to &cnt_games;
*** Init-Block;
Round=0; Dice1=.; Dice2=.;
do Player = 1 to &cnt_players;
PlayerPos[Player]=1;
end;
In the next step you
do Round = 1 to &cnt_rounds;
do Player = 1 to &cnt_players;
Dice1 = ceil(rand('Uniform')*6);
Dice2 = ceil(rand('Uniform')*6);
DiceSum = sum(Dice1,Dice2);
PlayerPos[Player] + DiceSum;
PlayerPos[Player] = mod(PlayerPos[Player]-1,40)+1;
output;
end; *** Player;
end; *** Round;
end; *** Game;
run;
The simulation also loops over players and outputs a record for each position. However you only need the state after als players have moved the token. Therefore you need to only keep this record. This can be done using BY processing in a datastep and using the "LAST." notation to access the respective datastep variable.
data work.MNP_Sim1_LastRec;
set work.MNP_Sim1;
by Game Round Player;
drop dice1 dice2 dicesum;
*** Keep only last record per round;
if last.round then output;
run;
Note that you could achieve a similar results by moving the OUTPUT statement in the simulation code in step 1.3 from the PLAYER LOOP to the GAME LOOP. However I left it intentionially there as we might need the intermediates steps for profit calculations .
the above code stores the GAMES and the ROUNDS in rows, but the PLAYER position in columns. In order to get that data also into row, you need to transpose it BY GAME and ROUND, using the TRANSPOSE procedure.
proc transpose data=work.MNP_Sim1_LastRec
out=work.Player_Long;
by game round;
var PlayerPos: ;
run;
You now have all your information already in the PLAYER_LONG table to view your results and create graphs. The following datastep prepares the data in a nicer way and already adds metadata information for each simulation run.
data work.Player_Location;
length ScenarioName $40 ConsiderJail ConsiderChance Doublet3Jail $3;
set work.Player_Long;
Player=input(compress(_name_,,"ul"),3.);
Feature=strip(compress(tranwrd(_name_,'Player',''),,"d"));
if Feature = "Pos" then Feature = "Location";
rename col1 = Value;
drop _name_;
ScenarioName = &ScenarioName;
ConsiderJail = upcase("&ConsiderJail");
ConsiderChance = upcase("&ConsiderChance");
Doublet3Jail = upcase("&Doublet3Jail");
run;
Finally you can add variables the describe the settings for the simulation. This is relevant when we append this data to a simulation result repository.
Finally you use the SGPLOT procedure to create a histogram for the locations.
Note that we
proc sgplot data=work.player_location;
title Scenario: &scenarioname.;
histogram value / binstart=1 binwidth=1;
yaxis max = 6;
run;
title;
The resulting histogram reveals what we would have assumed.
The following videos have been recorded for the SAS ask-the-expert session on Monopoly simulations which I used to talk about them in the session. (note I forgot to turn of the microphone when recording it, this is why you hear me clicking).
You see a more dramatic peak around fields 6-9.
In some games there is the rule that you must roll the dice again after you rolled a double. The value of your dice rolls is added up for a maximum of 3 doubles and then your new position is identified.
In order to achieve this in your SAS program (10.1b MNP Basic Model Doubles.sas) you just extend your code as follows:
The rest of the code stays as is.
Dice1 = ceil(rand('Uniform')*6);
Dice2 = ceil(rand('Uniform')*6);
DiceSum = sum(Dice1,Dice2);
if Dice1 = Dice2 then do; ** First Doublet;
Dice1 = ceil(rand('Uniform')*6);
Dice2 = ceil(rand('Uniform')*6);
DiceSum + sum(Dice1,Dice2);
if Dice1 = Dice2 then do; ** Second Doublet;
Dice1 = ceil(rand('Uniform')*6);
Dice2 = ceil(rand('Uniform')*6);
DiceSum + sum(Dice1,Dice2);
end; ** Second Doublet;
end; ** First Doublet;
PlayerPos[Player] + DiceSum;
PlayerPos[Player] = mod(PlayerPos[Player]-1,40)+1;
[11] Wicklin, R. 2015. “Ten Tips for Simulating Data with SAS®.” SAS Global Forum Proceedings. https://support.sas.com/resources/papers/proceedings15/SAS1387-2015.pdf (Paper SAS1387-2015).
[12] Wicklin, R. 2013. Simulating Data with SAS®. Cary, NC: SAS Institute Inc.
Super fun learning example @gsvolba! Thanks for sharing!
Dive into keynotes, announcements and breakthroughs on demand.
Explore Now →The rapid growth of AI technologies is driving an AI skills gap and demand for AI talent. Ready to grow your AI literacy? SAS offers free ways to get started for beginners, business leaders, and analytics professionals of all skill levels. Your future self will thank you.