Testing the Monty Hall problem using a Python program to run game simulations

If a search brought you here then you probably already know what the Monty Hall problem is. If not, and in case you are interested, the Wikipedia page explains it in detail.

In a nutshell: the game comes from a gameshow called Let’s Make a Deal, in which the contestant is presented with three doors, behind one of which is a car which will be won if the contestant chooses that door. Once the contestant has made their initial choice from the three doors, the gameshow host Monty Hall will open one of the other doors to show that the car is not behind that door. The contestant is then given a choice of staying with their original choice of door, or switching to the other remaining door which has not yet been opened.

I recently encountered the Monty Hall problem for the first time and succumbed to the logic failure that most people succumb to, i.e. feeling certain that staying on my original choice had exactly the same probability of winning as switching to the other available door.

Due to my initial difficulty in comprehending that switching doors always gives approximately double the chance of winning, I decided to take the scientific approach of writing a Python program to test this out. This clearly demonstrated that my 50/50 belief was wrong, and that in fact one should always switch because doing so approximately doubles the chance of winning.

There are plenty of other similar programs out there that people have written to test this, but they generally seem unnecessarily complicated. To test this satisfactorily it’s only necessary to choose Door 1 initially, then compare the results of staying with Door 1 vs switching to whichever one of Doors 2 and 3 is not already open.

To that end, here is my code:

import random

runs = 10 # total runs
games = 1000 # number of games per run for each choice

# Function to run the game simulations based on 
# choice of "stay" or "switch"
def play_games(choice):
  wins = 0

  # When running the game simulations we can assume 
  # an initial choice of door 1 each time. 
  # There's no need to make it any more complex than that
  for i in range(games):
    # Randomly select one of the three doors for the car to be behind
    car_door = random.randrange(1,4)
    # If we stay on door 1 we only need to know if the car is behind
    # door 1 (in which case we win) or not
    if choice == 'stay' and car_door == 1:
      wins += 1
    # If we switch to the other door (2 or 3, whichever of those two is
    # not already open) then we only need to know if the car is not
    # behind door 1 (in which case we win)
    elif choice == 'switch' and car_door != 1:
      wins += 1

  return(wins)

for j in range(runs):
  print('\nrun:\t' + str(j + 1))

  game_wins = {}

  # Run the game simulations for both choices, and print the results
  for choice in ('stay', 'switch'):
    game_wins[choice] = play_games(choice)
    print(choice + ':\t' + str(game_wins[choice]) + ' wins out of ' + str(games) + ' games')

  # Determine the win ratio of switches to stays and print the result
  win_ratio = game_wins['switch'] / game_wins['stay']
  print('result:\t"switch" won ' + str(round(win_ratio, 2)) + ' times as often as "stay"')

print('')

The results of the game simulations are very clear:

$ python3 ./montyhall.py 

run:	1
stay:	310 wins out of 1000 games
switch:	674 wins out of 1000 games
result:	"switch" won 2.17 times as often as "stay"

run:	2
stay:	350 wins out of 1000 games
switch:	667 wins out of 1000 games
result:	"switch" won 1.91 times as often as "stay"

run:	3
stay:	307 wins out of 1000 games
switch:	651 wins out of 1000 games
result:	"switch" won 2.12 times as often as "stay"

run:	4
stay:	336 wins out of 1000 games
switch:	670 wins out of 1000 games
result:	"switch" won 1.99 times as often as "stay"

run:	5
stay:	341 wins out of 1000 games
switch:	638 wins out of 1000 games
result:	"switch" won 1.87 times as often as "stay"

run:	6
stay:	322 wins out of 1000 games
switch:	679 wins out of 1000 games
result:	"switch" won 2.11 times as often as "stay"

run:	7
stay:	355 wins out of 1000 games
switch:	668 wins out of 1000 games
result:	"switch" won 1.88 times as often as "stay"

run:	8
stay:	311 wins out of 1000 games
switch:	665 wins out of 1000 games
result:	"switch" won 2.14 times as often as "stay"

run:	9
stay:	338 wins out of 1000 games
switch:	668 wins out of 1000 games
result:	"switch" won 1.98 times as often as "stay"

run:	10
stay:	358 wins out of 1000 games
switch:	646 wins out of 1000 games
result:	"switch" won 1.8 times as often as "stay"

It took me some time to comprehend why switching always roughly doubles your chances of winning. If you’re also having some difficulty getting your head around it, this video of Derren Brown explaining how his version of the game works is helpful for grasping the logic. Enjoy.