# F# and Monopoly Probabilities

My sons and I were playing Monopoly when we started discussing different strategies for property acquisition.  For example, should you try and get Park Place and Boardwalk for the large rent but low probability of someone landing on it or should you get the purples with a high hit chance but lower payout?

We decided to run a simulation and since I an teaching myself F#, we coded up a F# answer.  I created a F# Tutorial project and then added a .fsx file.  In that file, I first created a couple of variables – 1 of which is a .NET type:

1. let tiles = [|0 .. 39|]
2. let random = System.Random()

I then added a Community Chest function that returns a 1 in 16 chance of Going to Jail (board location 10) and a 1 in 16 chance of going to GO (board location 0).  This is not completely accurate because we don’t shuffle the deck after every draw – but it seems close enough.

1. let communityChest x =
2.     let communityChestDraw = random.Next(1,17)
3.     if communityChestDraw = 1 then
4.         0
5.     else if communityChestDraw = 2 then
6.         10
7.      else
8.         x

I then added a Chance function that did the same thing – with a lot more possibilities (Go to Boardwalk, go to the nearest railroad, etc…)

1. let chance x =
2.     let chanceDraw = random.Next(1,17)
3.     if chanceDraw = 1 then
4.         0
5.     else if chanceDraw = 2 then
6.         10
7.     else if chanceDraw = 3 then
8.         11
9.     else if chanceDraw = 4 then
10.         39
11.     else if chanceDraw = 5 then
12.         x – 3
13.     else if chanceDraw = 6 then
14.         5
15.     else if chanceDraw = 7 then
16.         24
17.     else if chanceDraw = 8 then
18.         if x < 5 then
19.             5
20.         else if x < 15 then
21.             15
22.         else if x < 25 then
23.             25
24.         else if x < 35 then
25.             35
26.         else
27.             5
28.     else if chanceDraw = 9 then
29.         if x < 12 then
30.             12
31.         else if x < 28 then
32.             28
33.         else
34.             12
35.     else
36.         x

I then added a move function that handled going past the 39th tile and looping around past go and also the “Go to Jail” Tile:

1. let move x y =
2.     if x + y > 39 then
3.         x + y – 40
4.     else if x + y = 30 then
5.         10
6.     else if x + y = 2 then
7.         communityChest 2
8.     else if x + y = 7 then
9.         chance 7
10.     else if x + y = 17 then
11.         communityChest 2
12.     else if x + y = 22 then
13.         chance 22
14.     else if x + y = 33 then
15.         communityChest 2
16.     else if x + y = 36 then
17.         chance 36
18.     else
19.         x + y

I then put it together with a simulation function that ran 10000 iterations:

1. let simulation =
2.     let mutable startingTile = 0
3.     let mutable endingTile = 0
4.     let mutable doublesCount = 0
5.     let mutable inJail = false
6.     let mutable jailRolls = 0
7.     for diceRoll in 1 .. 10000 do
8.         let dieOneValue = random.Next(1,7)
9.         let dieTwoValue = random.Next(1,7)
10.         let numberOfMoves = dieOneValue + dieTwoValue
11.
12.         if dieOneValue = dieTwoValue then
13.             doublesCount <- doublesCount + 1
14.         else
15.             doublesCount <- 0
16.
17.         if inJail = true then
18.             if doublesCount > 1 then
19.                 inJail <- false
20.                 jailRolls <- 0
21.                 endingTile <- move 10 numberOfMoves
22.             else
23.                 if jailRolls = 3 then
24.                     inJail <- false
25.                     jailRolls <- 0
26.                     endingTile <- move 10 numberOfMoves
27.                 else
28.                     inJail <- true
29.                     jailRolls <- jailRolls + 1
30.         else
31.             if doublesCount = 3 then
32.                 inJail <- true
33.                 endingTile <- 10
34.             else
35.                 endingTile <- move startingTile numberOfMoves
36.
37.         let endingTile = move startingTile numberOfMoves
38.
39.         printfn "die1: %A + die2: %A = %A FROM %A TO %A"
40.             dieOneValue dieTwoValue numberOfMoves startingTile endingTile
41.
42.         startingTile <- endingTile
43.         tiles.[endingTile] <- tiles.[endingTile] + 1

I hate the mutable keywords.  I don’t know enough about F# to not use it – but it seems that my code is a F# plate of spaghetti

I then spit out the results like this:

1. let Aggregation =
2.     for tile in tiles do
3.         printfn "%A" tile

And sure enough, I got some results: I then put these results into Excel where I added the tile names and did a quick pivot table on property groups like this: Note that the results seem wrong (or not 100% correct) because Tile #2 (Community Chest) can’t be the most landed on tile and I also had 30 out of the 10,000 times where the cop was the final resting place for a turn – which can’t happen.

If I was using C#, I would have done this in about 25% of the time and been 100% right using unit tests – but I am trying to make myself uncomfortable by learning F# and so I muddle through – often I find that  the process is more important than the results in learning.

In any event, I want to make the following changes:

• 1) Create a tuple using the Tile Name, the PropertyGroup, and the Count
• 2) Write the unit tests so that I am 100% correct
• 3) Re-write it getting rid of the mutable keyword
• 4) Aggregate the list using the F# constrcuts (versus using Excel)

The kids also want to put in the expected rate of return based on the rent for each tile and then the adjustment for each house.  That might be fun – but it is irrelevant for actually winning the game (the marginal benefit of additional analysis is very low).   As long as you know they key colors and can get monopolies on them (and prevent monopolies by your opponent), you will win more often than not.