Robo-Monty
JoeStrout

JoeStrout @joestrout

About: Creator of the MiniScript programming language and Mini Micro retro-styled virtual computer.

Joined:
Oct 25, 2022

Robo-Monty

Publish Date: Jun 8
0 0

I'm traveling this week with another family (our neighbors and BFFs), including two pre-teen kids who were amusing themselves in the car by playing a variant of the classic Monty Hall game. In this game, a game show host hides prizes behind three doors. One of them is a brand new car; the other two are mangy old goats. The player (who strongly prefers the car) picks a door. The host then opens one of the other two doors, revealing a goat, and then asks the player if they want to stick with their first choice, or switch to the other door.

The vast majority of people who have not encountered this puzzle before — including a great many mathematicians and other Ph.D.'s — incorrectly think that the remaining two doors have an equal chance of hiding a car, and so it doesn't matter if you switch. This is incorrect; your odds of winning are twice as high if you switch! But the human mind has a great deal of trouble accepting this. (Even though pigeons, apparently, can learn this with little trouble.)

Or so the math says. But does this counterintuitive strategy really work? Let's find out!

Build a robot to play Monty Hall

In this column, we're going to bang out a little program using MiniScript to automatically play this game many times, using any strategy you choose. It'll keep track of how often the game is won, so you can see for yourself what strategy does best.

You can run this code in Mini Micro, or using command-line MiniScript, or even the Try-It! page. Whichever you choose, start by defining a couple of functions to make the player's decisions in the game.

// initialPick: return which door (0, 1, or 2)
// you want to guess initially.
initialPick = function
    // Let's pick one at random.
    return floor(rnd * 3)
end function

// switch: return true to switch, or false to stay
// with your initial pick, given the current state
// like "?", "goat", "PICK".
switch = function(state)
    // Let's always switch!
    return true
end function
Enter fullscreen mode Exit fullscreen mode

Note that MiniScript, like most modern programming languages, starts indexing at 0 — so our door numbers are 0, 1, and 2.

Cartoon of Monty Hall game with doors numbered 0, 1, and 2.

The versions above pick an initial door at random, and then always switch after some non-car door is revealed. But feel free to change these functions however you like! You could have initialPick always choose door 0, if that makes sense to you. Have switch always return false to never change your initial pick, or even use input to ask the user (although in that case, it's not really a robot playing the game!).

Now, let's write a function to play one round of the game, making use of the functions above for the player's decisions.

// doOneGame: run the game once, and return true
// if the player wins, false if the player loses.
doOneGame = function
    prizes = ["goat", "goat", "goat"]
    carPos = floor(rnd * 3)
    prizes[carPos] = "CAR!"
    knownState = ["?", "?", "?"]

    // Let the player pick a door
    print "Doors: " + knownState.join(", ")
    pick = initialPick
    knownState[pick] = "PICK"
    print "You pick #" + pick + "; Doors: " + knownState.join(", ")

    // Now, let the host reveal a goat
    while true
        revealPos = floor(rnd * 3)
        if revealPos != pick and prizes[revealPos] == "goat" then break
    end while
    knownState[revealPos] = prizes[revealPos]
    print "Host opens #" + revealPos + "; Doors: " + knownState.join(", ")

    // See if the player wants to switch to the other door
    print "Do you switch? ", "..."
    if switch(knownState) then
        pick = knownState.indexOf("?")
        print "YES!  You switch to " + " #" + pick + "."
    else
        print "No."
    end if

    // Reveal whether they won the car
    print "Prizes: " + prizes.join(", ")
    print "You get a " + prizes[pick], ""
    if prizes[pick] == "goat" then
        print " ...You lose!"
        return false
    else
        print " ...YOU WIN!"
        return true
    end if
end function
Enter fullscreen mode Exit fullscreen mode

This looks a little complicated, but it's understandable if you go through it step by step. We keep two lists: one (prizes) is the actual prizes, where each element is either "goat" or "CAR!". The other one (knownState) represents what the player actually knows about the three doors. Each element here is either "?", "PICK" (indicating the player's choice, though they still don't know what's behind it), or "goat" (a non-car door revealed by the host).

So this code has the player pick a door, and updates the known state by replacing "?" with "PICK" at that position. Then the host chooses another door — one that's not the player's pick, but also not the car — and reveals that. Then the player is given a chance to switch to the other door (the only one still marked "?" in the known state). Finally, all the prizes are revealed, and we report whether the player successfully picked the door with the car.

Cartoon showing contestant winning the car

Make a main loop

You could run the code at this point and play the game once by just adding

doOneGame
Enter fullscreen mode Exit fullscreen mode

to the end of your program. In fact, do that! It's always a good idea to test your code early and often.

But then you'll want a way to play the game over and over, so you can really get a feel for how your strategy performs in the long run. So, let's replace that single doOneGame call with this main loop:

played = 0
won = 0
auto = false
while true
    print
    played += 1
    if doOneGame then won += 1
    print "Won: " + won + "/" + played + " (" + 
      round(100 * won / played) + "%)"
    if auto then
        wait 0.5
    else
        inp = input("Play again, Autoplay, or Quit: ").upper[:1]
        if inp == "A" then
            auto = true
        else if inp == "Q" then
            exit
        end if
    end if
end while
Enter fullscreen mode Exit fullscreen mode

This keeps a count of how many games have been played and won, and after each game, displays the updated statistics. Then, if we're not already in auto-play mode, it asks the user what to do next. The player can enter "A" to start auto-play mode, "Q" to quit, or anything else to play one more time. Once auto-play mode has been triggered, it skips this input, and automatically plays again after a short delay. (Feel free to change or remove wait 0.5 if you want it to go faster or slower.)

Enlightenment Through Simulation

I've always said, you don't really understand something unless you can simulate it. (For example: I can't simulate quantum mechanics!)

This applies even to a relatively simple problem like this. According to Wikipedia, Paul Erdős, one of the most prolific mathematicians in history, remained unconvinced of this always-switch strategy until he was shown a computer simulation of it.

With this short MiniScript program, you can play around with different strategies, study individual games to see what's going on, and then let it run for hundreds of games to see the average result. You'll come to understand the game much more deeply this way than you would with any other method.

This same approach can be used for many other problems too. Physics, chemistry, math, lotteries, fictional magic systems, or anything else — if you can describe the rules clearly enough to write code for it, then you can simulate it. The simulations will show emergent behavior which may not be obvious from a simple study of the rules, and that's where things get fun and interesting.

What do you think? Do you believe you should always switch in the Monty Hall game? What other systems would be fun to simulate? Leave your thoughts in the comments below!

Comments 0 total

    Add comment