Monty Hall
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...')