DEV Community

Cover image for Monty Hall Problem Simulation in JS
David Bambušek
David Bambušek

Posted on

Monty Hall Problem Simulation in JS

The Game

If you don't know the Monty Hall Problem it's quite famous mathematical problem, that got it's name after the TV game show host Monty Hall. The show was called Let's Make a Deal and involved games in which "traders", selected members from the audience, were making deals with the host.

Usually, the trader was given a certain prize and was asked, if he wants to trade it for something else. Something that only the host knew what was and what was usually hidden behind some curtain, doors, in a box etc.

In one of the games, the trader was presented with three closed doors. Behind one of them, there was a brand new car, behind the remaining two, there were "zonks" - items of no value.

Alt Text

Trader was asked by Monty to choose one of the doors.

Alt Text

When trader made his choice, Monty went ahead and opened one of the two remaining doors, where he knew there was a zonk.

Alt Text

This left the trader with two unopened doors, one with the car behind it, one with a zonk.

And here comes the crucial part - at this moment, Monty offered the trader, that he can change his initial door selection.

Alt Text

And the question is - what is the best strategy for the trader at this moment? Should he keep the initial selection, should he go for the other doors or does it even matter which doors he chooses?

If the answer was obvious, this would have probably not became a famous mathematical problem, therefore I recommend thinking about it twice before saying the answer...

You should always change your initial selection to the other unopened doors!

Changing the initial selection of doors won't guarantee wining the car, but it will give the trader much higher chance of winning. That means, if he was to play this games trillion times, this strategy would bring him more wins than losses.

Probability

How comes it is always better to change the selection? It might seem counter-intuitive at first, but we will make it clear and also prove it by a simulation of this game in JavaScript!

Usually, there are two answers when you ask people what is the best strategy and what is the probability of winning for it:

  • it doesn't matter if I change the selection, I am choosing from two doors, so it is 50%-50%
  • it is better to switch, the chance that car is behind the doors I selected initially is only 33.3% as I was choosing from three doors, now there are only two of them, so the remaining doors gets 50% chance of winning

And yes, the answer is none of those two. It is better to switch, because the other doors represent 66.6% chance of winning, ultimately "concentrating" the chance of winning from all the doors opened by Monty and its own chance of winning.

This becomes much clearer, if we change the game setup to ten doors, one car, nine zonks. Trader picks one of the doors, Monty opens eight of nine remaining doors, where he knows there is a zonk. Again, two doors remain unopened.

The initial selected doors represent 10% chance of winning, as there are 10 unopened doors, which applies to any of the doors. When Monty opens eight other doors, he can not touch the initially selected doors by the trader, so he does not give any information about them, but what he gives is information about all the remaining doors.

The car is not behind any of the eight opened doors, which means there is quite high probability it's behind that remaining doors, as Monty was forced to leave the initially selected doors alone and could only care about the remaining nine. And the chance of winning the car is equal to the sum of chances of all the opened doors, plus of the remaining unopened and not initially selected doors, that is (8 + 1) x 10%, which is 90%. Quite a nice chance of winning a car, isn't it?

There are always only two possible scenarios for the game:

  1. Trader hit the doors with a car behind them as his initial selection, in this case, Monty knows that there are zonks behind all other doors, so he can leave any of them unopened
  2. Trader hit the doors with a zonk behind them as his initial selection, in this case, Monty must leave the doors with a car behind them unopened.

The more doors you add, the bigger the chance of winning is, as Monty always opens all doors except the one the trader initially selected and one other.

Simply put, the initial selection has always the chance of winning equal to 1:<number of doors>, and the chance that it is behind any other doors is the rest to 100% (which is <number of doors>-1:<number of doors>). And as Monty tells the trader about all the doors that contain zonks, but one, it is clear, that the chance of winning gets concentrated into that one.

Simulation

At this point, you might still be in a point of "I don't believe you, that's weird" and I get you. But hey, let's play the game virtually and see the real results. And let's play it million times, so we can really say it works as we described. Let's fire up some code editor and dust off our JS skills.

Game of N Doors

The game in TV was played with three doors, but we want to play with any number of doors, to prove that game with more doors gives the trader higher chance of winning. The game will therefore be a function with one input numberOfDoors.

function playGame(numberOfDoors){//...}
Enter fullscreen mode Exit fullscreen mode

First thing to do is to create a representation of doors. We will use an array for that. We need to create an array of length equal to the number of doors.

const doors = new Array(numberOfDoors);
Enter fullscreen mode Exit fullscreen mode

Now we need to place zonks in all doors but one, where we will place a car. Zonk will be represented by false value and car by true. What I will do is place zonks behind all doors.

doors.fill(false);
Enter fullscreen mode Exit fullscreen mode

And then I will pick a random door index and switch the zonk for a car. Say bye to zonk, and welcome the brand new shining car!

const carIndex = Math.floor(Math.random() * numberOfDoors);
doors[carIndex] = true; 
Enter fullscreen mode Exit fullscreen mode

Now it is time for a trader to chose one of the doors.

const traderSelection = Math.floor(Math.random() * numberOfDoors);
Enter fullscreen mode Exit fullscreen mode

Monty's turn - he is going to open all the doors, but the one that trader selected and one another. Monty knows where the car is and where the zonks are, so he needs to proceed as follows not to uncover the car - if the trader selected doors with a zonk, he must open all other doors, but the one with the car in it.

let remainingDoorsIndex;
if(traderSelection !== carIndex){
  remainingDoorsIndex = carIndex;
}
Enter fullscreen mode Exit fullscreen mode

However, if the trader selected the doors where there is actually the car, Monty can be calm and randomly keep one of the doors closed and open all others, cause he knows there are zonks behind all of them.

else {
  remainingDoorsIndex = Math.floor(Math.random() * (doorsNumber - 1));
  if(remainingDoorsIndex >= traderSelection){
    remainingDoorsIndex++;
  }
}
Enter fullscreen mode Exit fullscreen mode

There are many ways how we could randomly select one of the remaining doors, but here we are re-indexing all doors, ignoring the one picked by trader.

We said that the best strategy is to switch the doors, so the last step is to pick the other doors.

const price = doors[remainingDoorsIndex];
return price;
Enter fullscreen mode Exit fullscreen mode

Our function playGame will return true if the "always switch" strategy resulted into driving home in a new car, or false when the trader lost.

Playing Till Infinity

Now, to be sure our strategy really works over long time, we need to play the game many and I mean many many times. The more we play, the closer to the expected probability we will get. A million times will be enough to meet the "many many" requirement for our purposes and not to set your browser on fire.

playSeries(numberOfGames, numberOfDoors){
  let wins = 0;
  for(let i = 0; i < numberOfGames; i++){
    if(playGame(numberOfDoors)){
      wins++;
    }
  }
  const winsPercentage = wins / numberOfGames * 100;
  console.log(`Games won: ${wins} = ${winsPercentage}%`);
}
Enter fullscreen mode Exit fullscreen mode

Let's try the game with three doors.

const MANY_GAMES_CONSTANT = 1000000;
playSeries(MANY_GAMES_CONSTANT, 3); 
// Result around 66%
Enter fullscreen mode Exit fullscreen mode

And now with 10 and 100 doors.

playSeries(MANY_GAMES_CONSTANT, 10); 
// Result around 90%
playSeries(MANY_GAMES_CONSTANT, 100); 
// Result around 99%
Enter fullscreen mode Exit fullscreen mode

Cool, looks like we have proved our hypothesis, that the "always switch" strategy is the best! You might still not believe it, but here it is, JS right in your face.

Hope you enjoyed the article and here is all the code at one place:

Top comments (0)