5
\$\begingroup\$

I'm pretty new to Python (I've been doing it for about 4 months, but not every day) and I've been struggling to get to grips with how to use functions and deploy them efficiently to solve problems. To test my abilities, I created the below program to simulate the Monty Hall problem. The program does the following things:

  • It chooses a winning door at the start from list door = [1,2,3].
  • It then asks the user to choose a door.
  • Then it checks to see if the user's guess matches the winning door.
  • If the user guesses the winning door, it randomly selects one of the two remaining doors to 'open'. If, however, the user doesn't guess the winning door, it 'opens' the one remaining unpicked door.
  • It then asks the user if they want to switch doors. If the user picked the correct door first time out, then sticking with that choice wins the game and switching loses. The reverse is true if the user didn't pick the correct door first time.
  • Finally, it updates a running tally of wins and losses, then asks the user if he wants to play again.

The program works and the probabilities are correct (I ran it 100 times just to make sure and got 74 wins and 26 losses, which is around about what I'd expect). I'm just not sure the program is as efficient as it could be. In fact, given my relative lack of experience I'm pretty sure it isn't. Would anyone be able to tell me how good they think the code is, how I could go about improving it?

import random

win = 0
lose = 0

while True:

    door = [1,2,3]
    x = 0

    def pick_answer():
        """Computer chooses the winning door"""
        answer = random.choice(door)
        return answer
    a = pick_answer()

    def pick_guess():
        """User guesses winning door"""
        guess = int(input("Please choose a door (1-3): ").strip())
        return guess
    b = pick_guess()

    if a == b:
        def montys_door_if_a_is_b():
            """If answer = guess, pick one of the remaining doors to open"""
            global x
            door = [1,2,3]
            door.remove(a)
            x = random.choice(door)
            return x
        c = montys_door_if_a_is_b()
        print("You chose door {}".format(b))
        print("I choose door {}".format(c))

        def montys_final_choice():
            """Tells user which 2 doors are now left"""
            door = [1,2,3]
            door.remove(x)
            door.remove(a)
            y = door[0]
            return y
        d = montys_final_choice()

        print("You're left with doors {} and {}".format(b,d))
        choice = input("Will you SWITCH to door {}? y/n: ".format(d)) 

        def switch_or_stick():
            """Asks user if they want to switch. y = Lose / n = win"""
            if choice == "y":
                print("Bad luck.  You lost.  You should have stuck as you already had the right answer.")
                global lose
                lose = lose + 1
                print("{} wins and {} losses".format(win,lose))
            else:
                print("Huzzah! You made the right choice by STICKING!")
                global win
                win = win + 1
                print("{} wins and {} losses".format(win,lose))
        switch_or_stick()

        input("\nPress Enter to play again")

    else:
        def montys_door_if_a_not_b():
            """If answer != guess, remove answer & guess and 'open' the remaining door"""
            door = [1,2,3]
            door.remove(a)
            door.remove(b)
            md = door[0]
            return md
        e = montys_door_if_a_not_b()
        print("You chose door {}".format(b))
        print("I choose door {}".format(e))

        def montys_final_choice_if_a_not_b():
            """Asks user to choose between answer & guess"""
            door = [1,2,3]
            door.remove(e)
        f = montys_final_choice_if_a_not_b()

        print("You're left with doors {} and {}".format(a,b))
        choice = input("Will you SWITCH to door {}? y/n: ".format(a)) 

        def switch_or_stick_redux():
            """Since answer != guess, if user sticks, he loses but if he switches he wins"""
            if choice == "y":
                print("HUZZAH!  YOU WIN")
                global win
                win = win + 1
                print("{} wins and {} losses".format(win,lose))
            else:
                print("Bad luck.  You should have switched. You chose the wrong door at the start")
                global lose
                lose = lose + 1
                print("{} wins and {} losses".format(win,lose))
        switch_or_stick_redux()

        input("\nPress Enter to play again")
\$\endgroup\$
2
\$\begingroup\$

Functions should be used to cut down on code repetition so as to make code more concise and more readable. But you are constantly defining functions only to immediately invoke them once on the very next line. That makes the code less concise and less readable, and also forces you to introduce superfluous global variables. (It also seems like a JavaScript idiom -- are you coming from that language?) The code can be made more concise like this:

import random

win = 0
lose = 0
playing = True

while playing:

    door = [1,2,3]
    x = 0
    a = random.choice(door) #Computer chooses the winning door
    b = int(input("Please choose a door (1-3): ")) #User guesses winning door -- .strip() is pointless
    if a == b:
        #If answer = guess, pick one of the remaining doors to open
        door.remove(a)
        x = random.choice(door)
        c = x
        print("You chose door {}".format(b))
        print("I choose door {}".format(c))

        #Tells user which 2 doors are now left:
        door = [1,2,3]
        door.remove(x)
        door.remove(a)
        d = door[0]
        print("You're left with doors {} and {}".format(b,d))
        choice = input("Will you SWITCH to door {}? y/n: ".format(d)) 

        #Ask user if they want to switch. y = Lose / n = win
        if choice == "y":
            print("Bad luck.  You lost.  You should have stuck as you already had the right answer.")
            lose += 1
        else:
            print("Huzzah! You made the right choice by STICKING!")
            win += 1
        print("{} wins and {} losses".format(win,lose))
    else:
        #If answer != guess, remove answer & guess and 'open' the remaining door
        door.remove(a)
        door.remove(b)
        e = door[0]
        print("You chose door {}".format(b))
        print("I choose door {}".format(e))

        #Ask user to choose between answer & guess
        print("You're left with doors {} and {}".format(a,b))
        choice = input("Will you SWITCH to door {}? y/n: ".format(a)) 

        #Since answer != guess, if user sticks, he loses but if he switches he wins
        if choice == "y":
            print("HUZZAH!  YOU WIN")
            win +=1
        else:
            print("Bad luck.  You should have switched. You chose the wrong door at the start")
            lose += 1
        print("{} wins and {} losses".format(win,lose))

    ans = input("\nPress Enter to play again, 'q' to quit")
    if len(ans) > 0: playing = False

A few other points:

1) int() strips its input -- so the .strip() there was pointless

2) You are setting door = [1,2,3] more often than needed

3) Things like win = win + 1 were replaced by win += 1

4) The line print("{} wins and {} losses".format(win,lose)) was moved to after then if statement

5) montys_final_choice_if_a_not_b had absolutely no effect and was simply removed

6) I made it so that it you can break out of the infinite loop without closing the Python shell.

There are still improvements that can be made. In particular, you should find a way so that the code for the dialogue to switch doors occurs only once rather than twice, but this should be enough to get you started.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.