It is important to realize that all squares on a Monopoly board aren’t equally valuable in terms of payoff. It is common knowledge that the green and the deep blue squares are slightly less visited, whereas, red squares seem to be a lot busier. But these intuitions have no solid proof.
Here’s a program which calculates the probability distribution of a person landing on each square on a Classic Monopoly (UK) board at the end of his turns. After many faulty algorithms, I came up with what I think is a reasonably accurate version. This took approximately 10 hours of coding, pondering, and debugging. 10,000,000 data have been taken to generate probabilities.
Click to expand:
from random import * print "This program will calculate the probabilities of each square on a Classic Monopoly (UK) board.\nThe accuracy depends on the number of calculations made.\nA large number of calculations will yield an accurate result, but will take time.\n" arr=*36 brr=*36 squares=*40 matrix = [[0 for i in range(40)] for j in range(40)] probability=*40 cnt=0 square_names=["Go", "Old Kent Road", "Community Chest", "Whitechapel Road", "Income Tax", "King's Cross station", "The Angel Islington", "Chance", "Euston Road", "Pentonville Road", "Jail", "Pall Mall", "Electric Company", "Whitehall", "Northumberland Avenue", "Marylebone station", "Bow Street", "Community Chest", "Marlborough Street", "Vine Street", "Free Parking", "Strand", "Chance", "Fleet Street", "Trafalgar Square", "Fenchurch Street station", "Leicester Square", "Coventry Street", "Water Works", "Piccadilly", "Go to Jail", "Regent Street", "Oxford Street", "Community Chest", "Bond Street", "Liverpool Street station", "Chance", "Park Lane", "Super Tax", "Mayfair"] turns=input("Enter number of calculations: ") def setdice(n): print "\nSetting dice probabilities..." f1 = open("dice_probabilities.dat", 'w') for i in range(n): cnt=0 a=b=c=d=e=f=0 a=randint(1,6) b=randint(1,6) cnt=a+b arr[cnt]=arr[cnt]+1 if a==b: c=randint(1,6) d=randint(1,6) cnt=a+b+c+d arr[cnt]=arr[cnt]+1 if c==d: e=randint(1,6) f=randint(1,6) if e!=f: cnt = a+b+c+d+e+f arr[cnt]=arr[cnt]+1 for i in range(36): brr[i]=arr[i]/float(n) print>>f1,(str(i)+"\t"+str(brr[i])) print "Dice probabilities have been set using Monopoly rules.\n"+str(turns)+" steps have been used.\nData saved to dice_probabilities.dat\n" f1.close() setdice(turns) for i in range(40): for j in range(40): k=j-i if k<0: k=k+40 if k<36: matrix[i][j]=brr[k] else: matrix[i][j]=0 def chance(x): n=randint(1,16) if n==1: return 39 elif n==2: return 0 elif n==3: return 30 elif n==4: return x-3 elif n==5: return 24 elif n==6: return 15 elif n==7: return 11 else: return x def community_chest(x): n=randint(1,16) if n==1: return 0 elif n==2: return 30 elif n==3: return 1 else: return x def square_probs(num): print "Calculating square probabilities..." a=i=x=sum=0 while x<num: n=randint(1,num) i=0 for i in range(40): b=a+i if b>39: b=b-40 n=n-(matrix[a][b]*num) if n<=0: break a=b if a in [7,22,36]: a=chance(a) if a in [2,17,33]: a=community_chest(a) squares[a] = squares[a]+1 x=x+1 if a==30: a=10 squares=0 squares=squares+2 x=x+1 f2 = open("square_probabilities.dat",'w') for i in range(40): probability[i]=squares[i]/float(num) print>>f2,(str(i+1)+"\t"+str(probability[i])+"\t"+square_names[i]) f2.close() print "Square probabilities have been calculated using " +str(num)+" dice rolls.\nData saved to square_probabilities.dat" square_probs(turns)
This program will calculate the probabilities of each square on a Classic Monopoly (UK) board. The accuracy depends on the number of calculations made. A large number of calculations will yield an accurate result, but will take time. Enter number of calculations: 10000000 Setting dice probabilities... Dice probabilities have been set using Monopoly rules. 10000000 steps have been used. Data saved to dice_probabilities.dat Calculating square probabilities... Square probabilities have been calculated using 10000000 dice rolls. Data saved to square_probabilities.dat
- The program considers that a player rolls only once per turn. In reality, a player gets to roll again in case of doubles. To minimize error, I’ve come up with something which I’ll call a pseudo-multi-roll system. It will be explained shortly. This method is a leftover from one of my previous algorithms. I kept it because I believe it is faster than actual multiple rolls.
- A player does not buy his way out of jail, i.e. he stays in jail for three turns whenever he gets sent to it. Thus the probability of the jail square calculated is more than its actual value.
- Chance and Community Chest cards are completely reshuffled after each draw. This increases the probability of squares which have associated cards (e.g. Go, Mayfair, Trafalgar Square, Pall Mall, Old Kent Road, Jail etc.). This assumption is an error only if the drawn cards are (lazily) kept at the bottom of the decks in actual games.
- The probability is calculated by considering the position of the player at the end of each turn. Hence, the square “Go to Jail” has 0 probability, since no one occupies it after the turn is over. Also, the probability of the Chance and Community Chest squares decreases a lot because they have cards sending players to other squares.
The Pseudo-Multi-Roll system:
The system takes into account that a player rolls again if he gets a double (and that he can roll a maximum of two doubles per turn), and generates a probability distribution after considering 10,000,000 rolls. This probability distribution gives us the chance of getting a particular sum at the end of a turn. For example, we can know the probability of the player getting a sum of 17 (say the dice gave [6,6] on first roll and [2,3] on second). We will not know that he had stopped at 12 in the middle, before advancing to 17, however this should not affect the end result considerably. The system lowers the probability of “Jail” since it doesn’t consider triple doubles.
The data from “dice_probabilities.dat” has been plotted. You can observe a slight increase of probabilities for the numbers between 7 and 12 due to the pseudo-multi-roll.
The Probability Matrix:
This is a 40×40 table, each row of which will store a copy of the probability chart generated above. It is stored in a way that the element at the j-th column of the i-th row will give the probability of travelling from square number ‘i’ to square number ‘j’ in one turn. This matrix will be referred after each turn to determine where the player will go next (by weighted random generation method).
“Chance”, “Community Chest” and “Go to Jail” have been hard-coded separately to generate their desired results.
Probabilities of each square:
This has been done by playing a game of 10,000,000 turns. The number of times the player stays on each square at the end of his turns has been recorded, and the probabilities have been calculated accordingly.
The data from “square_probabilities.dat” has been plotted.
Following observations are crucial:
- The red and orange squares are indeed the best squares on the board in terms of probability of visits. This is due to their location after jail (which draws in players from other parts of the board). The pink squares don’t get that big an advantage from jail, since they’re located too close (the dice probabilities are highest for values between 4 and 10).
- Jail has a drastically high probability because there are many ways of landing in jail. Also, one landing consumes three turns.
- The Chance and Community Chest squares show less probability since they can send the player to different squares within the same turn.
- The Go to Jail has 0 probability since it sends the player to jail.
- The squares with Chance/Community Chest cards have slightly higher probabilities (e.g. Go, Mayfair, Trafalgar Square, Pall Mall, Old Kent Road etc.).
- The squares before “Jail” and after “Go to Jail” have slightly lower probabilities because they’re skipped whenever the player lands on “Go to Jail”.
- “Marylebone station” is the best station and “Water Works” is the better utility.