How to win at Monopoly

The game

Monopoly is a board game where players roll two six-sided dice to move around the game board, buying and trading properties, and developing them with houses and hotels. Players collect rent from their opponents, with the goal being to drive them into bankruptcy. Players can also end up in jail, which they cannot move from until they have met one of several conditions. The game has hundreds of different editions but here we will be using the Portuguese edition. 1 2

The board

How to increase our chances of winning

Because the game has several ways of redirecting players around the board (for instance, Chance cards, Community Chest cards or the "Go to Jail" space), not all spaces in the board are equal. The simplest way to increase the probability of winning a game would be to buy the properties where players have a highest probability of ending, so as to collect more rents.

The simulator

To find out which properties are the best ones, we are going to create a program to simulate a large number of Monopoly games and analize the results. In a nutshell, for each play the code below will:

  1. Check if the player stays in Jail.
  2. Roll the dice and keep track of the doubles (to check if the player showld leave or go to Jail)
  3. If the player is not in Jail or if it rolled a double (can leave Jail), move the player position.
  4. Check if the player ends in an Chance or Community Chest space. Draw the card and take the corresponding action.
  5. Check if the player ends in the "Go to Jail" space and move him accordingly.
  6. Save the space where the player ends the turn.
  7. Repeat steps 1-6 a large number of times to get statistically significant results.

Below is a simplified version the code for the Monopoly simulator. Full version is available on Github. 3

import random
import csv

# Game settings
GAMES = 10000
DIE_ROLLS_PER_GAME = 30
PLAYERS = 4

class Board:
"""Class representing the Monopoly game board"""
    SPACES = [
        {"name": "Partida", "color": "#CCE5D2", "value": None, "rent": None},
        {"name": "Campo Grande (Lisboa)", "color": "#934824", "value": 60, "rent": 2},
        # ... truncated
    ]

    def __init__(self):
        """Set up chand and community card piles (shuffle them)"""
        self.chance_chest = ["No movement"]*6 + ["Next station"]*2 + [24, 11, 5, 0, 39, 30, "Back 3 spaces", "Next company"]
        self.community_chest = ["No movement"]*14 + [30, 0]
        random.shuffle(self.chance_chest)
        random.shuffle(self.community_chest)

    def draw_chance(self, position):
        """Handle drawing of a chance card"""
        chance = self.chance_chest.pop(0)  # get first card (top of the pile)
        self.chance_chest.append(chance)  # place card at the end (bottom of the pile)
        if chance == "No movement":
            return position
        elif chance == "Next station":
            if position >= 35:
                return 5
            elif position >= 25:
                return 35
            elif position >= 15:
                return 25
            elif position >= 5:
                return 15
            return 5
        elif chance == "Back 3 spaces":
            if position < 3:
                return 37 + position
            else:
                return position - 3
        elif chance == "Next company":
            if position >= 12 and position < 28:
                return 28
            else:
                return 12
        else:
            return chance

    def draw_community(self, position):
        """Handle drawing of a community card"""
        community = self.community_chest.pop(0)  # get first card (top of the pile)
        self.community_chest.append(community)  # place card at the end (bottom of the pile)
        if community == "No movement":
            return position
        else:
            return community


class Player:
    def __init__(self, board):
        """Set starting game info"""
        self.position = 0  # starting position
        self.visits = []  # save spaces
        self.board = board  # initialize board
        self.doubles = 0  # doubles rolled in a row
        self.jail = False  # whether or not in jail
        self.jail_exit_attempts = 0  # number of attempts to exit jail

    def play(self):
        """Play the game"""
        # Check if player stays in jail
        if self.jail_exit_attempts == 3:
            self.jail = False

        # Roll dice
        die1 = random.randint(1, 6)
        die2 = random.randint(1, 6)

        if die1 == die2:
            self.jail = False
            self.doubles += 1
        else:
            self.doubles = 0

        if self.doubles == 3:
            self.position = 10  # Go to jail
            self.jail = True
            self.doubles = 0
        elif self.jail:
            self.jail_exit_attempts += 1
        else:
            self.position = (self.position + die1 + die2) % 40
            if self.position in [7, 22, 36]:
                self.position = self.board.draw_chance(self.position)
            if self.position in [2, 17, 33]:
                self.position = self.board.draw_community(self.position)
            if self.position == 30:
                self.position = 10
                self.jail = True
        self.visits.append(self.board.SPACES[self.position]["name"])


def run():
    # Games
    visits = []
    for i in range(GAMES):
        board = Board()
        player = Player(board)
        # Die rolls in each game
        for j in range(DIE_ROLLS_PER_GAME * PLAYERS):
            player.play()
            visits += player.visits

    return visits


if __name__ == "__main__":
    run()

The result

The code above simulates 10 thousand Monopoly games with 4 players, with each game lasting for 30 turns. This means that 1.2 million die rolls are simulated. The image below shows the probability of ending a turn in each space of the Monopoly board game.

Monopoly Space Probabilities

Unsurprisingly, the most probable space to end the turn in is the Jail (almost 10 % probability of ending there in each turn). This is because there are several ways to end there: "Go To Jail" space, Chance card, and rolling doubles consecutively.

What is more interesting is that Rua Júlio Dinis (Porto) has the highest probability at 3.06 %, significantly higher than Campo Grande (Lisboa) at only 1.96 %. Also, Rua das Amoreiras (Lisboa), the second most expensive property in the game, has one of the lowest probabilities.

The railway stations also have high probabilities, however their rents do not scale so much as you cannot build houses and hotels.

Overall we can see that the RED and ORANGE zone properties have very good probabilities and thus can be the most interesting ones to buy.

Final remarks

This analysis only takes into account the number of times players are likely to end at a given position. Because some properties have higher rents than others it can happen that owning a property with a lower probability but an higher rent can result in an higher income. For instance, the GREEN properties appear to have slightly lower proababilities than the YELLOW ones, however they do have higher value.

If taking value of properties into account, one would also have to account for the capital investment costs as houses and hotels are much more expensive to install in the high value properties.

Anyway, just don't buy the BROWN properties, they have both low value and low probabilities.

Notes

  1. Monopoly, Wikipedia
  2. It's extremely difficult to find a good image of the Monopoly board. Probably for copyright reasons.
  3. Full source code