Solving Monty Hall Game with Simulation and Bayes Theorem

Posted 2018-10-19

In [1]:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd


%matplotlib inline
plt.style.use('fivethirtyeight')
In [2]:
class Game():
    def __init__(self, n_doors=3):
        self.doors = ['goat' for _ in range(n_doors)]
        self.prize = np.random.randint(0, n_doors)
        self.doors[self.prize] = 'car'
        self.revealed = []
    
    def reveal(self, contestant):
        while len(self.revealed) < len(self.doors) - 2:
            avail_doors = set(np.arange(len(self.doors))) - set(self.revealed) - {contestant.pick} - {self.prize}
            reveal = np.random.choice(list(avail_doors))
            self.revealed.append(reveal)
    
    def check_win(self, contestant):
        if contestant.pick == self.prize:
            return True
        return False
In [3]:
class Contestant():
    def __init__(self, n_doors=3, switcher=True):
        self.switcher = switcher
        self.pick = np.random.randint(n_doors)
    
    def change(self, game):
        if self.switcher:
            avail_doors = set(np.arange(len(game.doors))) - set(game.revealed) - {self.pick}
            self.pick = np.random.choice(list(avail_doors))
In [4]:
def play_game(n_doors=3, switcher=True, iterations=1, verbose=False):
    wins = 0
    for _ in range(iterations):
        game = Game(n_doors)
        contestant = Contestant(n_doors, switcher)
        first_pick = contestant.pick
        game.reveal(contestant)
        contestant.change(game)
        second_pick = contestant.pick
        is_winner = game.check_win(contestant)
        if is_winner:
            wins += 1

        if verbose:
            print('Doors:\t\t\t\t', game.doors)
            print('Contestant picked door(index):\t', first_pick)
            print('Door revealed(index):\t\t', game.revealed)
            print('Contestant current pick:\t', second_pick)
            print('***Winner!***\n' if is_winner else '***Lost.***\n')

    return wins, iterations

Simulation of the Game

In [5]:
simulation_iterations = 1000

# Switcher strategy
wins, iterations = play_game(n_doors=3, switcher=True, iterations=simulation_iterations, verbose=False)
print('3 door game - Switcher winning % after {1} games:\t {0:.1f}%'.format(wins / iterations * 100, iterations))

# Non-switcher strategy
wins, iterations = play_game(3, False, simulation_iterations, False)
print('3 door game - Non-switcher winning % after {1} games:\t {0:.1f}%'.format(wins / iterations * 100, iterations))

# 100 doors 
wins, iterations = play_game(100, True, simulation_iterations, False)
print('100 door game - Switcher winning % after {1} games:\t {0:.1f}%'.format(wins / iterations * 100, iterations))

print('\n-----3 plays of the 3-door game-----')
play_game(3, True, 3, True) 
3 door game - Switcher winning % after 1000 games:	 66.7%
3 door game - Non-switcher winning % after 1000 games:	 35.7%
100 door game - Switcher winning % after 1000 games:	 99.1%

-----3 plays of the 3-door game-----
Doors:				 ['goat', 'car', 'goat']
Contestant picked door(index):	 0
Door revealed(index):		 [2]
Contestant current pick:	 1
***Winner!***

Doors:				 ['goat', 'goat', 'car']
Contestant picked door(index):	 2
Door revealed(index):		 [1]
Contestant current pick:	 0
***Lost.***

Doors:				 ['car', 'goat', 'goat']
Contestant picked door(index):	 2
Door revealed(index):		 [1]
Contestant current pick:	 0
***Winner!***

Out[5]:
(2, 3)

As # of Doors Increase, Switching Becomes More Beneficial

In [6]:
game_results = {}
for n_doors in range(3, 104, 2):
    switcher_wins, iterations = play_game(n_doors=n_doors, switcher=True, iterations=simulation_iterations, verbose=False)
    non_switcher_wins, iterations = play_game(n_doors=n_doors, switcher=False, iterations=simulation_iterations, verbose=False)
    game_results[n_doors] = [switcher_wins, non_switcher_wins]

# Plot the results
game_results_df = pd.DataFrame.from_dict(game_results, orient='index', columns=['switcher', 'non-switcher'])
plt.figure(figsize=(12, 8))
plt.scatter(game_results_df.index, game_results_df['switcher'])
plt.scatter(game_results_df.index, game_results_df['non-switcher'])
plt.legend()
plt.xlabel('# of Doors')
plt.ylabel('% Wins')
Out[6]:
<matplotlib.text.Text at 0x115611278>

Solving via Math

Monty Hall Bayes Monty Hall Bayes

Another cool site as reference with more explanations http://www.montyhallproblem.com/