Monty Hall Simulation Problem

Hello Everyone, I am working on Monty Hall Simulation Problem Project and I am looking for source code. I have checked on github and found this source but I want to know below code is optimize or not as a speed and size point of view?

import random
from tkinter import StringVar, Label, Tk, Entry

window = Tk()
window.geometry("400x100")
window.title("Monty hall simulation")
window.resizable(0, 0)

same_choice = StringVar()
switched_choice = StringVar()
same_choice.set(0)
switched_choice.set(0)
no_sample = Entry()
Label(text="Same choice").place(x=80, y=8)
Label(text="Switched choice").place(x=80, y=40)
Label(textvariable=same_choice, font=(15)).place(x=180, y=8)
Label(textvariable=switched_choice, font=(15)).place(x=180, y=40)
no_sample.place(x=100, y=70)


def simulate(event):
    same_choice_result = 0
    switched_choice_result = 0
    samples = int(no_sample.get())
    doors = ["gold", "goat", "bed"]
    for _ in range(samples):
        simulated_doors = doors.copy()
        random.shuffle(simulated_doors)
        first_choice = random.choice(simulated_doors)
        simulated_doors.remove(first_choice)
        opened_door = (
            simulated_doors[0] if simulated_doors[0] != "gold" else simulated_doors[1]
        )
        simulated_doors.remove(opened_door)
        switched_second_choice = simulated_doors[0]
        if first_choice == "gold":
            same_choice_result += 1
            same_choice.set(same_choice_result)
        elif switched_second_choice == "gold":
            switched_choice_result += 1
            switched_choice.set(switched_choice_result)
        else:
            print("That's will never happed")


no_sample.bind("<Return>", simulate)
window.mainloop()

That looks like python code. It may do what you want, but it being “optimized” is completely subjective.

You would be a lot better off if you wrote a version yourself. Then you would understand it a lot better…

3 Likes

The standard Monty Hall problem involves two goats and a car, with the car being the coveted prize. Given that, it is unclear why the example you found uses three different items here:

    doors = ["gold", "goat", "bed"]

See the following:

The advice from @TfGuy44 is good. Discard the implementation from GutHub, study the standard version of the problem, and develop your solution from scratch.

Will you be using Processing Python Mode?

EDITS (July 29 and 30, 2021):

This is a very interesting problem that seems worthy of some experimentation, since according to the Wikipedia article cited above,

… the correct choice (that one should switch doors) is so counterintuitive it can seem absurd, but is nevertheless demonstrably true.

Following are three simulations, each with a different user strategy. In all three cases, the host’s strategy is to follow the user’s first choice by opening a door with a goat behind it. If the user’s first choice has a car behind it, the host randomly chooses among the other two doors, with each having a goat. Otherwise, the user’s first choice has a goat behind it, and the host opens the single remaining door that also is backed by a goat.

The three user strategies in the simulations are:

  • always stick with the first choice
  • randomly decide whether to stick or switch
  • always switch

Each simulation was run for 10,000 games.

The code is written for Python 3 with no tkinter or Processing Python Mode interface. @adisharma, I believe you would like a more graphical user interface than this, so this code probably does not satisfy your needs. However, you could opt for a similar algorithm.

Here’s the code:

import random

class MontyHallGame:
    def __init__(self):
        self.doors = ["goat", "goat", "car"]
        random.shuffle(self.doors)
        self.available_choices = [0, 1, 2]
        self.first_user_choice = None # first door picked by user
        self.host_choice = None # door opened by host
        self.second_user_choice = None # second door picked by user
    def set_first_user_choice(self, door):
        self.first_user_choice = door
        self.available_choices.remove(door)
        for i in self.available_choices:
            if self.doors[i] == "car":
                self.available_choices.remove(i)
                break
    def get_first_user_choice(self):
        return self.first_user_choice
    def set_host_choice(self, door):
        self.host_choice = door
        self.available_choices = [0, 1, 2]
        self.available_choices.remove(door)
    def get_host_choice(self):
        return self.host_choice
    def set_second_user_choice(self, door):
        self.second_user_choice = door
    def get_second_user_choice(self):
        return self.second_user_choice
    def get_outcome(self):
        return self.doors[self.second_user_choice]
    def get_available_choices(self):
        return self.available_choices
    def get_doors(self):
        return self.doors

    # three user strategies for simulations
    # the host always chooses randomly among the two other doors
    # when user's first choice is the door with the car
    
    def play_random_game(self):
        # user randomly chooses from unopened doors for second choice
        self.set_first_user_choice(random.choice(self.get_available_choices()))
        self.set_host_choice(random.choice(self.get_available_choices()))
        self.set_second_user_choice(random.choice(self.get_available_choices()))
    def play_stick_game(self):
        # user always repeats first choice again as second choice
        self.set_first_user_choice(random.choice(self.get_available_choices()))
        self.set_host_choice(random.choice(self.get_available_choices()))
        self.set_second_user_choice(self.get_first_user_choice())
    def play_switch_game(self):
        # user always switches door for second choice
        self.set_first_user_choice(random.choice(self.get_available_choices()))
        self.set_host_choice(random.choice(self.get_available_choices()))
        ac = self.get_available_choices()
        ac.remove(self.get_first_user_choice())
        self.set_second_user_choice(ac[0])

print("User always sticks with first choice.")
wins = 0      
for i in range(10000):
    g = MontyHallGame()
    g.play_stick_game()
    if g.get_outcome() == "car":
        wins += 1
print(wins)

print("User randomly decides whether to stick with or switch choice.")
wins = 0      
for i in range(10000):
    g = MontyHallGame()
    g.play_random_game()
    if g.get_outcome() == "car":
        wins += 1
print(wins)

print("User always switches choice.")
wins = 0      
for i in range(10000):
    g = MontyHallGame()
    g.play_switch_game()
    if g.get_outcome() == "car":
        wins += 1
print(wins)

Sample output:

User always sticks with first choice.
3363
User randomly decides whether to stick with or switch choice.
5001
User always switches choice.
6666

The results suggest that the three strategies offer the user the following probabilities of winning the car:

  • always stick with the first choice: 1/3
  • randomly decide whether to stick or switch: 1/2
  • always switch: 2/3
2 Likes

A bit off topic, but where can I find the details behind the 10 door Monty Hall problem? I think there was a change to the usual 3 doors. Not too sure about the details. I think there was something to do with “You can open 5 more doors, or I can open 5 empty ones” or something along the lines.

Thank you!

1 Like

Due to the amount of discussion of the problem among mathematicians and others, lots of variants have been developed, so there may be several versions of it that involve ten doors. This article presents something of that sort:

1 Like

Below is an image of a Monty Hall problem simulation executing in Processing Python Mode.
MontyHall

It is derived from the Python 3 simulation that was previously posted. During each frame, each of the three player strategies is run 100 times, and the percentages of the wins for that frame are displayed. The percentages are also represented graphically to the right. The graphical representation is cumulative.

All are free to copy, revise, and use the code.

The code:

import random
iterations = 100

graph_x = 200
def setup():
    size(320, 240)
    frameRate(0.5)
    background(255)
    fill(0)
    stroke(0)
    # labels
    textSize(16)
    text("0%", graph_x - 8, 220)
    text("100%", graph_x + 76, 220)
    line(graph_x, 92, graph_x, 200)
    line(graph_x + 100, 92, graph_x + 100, 200)
    textSize(24)
    text("Monty Hall Problem", 20, 40)
    text("Wins", 20, 80)
    text("Player Strategy", 96, 80)
    text("Stick", 96, 120)
    text("Random", 96, 160)
    text("Switch", 96, 200)

def draw():
    # obscure previous win percentages and frameCount
    noStroke()
    fill(255)
    rect(0, 94, 88, height - 90)
    rect(0, 210, 140, 30)
    
    # display current win percentages
    fill(0)
    textSize(24)
    # stick player strategy
    stick_wins = 0      
    for i in range(iterations):
        g = MontyHallGame()
        g.play_stick_game()
        if g.get_outcome() == "car":
            stick_wins += 1
    text("{}%".format(stick_wins), 20, 120)

    # random player strategy
    random_wins = 0      
    for i in range(iterations):
        g = MontyHallGame()
        g.play_random_game()
        if g.get_outcome() == "car":
            random_wins += 1
    text("{}%".format(random_wins), 20, 160)

    # switch player strategy
    switch_wins = 0      
    for i in range(iterations):
        g = MontyHallGame()
        g.play_switch_game()
        if g.get_outcome() == "car":
            switch_wins += 1
    text("{}%".format(switch_wins), 20, 200)
    textSize(16)
    text("Frame {}".format(frameCount), 10, 230)

    # graph the percentages
    stroke(0, 16)

    line(graph_x + stick_wins, 92, graph_x + stick_wins, 120)
    line(graph_x + random_wins, 132, graph_x + random_wins, 160)
    line(graph_x + switch_wins, 172, graph_x + switch_wins, 200)

class MontyHallGame(object):
    def __init__(self):
        self.doors = ["goat", "goat", "car"]
        random.shuffle(self.doors)
        self.available_choices = [0, 1, 2]
        self.first_user_choice = None # first door picked by user
        self.host_choice = None # door opened by host
        self.second_user_choice = None # second door picked by user
    def set_first_user_choice(self, door):
        self.first_user_choice = door
        self.available_choices.remove(door)
        for i in self.available_choices:
            if self.doors[i] == "car":
                self.available_choices.remove(i)
                break
    def get_first_user_choice(self):
        return self.first_user_choice
    def set_host_choice(self, door):
        self.host_choice = door
        self.available_choices = [0, 1, 2]
        self.available_choices.remove(door)
    def get_host_choice(self):
        return self.host_choice
    def set_second_user_choice(self, door):
        self.second_user_choice = door
    def get_second_user_choice(self):
        return self.second_user_choice
    def get_outcome(self):
        return self.doors[self.second_user_choice]
    def get_available_choices(self):
        return self.available_choices
    def get_doors(self):
        return self.doors

    # methods for the three user strategies
    # the host always chooses randomly among the two other doors
    # when user's first choice is the door with the car
    
    def play_random_game(self):
        # user randomly chooses from unopened doors for second choice
        self.set_first_user_choice(random.choice(self.get_available_choices()))
        self.set_host_choice(random.choice(self.get_available_choices()))
        self.set_second_user_choice(random.choice(self.get_available_choices()))
    def play_stick_game(self):
        # user always repeats first choice again as second choice
        self.set_first_user_choice(random.choice(self.get_available_choices()))
        self.set_host_choice(random.choice(self.get_available_choices()))
        self.set_second_user_choice(self.get_first_user_choice())
    def play_switch_game(self):
        # user always switches door for second choice
        self.set_first_user_choice(random.choice(self.get_available_choices()))
        self.set_host_choice(random.choice(self.get_available_choices()))
        ac = self.get_available_choices()
        ac.remove(self.get_first_user_choice())
        self.set_second_user_choice(ac[0])
1 Like

Here is my take on this with 3 doors (in processing java mode)

Code
long w = 0, t = 0;
void setup() {
  float swapChance=0.5;
  for (int i = 0; i < 10000000; i++) {
    if (simulate(swapChance)) w++;
    t++;
  }
  println((float)w/(float)t);
}
boolean simulate(float swapRate) {
  boolean won;

  boolean correct[] = new boolean[3];
  int cr  =(int)random(3);
  correct[cr] = true;
  int pick=(int)random(3);
  boolean swap = (random(1)>swapRate);
  if (swap) { 
    if(pick!=cr) pick = cr;
    else pick = ((correct[0]==false)? 0 : ((correct[1]==false)? 1 : 2));
  }
  won = correct[pick];
  return won;
}

I have skipped the host opening the empty door since it is always empty (host knows the empty doors).
Therefore, I just ask if he wants to swap. If he originally had the correct door, he will loose, otherwise he wins.

1 Like

Just in case anyone is unfamiliar with problem view this clip from BBC.

1 Like

Thanks for the simulation, @CodeMasterX, and the video, @monkstone.

From the video and the other media we have seen, it is evident that there is much confusion out there regarding this problem.

Perhaps the following could clarify it.

We start out with the following, in any order:

door 1 door 2 door 3
goat goat car

If the contestant chooses a door and sticks with it, regardless of what the host does, there is a 1/3 chance of winning the car.

Of course, the initial configuration could be any of the following:

door 1 door 2 door 3
goat goat car

door 1 door 2 door 3
goat car goat

door 1 door 2 door 3
car goat goat

Let’s assume the contestant begins by choosing door 1, and has decided to switch, regardless of what the host does. That leaves one of the following, with a 1/3 probability for each one:

door 2 door 3
goat car

door 2 door 3
car goat

door 2 door 3
goat goat

Now the host opens one of the doors, either 2 or 3, revealing a goat. The door is chosen at random from the one or both that remain that have a goat.

Then, what remains is one of the following, with 1/3 probability of each:

remaining door
car

remaining door
car

remaining door
goat

The contestant, having decided in advance to switch, picks whichever door remains, with a 1/3 probability for each configuration above. Since two of those configurations contain the car, there is a 2/3 probability of winning it. The configurations and their probabilities would be equivalent, regardless of which door the contestant had chosen in the first place.

Therefore, switching is the best strategy.

1 Like