Search code examples
c#simulation

Monty Hall game show simulation giving me 50/50 results


I wanted to create a simple simulation for the Monty Hall problem as a fun, quick side project. My plan was to create the full game, forms with clickable doors and all, but I first wanted to create a way to simulate as many games as I wanted to prove to myself that switching doors gives a 66% chance of winning. The problem is that I am getting about 50/50 odds every time.

Can anyone find the problem with my simulation? When I increase the number of doors, the odds of winning after switching doors goes up. However, with 3 doors, I am always getting about 50/50 so I know there is an issue with my code.

private void simulateButton_Click(object sender, EventArgs e)
{
    int wins = 0;
    int losses = 0;
    int attempts = 0;
    int numberOfDoors = 3;

    // Main game loop. Number of simulations can easily be changed from 1000
    for (int games = 0; games < 1000; games++)
    {                               
        List<Door> doors = new List<Door>();
        for (int i = 0; i < numberOfDoors; i++)
        {
            doors.Add(new Door
            {
                Contents = "goat",
                Opened = false,
                PlayerChoice = false,
            });     
        }


        // Randomly pick a door for the car to be in
        Random random = new Random();
        int doorNumWithCar = random.Next(0, numberOfDoors - 1);
        doors[doorNumWithCar].Contents = "car";


        // I set player choice to always be door #1 to make it simpler
        doors[0].PlayerChoice = true;


        // Monty randomly removes one of the 2 doors not chosen by the player.
        // (obviously, he doesn't remove the door with the car)
        int doorMontyOpens;
        while (doors.Count != 2)
        {
            doorMontyOpens = -1;
            while (doorMontyOpens == -1)
            {
                int possibleDoorToBeShown = random.Next(1, doors.Count);

                if (doors[possibleDoorToBeShown].Contents != "car")
                {
                    doorMontyOpens = possibleDoorToBeShown;
                }
            }
            
            // I just remove the door since that is essentially what Monty Hall is doing when 
            // He reveals one of the goats
            doors.RemoveAt(doorMontyOpens);
        }


        // Since player choice is always door #1 (index 0) and there are only two doors left,
        // we only need to check door #2 at index 1. If it has the car, it is a win. If not, a loss.
        // This simulation only tests the case in which the player changes doors after Monty reveals
        // a door. So it SHOULD usually have about 667 wins and 333 losses. Why do I get 50/50???
        if (doors[1].Contents == "car")
        {
            wins++;
        }
        else
        {
            losses++;
        }

        attempts++;
        this.winsTextbox.Text = $"{wins}";
        this.lossesTextbox.Text = $"{losses}";
        this.attemptsTextbox.Text = $"{attempts}";                
    }

    MessageBox.Show("Done!");
}

public class Door
{
    public string Contents { get; set; }
    public bool Opened { get; set; }
    public bool PlayerChoice {  get; set; }
}

Don't mind the crappiness of the code. This was meant to be a super quick and dirty simulation. Explanations for each section of code are in the comments. Thanks!


Solution

  • The problem is here:

    int doorNumWithCar = random.Next(0, numberOfDoors - 1);
    

    The upper bound (the second parameter) of the Next is exclusive (see the Random.Next(Int32, Int32) docs) so by using numberOfDoors - 1 you are limiting car to being present only in one of the first two doors, so the original guess is 50/50 instead of ~33/~66 which makes the final guess also 50/50.

    It should be

    int doorNumWithCar = random.Next(0, numberOfDoors);
    

    Fixed demo @dotnetfiddle