You might have noticed that we at Axure HQ like our fun to be as random as possible—it helps to keep us on our toes! To give you an idea of the random fun we have, we’ve shared Kip’s Magic 8 Ball, Rachel’s Slot Machine, Julie’s A Game of Cards, Alex’s Deal or No Deal, Tuomas’ Threes!, and most recently Alyssa’s Rock, Paper, Scissors, Lizard, Spock. Well, today’s the day to add another installment in our Fun with Random series—a presentation of the classically confusing and brainteasing “Monty Hall Problem”.
This puzzle, first posed in 1975 and popularized by Marilyn vos Savant in 1990, takes its name from the original host of TV’s “Let’s Make a Deal” and is loosely based on a game show format. You start off with three closed doors. The host announces that there’s a car behind one of the doors and goats behind the other two. You select one of the doors, and the host opens a door that you didn’t pick to reveal a goat. The host now asks “Would you like to change your selection?” Herein lies the crux of the puzzle: is there any benefit to changing your selection or to keeping it the same, or is there no benefit either way?
Read more about the Monty Hall Problem here: https://en.wikipedia.org/wiki/Monty_Hall_problem
To get the game ready, I use a hot spot called “Initializer”—this is triggered by moves on OnPageLoad and the eventual reset button. Initializer sets up some randomness, sets some text, and clears out a bunch of variables:
Those first two lines are really important and help to set up the randomness that this puzzle needs. A variable called “CarLocation” is set as follows:
[[Math.floor(Math.random() * 3) + 1]]
Math.random provides a random value in the range [0, 1), meaning that it can return 0 but not 1. This is multiplied by 3 and then used as the argument for Math.floor, which returns the largest previous integer. That means that if Math.random * 3 gives 2.999999, Math.floor will return 2. To make sure I have a nice and neat set of values, I add 1. Now, my function will randomly give me 1, 2, or 3.
This is great, but I need another random value for the game because of the way the host behaves. Let’s say I choose Door 1, and the car is behind Door 2. The host will automatically open Door 3, because they need to open a door that a) I didn’t select and b) reveals a goat. The logic for that is easy: if my first selection is 1, and CarLocation is 2, then the host opens 3. But if I happen to choose the door that contains the car, the host has a decision to make: both other doors contain goats, but only one can be opened. For this, I generate another random value, called “MisdirectVariable”, that is either 1 or 2:
[[Math.floor(Math.random() * 2) + 1]]
Now, with either 1 or 2, the host “makes a decision” to open a door that’s not the one I chose but that isn’t predictable. Using these two values, along with a variable called “FirstSelection” that stores my first choice, there are 12 potential outcomes that are evaluated by a hot spot called “Click Functions”:
These are followed by four similar cases when CarLocation is 2, and four when CarLocation is 3.
Once this is evaluated, one of the doors will open to reveal a goat, accompanied by a fancy sliding door animation. To fit in a bit more randomness, there are actually four different goats and four different cars that can be shown throughout the game. These are contained in different states of two dynamic panels (one each for cars and goats) which are set according to, you guessed it:
[[Math.floor(Math.random() * 4) + 1]]
My second choice will reveal either a goat or a car, but this time the logic is a bit more straightforward, with 9 total cases:
And so on for each combination of SecondSelection and CarLocation.
We could stop there, but that wouldn’t really help to tackle the Monty Hall Problem, would it? This “problem” is a problem because of the statistical logic at play. It’s easy to assume that, after the host opens a door, you have a 50/50 chance with the remaining two. As Marilyn vos Savant wrote, though, and as numerous simulations have shown, switching will actually improve your odds to 2-in-3. By playing this version of the the Monty Hall Problem an infinite number of times (you know you want to!), you too can demonstrate that the probability of winning a car after changing your selection will eventually settle on 67% while the odds without switching will stay at the predicted 33%.
To keep track of the dataset that develops as you play the game—how many games you’ve played and how many times you’ve won, plus how many times you switched and how many times you won after switching—there’s a hidden repeater that stores, for each round, your first and second selections and your prize (car or goat). Armed with this data, we can set out to prove or disprove vos Savant’s assertion.
Whenever you finish a round and the “You won a ___” message appears, the relevant data is added to a new row in the repeater and another hot spot is moved. This one, appropriately called “Stats Generator”, both calculates the statistics and prints them:
It uses the Repeater.dataCount method to get the total number of rows in the repeater—that’s the number of rounds played so far.
It sets a filter “Cars Won”, [[TargetItem.Prize == ‘Car’]], to limit the repeater to just the rounds in which a car has been won, and then uses the Repeater.itemCount method to grab the number of rows left—this is the total number of cars won over the course of play. You’ll notice that there’s a “Wait 1 ms” action here—that forces Axure RP to execute the steps sequentially. Without it, Axure RP will try to apply the filter at the same time that it performs .itemCount, which will return unexpected values.
It removes the car filter and calculates the total win percentage thus far: ((TotalGamesWon/TotalGamesPlayed) * 100).toFixed(0), which gives us a nice, clean integer between 0 and 100.
It sets a filter “Times Switched”, [[TargetItem.FirstSelection != TargetItem.SecondSelection]], to give us just the games in which the selection was changed, and then repeats the above process on this smaller set of data to give us the total numbers of games with and without a switch, and the total numbers of games won with and without a switch.
The widget is moved again, causing it advance to a few cases that allow it to print legible statistics while avoiding printing the results of a divide-by-zero operation, which would display as “NaN’”—“not a number”.
Finally, there’s a “Reset stats” button, which deletes all rows from the repeater and hides the statistics printout, and a “Play Again” button that triggers the “Initializer” hot spot, getting the environment ready to play again (clearing all variables and then generating all of our wonderful random numbers).
Now, why would I build the statistics with a repeater instead of with simple variables? It would’ve been pretty straightforward to just increment a few values whenever a certain result is reached. Aside from wanting to illustrate one potential usage of the repeater widget, I wanted some flexibility that flat variables don’t necessarily provide. Let’s say that, at some point, I thought it would be interesting to see if I have a tendency to choose one door first over the other two. I could set up variables to track the total number of times that each door is selected first, or I could set three different filters on the repeater and count the number of rows. Then let’s say I want to know if I have a tendency to pick Door 2 or Door 3 as my second choice after having first chosen Door 1. Again, we could use two more variables and increment them, or we could simply use a couple of additional filters on the repeater. With variables, we need at least one new variable for each piece of data we want to track. With the repeater, we keep the dataset the same and use a range of filters to glean lots of different things from it.
Feel free to poke around in the .rp file, which I’ve attached here.
MontyHallProblem.rp (1.56 MB)
Once you’ve had a chance to play through a few (hundred (thousand)) times, let me know what you think! I’d love to hear any feedback, suggestions, or questions you might have. Thanks for checking it out!