0

I'm pretty new to programming, so please pardon me!

***EDIT 2: Ok so this is the full thing. Everything is working fine except when I try to break out of the while enthusiasm is True: loop, I just keep getting more and more questions (the loop keeps running)

I'm building a Python trivia quiz, and I managed all the answer input loop to work (doesn't allow invalid input, breaks the loop succesfully)

I want the program to ask the user "Would you like another question? (y/n)" and stop the program if 'n'.

The problem is no matter what I tried, I keep getting more trivia questions! Thanks in advance

import requests
import pprint
import json
import html
print("Welcome to the ultimate test of knowledge and valour, The Internet Quiz!")
print("You will be given an array of multiple choice questions in general knowledge")
input("Press Enter to start!")

y_n = ["y", "n"]
import random
q_num = 1
score = 0
enthusiasm = True
while enthusiasm is True:
    r = requests.get("https://opentdb.com/api.php?amount=1&category=9&type=multiple")
    question = json.loads(r.text)
    first = question['results'][0]['correct_answer']
    second = question['results'][0]['incorrect_answers'][0]
    third = question['results'][0]['incorrect_answers'][1]
    fourth = question['results'][0]['incorrect_answers'][2]
    print("--------------------------------------------------------")
    print("Question number " + str(q_num)+ ":")
    print(html.unescape(question['results'][0]['question']))
    options = [first,second,third,fourth]
    random.shuffle(options)
    for X in options:
        print(html.unescape(X))
    legend = (
        (options[0], 1),
        (options[1], 2),
        (options[2], 3),
        (options[3], 4)
        )

    error = False
    while error is False:
            
        guess = input("Please enter the number of your answer(1-4):")
        try:
            guess = int(guess)
        except:
            print("Your answer must be a number between 1-4.")
            continue
        if guess <1 or guess > 4:
            print("Your answer must be a number between 1-4.")
            continue
        else:
            error = True

        
        if (first, guess) in legend:
            score += 1
            q_num += 1

            print("Correct! \nCurrent score: " +str(score))
            
            acid = True
            while acid is True:
                yesno=input("Would you like another question? (y/n)")     
                try:
                    yesno = str(yesno.lower())
                except:
                    print("Invalid input. Please enter y/n.")
                    continue
                if yesno.lower() != "y" and yesno.lower() != "n":
                    print("Invalid input. Please enter y/n.")
                    continue
                elif yesno.lower() == "y":
                    break
            
                else:
                    acid=False
                    error=True
                continue
        
        
        
        else:
            print("Incorrect! Better hit the books, buddy! \nCurrent score: " +str(score)) 
            q_num += 1
            acid = True
            while acid is True:
                yesno=input("Would you like another question? (y/n)")     
                try:
                    yesno = str(yesno.lower())
                except:
                    print("Invalid input. Please enter y/n.")
                    continue
                if yesno.lower() != "y" and yesno.lower() != "n":
                    print("Invalid input. Please enter y/n.")
                    continue
                elif yesno.lower() == "y":
                    break
            
                else:
                    acid=False
                    error=True
            continue
Rey Fit
  • 15
  • 4
  • 2
    You're using awkward truth tests. If a variable `x` is a `bool`, then when testing it, you want to use things like `if x:` or `while not x:` etc. You don't compare them to see if they're `== True` or `== False`, since you *already* have a truth value. It's a lot like doing `if (((x == True) == True) == True) == True:` instead of just `if x:`. The only time to compare a value for equality with a `bool` constant is if it may have a type other than `bool`, which is something one usually avoids. – Tom Karzes Jul 25 '21 at 19:45
  • 1
    The code is incomplete, please create an [MCVE](https://stackoverflow.com/help/minimal-reproducible-example) – Michael Butscher Jul 25 '21 at 19:45
  • 1
    Hi Rey, welcome to SO. Definitely take a look at the "minimal example" that Michael linked. In this case, for example, I find `if (first, guess) in legend` very suspicious (are you thinking it might be equivalent to "if first in legend and guess in legend", perhaps, because it is not), but I have no way to know because you haven't given us what "legend" or "first" are. If we had enough info to run your code completely, we could diagnose much faster! – en_Knight Jul 25 '21 at 20:17
  • 1
    It looks like you changed `if x == True:` to `if x is True:`, which makes no more sense. Just use `if x:`. You don't need to keep doing all these bizarre comparisons with `True` and `False`. – Tom Karzes Jul 25 '21 at 20:17
  • @TomKarzes 's advice is explained here I think: https://stackoverflow.com/a/4050625/2329988 – en_Knight Jul 25 '21 at 20:19
  • I appreciate your edit! I don't think the issue is that it isn't "minimal" enough, but that it isn't a "verifiable example" because we can't run it. If you take exactly what you wrote, and try and run it, does it? If not, it's very difficult to tell you what's going wrong – en_Knight Jul 25 '21 at 20:24
  • Part of the problem with this code is there are a lot of useless, redundant things in it. For example, `yesno` is a string, having come from `input`. If then tries to convert it to lowercase within a `try`, even though it can never fail and the entire `try` is unnecessary. It then explicitly converts the result to a string, even though it's already a string. And, for good measure, if then calls the `lower` method on the now-lowercase value multiple times after that, for no reason at all. This sort of bloated, keep-adding-things approach to code writing never produces good results. – Tom Karzes Jul 25 '21 at 20:59
  • @TomKarzes that's a good point, I hope I'm not being confusing by giving contradictory advice. Rey, I really appreciate that you keep editing your code in response to comments, that's great. To get the best responses, you want it to *minimal*, as Tom is describing (cutting out things that are unnecessary), but *reproducible*, in that it runs completely and we can try it, so don't cut out variables that are going to be used in the remaining code. I totally get that it can be difficult to make things minimal when you're new at the language and don't know what to cut out. – en_Knight Jul 25 '21 at 21:36

2 Answers2

0

Possible to stop the program with:

import sys

sys.exit()

AdR
  • 572
  • 1
  • 4
  • 13
  • Hi AdR, this seems true as an isolated statement, but does not address the control flow logic problem in the code, correct? – en_Knight Jul 25 '21 at 21:40
0

You have two while loops:

while enthusiasm is True:

and

while error is False

Let's look at what happens when you ask if the user wants another question (simplified a little):

            yesno=input("Would you like another question? (y/n)")     
            if yesno.lower() == "y":
                break            
            else:
                error=True

You set error to True, but you do not set enthusiasm to False, so the loop starts again at the top. Looking further, enthusiasm never gets set to False, so that condition will always start over. Simply writing

else:
    error = True
    enthusiasm = False

would be fine. I also would recommend thinking about if you want two loops, and what the purpose of the enthusiasm variable is.


There's a lot of other refactoring that can be done here, but not that you probably should write

while not error:

instead of explicitely checking if error "is" False. Similarly,

while enthusiasm:

is a good check on a boolean

Similarly,

            yesno=input("Would you like another question? (y/n)")     
            try:
                yesno = str(yesno.lower())
            except:
                print("Invalid input. Please enter y/n.")
                continue
            if yesno.lower() != "y" and yesno.lower() != "n":

can be improved. You don't need to cast yesno to a string, becuase that's what happens when it comes from input. You don't need the try/except either (you may have taken this from an int cast, elsewhere). Since you've already converted yesno to lower once, you don't need to keep doing that every time you compare it to a y or n.

en_Knight
  • 5,301
  • 2
  • 26
  • 46
  • Thank you so much for all the input! en_Knight's comment was super helpful I managed to find the issue finally: I was using the 'break' function in the wrong place (I tried to break the loop for user input "n" instead of "y". I now see it needs to be break for "y" because breaking just goes back to the old question asking loop.) Secondly I found that I was incorrectly using else to make enthusiasm=False, and that was what I needed to break the question asking loop(because 'while enthusiasm: *ask another question*'.) – Rey Fit Jul 26 '21 at 10:32