Monty Hall

Tags: large, game, math, simulation

The Monty Hall Problem illustrates a surprising fact of probability. The problem is loosely based on the old game show Let’s Make a Deal and its host, Monty Hall. In the Monty Hall Problem, you can pick one of three doors. Behind one door is a prize: a new car. Each of the other two doors opens onto a worthless goat. Say you pick Door #1. Before the door you choose is opened, the host opens another door (either #2 or #3), which leads to a goat. You can choose to either open the door you originally picked or switch to the other unopened door.

It may seem like it doesn’t matter if you switch or not, but your odds do improve if you switch doors! This program demonstrates the Monty Hall problem by letting you do repeated experiments.

To understand why your odds improve, consider a version of the Monty Hall Problem with one thousand doors instead of three. You pick one door, and then the host opens 998 doors, which all reveal goats. The only two doors that are unopened are the one you selected and one other door. If you correctly picked the car door to begin with (a 1 in a 1,000 chance), then the host left a random goat door closed. If you picked a goat door (a 999 in a 1,000 chance), the host specifically chose the car door to leave closed. The choice of which doors to open isn’t random; the host knows to leave the car door closed. It’s almost certain that you didn’t pick the car door to begin with, so you should switch to the other door.

Another way to think of it is that you have 1,000 boxes and one box contains a prize. You guess which box the prize is in and the host puts it in your hands. Do you think the prize is in your box or one of the 999 other boxes? You don’t need the host to open 998 of the 999 boxes that don’t contain a prize; the amount of choice is the same as with the 1,000 doors. The odds that you guessed correctly in the beginning are 1 in 1,000, while the odds that you did not (and that the prize is in one of the other boxes) is a near certain 999 in 1,000.

More information about the Monty Hall Problem can be found at https://en.wikipedia.org/wiki/Monty_Hall_problem.

monty_hall.py
  1"""The Monty Hall Problem, by Al Sweigart al@inventwithpython.com
  2A simulation of the Monty Hall game show problem.
  3More info at https://en.wikipedia.org/wiki/Monty_Hall_problem
  4This code is available at https://nostarch.com/big-book-small-python-programming
  5Tags: large, game, math, simulation"""
  6
  7import random, sys
  8
  9ALL_CLOSED = """
 10+------+  +------+  +------+
 11|      |  |      |  |      |
 12|   1  |  |   2  |  |   3  |
 13|      |  |      |  |      |
 14|      |  |      |  |      |
 15|      |  |      |  |      |
 16+------+  +------+  +------+"""
 17
 18FIRST_GOAT = """
 19+------+  +------+  +------+
 20|  ((  |  |      |  |      |
 21|  oo  |  |   2  |  |   3  |
 22| /_/|_|  |      |  |      |
 23|    | |  |      |  |      |
 24|GOAT|||  |      |  |      |
 25+------+  +------+  +------+"""
 26
 27SECOND_GOAT = """
 28+------+  +------+  +------+
 29|      |  |  ((  |  |      |
 30|   1  |  |  oo  |  |   3  |
 31|      |  | /_/|_|  |      |
 32|      |  |    | |  |      |
 33|      |  |GOAT|||  |      |
 34+------+  +------+  +------+"""
 35
 36THIRD_GOAT = """
 37+------+  +------+  +------+
 38|      |  |      |  |  ((  |
 39|   1  |  |   2  |  |  oo  |
 40|      |  |      |  | /_/|_|
 41|      |  |      |  |    | |
 42|      |  |      |  |GOAT|||
 43+------+  +------+  +------+"""
 44
 45FIRST_CAR_OTHERS_GOAT = """
 46+------+  +------+  +------+
 47| CAR! |  |  ((  |  |  ((  |
 48|    __|  |  oo  |  |  oo  |
 49|  _/  |  | /_/|_|  | /_/|_|
 50| /_ __|  |    | |  |    | |
 51|   O  |  |GOAT|||  |GOAT|||
 52+------+  +------+  +------+"""
 53
 54SECOND_CAR_OTHERS_GOAT = """
 55+------+  +------+  +------+
 56|  ((  |  | CAR! |  |  ((  |
 57|  oo  |  |    __|  |  oo  |
 58| /_/|_|  |  _/  |  | /_/|_|
 59|    | |  | /_ __|  |    | |
 60|GOAT|||  |   O  |  |GOAT|||
 61+------+  +------+  +------+"""
 62
 63THIRD_CAR_OTHERS_GOAT = """
 64+------+  +------+  +------+
 65|  ((  |  |  ((  |  | CAR! |
 66|  oo  |  |  oo  |  |    __|
 67| /_/|_|  | /_/|_|  |  _/  |
 68|    | |  |    | |  | /_ __|
 69|GOAT|||  |GOAT|||  |   O  |
 70+------+  +------+  +------+"""
 71
 72print('''The Monty Hall Problem, by Al Sweigart al@inventwithpython.com
 73
 74In the Monty Hall game show, you can pick one of three doors. One door
 75has a new car for a prize. The other two doors have worthless goats:
 76{}
 77Say you pick Door #1.
 78Before the door you choose is opened, another door with a goat is opened:
 79{}
 80You can choose to either open the door you originally picked or swap
 81to the other unopened door.
 82
 83It may seem like it doesn't matter if you swap or not, but your odds
 84do improve if you swap doors! This program demonstrates the Monty Hall
 85problem by letting you do repeated experiments.
 86
 87You can read an explanation of why swapping is better at
 88https://en.wikipedia.org/wiki/Monty_Hall_problem
 89'''.format(ALL_CLOSED, THIRD_GOAT))
 90
 91input('Press Enter to start...')
 92
 93
 94swapWins = 0
 95swapLosses = 0
 96stayWins = 0
 97stayLosses = 0
 98while True:  # Main program loop.
 99    # The computer picks which door has the car:
100    doorThatHasCar = random.randint(1, 3)
101
102    # Ask the player to pick a door:
103    print(ALL_CLOSED)
104    while True:  # Keep asking the player until they enter a valid door.
105        print('Pick a door 1, 2, or 3 (or "quit" to stop):')
106        response = input('> ').upper()
107        if response == 'QUIT':
108            # End the game.
109            print('Thanks for playing!')
110            sys.exit()
111
112        if response == '1' or response == '2' or response == '3':
113            break
114    doorPick = int(response)
115
116    # Figure out which goat door to show the player:
117    while True:
118        # Select a door that is a goat and not picked by the player:
119        showGoatDoor = random.randint(1, 3)
120        if showGoatDoor != doorPick and showGoatDoor != doorThatHasCar:
121            break
122
123    # Show this goat door to the player:
124    if showGoatDoor == 1:
125        print(FIRST_GOAT)
126    elif showGoatDoor == 2:
127        print(SECOND_GOAT)
128    elif showGoatDoor == 3:
129        print(THIRD_GOAT)
130
131    print('Door {} contains a goat!'.format(showGoatDoor))
132
133    # Ask the player if they want to swap:
134    while True:  # Keep asking until the player enters Y or N.
135        print('Do you want to swap doors? Y/N')
136        swap = input('> ').upper()
137        if swap == 'Y' or swap == 'N':
138            break
139
140    # Swap the player's door if they wanted to swap:
141    if swap == 'Y':
142        if doorPick == 1 and showGoatDoor == 2:
143            doorPick = 3
144        elif doorPick == 1 and showGoatDoor == 3:
145            doorPick = 2
146        elif doorPick == 2 and showGoatDoor == 1:
147            doorPick = 3
148        elif doorPick == 2 and showGoatDoor == 3:
149            doorPick = 1
150        elif doorPick == 3 and showGoatDoor == 1:
151            doorPick = 2
152        elif doorPick == 3 and showGoatDoor == 2:
153            doorPick = 1
154
155    # Open all the doors:
156    if doorThatHasCar == 1:
157        print(FIRST_CAR_OTHERS_GOAT)
158    elif doorThatHasCar == 2:
159        print(SECOND_CAR_OTHERS_GOAT)
160    elif doorThatHasCar == 3:
161        print(THIRD_CAR_OTHERS_GOAT)
162
163    print('Door {} has the car!'.format(doorThatHasCar))
164
165    # Record wins and losses for swapping and not swapping:
166    if doorPick == doorThatHasCar:
167        print('You won!')
168        if swap == 'Y':
169            swapWins += 1
170        elif swap == 'N':
171            stayWins += 1
172    else:
173        print('Sorry, you lost.')
174        if swap == 'Y':
175            swapLosses += 1
176        elif swap == 'N':
177            stayLosses += 1
178
179    # Calculate success rate of swapping and not swapping:
180    totalSwaps = swapWins + swapLosses
181    if totalSwaps != 0:  # Prevent zero-divide error.
182        swapSuccess = round(swapWins / totalSwaps * 100, 1)
183    else:
184        swapSuccess = 0.0
185
186    totalStays = stayWins + stayLosses
187    if (stayWins + stayLosses) != 0:  # Prevent zero-divide.
188        staySuccess = round(stayWins / totalStays * 100, 1)
189    else:
190        staySuccess = 0.0
191
192    print()
193    print('Swapping:     ', end='')
194    print('{} wins, {} losses, '.format(swapWins, swapLosses), end='')
195    print('success rate {}%'.format(swapSuccess))
196    print('Not swapping: ', end='')
197    print('{} wins, {} losses, '.format(stayWins, stayLosses), end='')
198    print('success rate {}%'.format(staySuccess))
199    print()
200    input('Press Enter repeat the experiment...')

https://inventwithpython.com/bigbookpython/project47.html