1
\$\begingroup\$

I have been programming a maths quiz that can be used for teachers an I have have been trying to make the code as short as possible so it is easier to understand. If you have seen my previous post you maybe be wondering why I am re-posting as I already have received feedback, when trying to act on this feedback I seemed to have got stuck again.

In my code, there's a lot of prompting the user for an input and then checking to see if it's valid - so I have been trying to turn this into a function.

Now some of you maybe confused as why I am unable to do this as I have already done this with the variable get_bool_input. However, for this desired function, it needs to be used in a different context which is what I am unable to do.

import sys
import random

def get_bool_input(prompt=''):
    while True:
        val = input(prompt).lower()
        if val == 'yes':
            return True
        elif val == 'no':
            return False
        else:
            sys.exit("Not a valid input (yes/no is expected) please try again")

status = input("Are you a teacher or student? Press 1 if you are a student or 2 if you are a teacher")

if status == "1":
    score=0
    name=input("What is your name?")
    print ("Alright",name,"welcome to your maths quiz")

    level_of_difficulty = int(input(("What level of difficulty are you working at?\n"
                                 "Press 1 for low, 2 for intermediate "
                                    "or 3 for high\n")))
    if level_of_difficulty not in (1,2,3):
        sys.exit("That is not a valid level of difficulty, please try again")

    if level_of_difficulty == 3:
        ops = ['+', '-', '*', '/']
    else:
        ops = ['+', '-', '*']

    for question_num in range(1, 11):
        if level_of_difficulty == 1:
            number_1 = random.randrange(1, 10)
            number_2 = random.randrange(1, 10)
        else:
            number_1 = random.randrange(1, 20)
            number_2 = random.randrange(1, 20)

        operation = random.choice(ops)
        maths = round(eval(str(number_1) + operation + str(number_2)),5)
        print('\nQuestion number: {}'.format(question_num))
        print ("The question is",number_1,operation,number_2)

        answer = float(input("What is your answer: "))
        if answer == maths:
            print("Correct")
            score = score + 1
        else:
            print ("Incorrect. The actual answer is",maths)

    if score >5:
        print("Well done you scored",score,"out of 10")
    else:
        print("Unfortunately you only scored",score,"out of 10. Better luck next time")

    class_number = input("Before your score is saved ,are you in class 1, 2 or 3? Press the matching number")
    while class_number not in ("1","2","3"):
        print("That is not a valid class, unfortunately your score cannot be saved, please try again")
        class_number = input("Before your score is saved ,are you in class 1, 2 or 3? Press the matching number")
# I realised that previously if the user entered an invalid class they would have to start the quiz again from the beginning which is quite
    else:
        filename = (class_number + "txt")

        with open(filename, 'a') as f:
            f.write("\n" + str(name) + " scored " + str(score) +  " on difficulty level " + str(level_of_difficulty))

        with open(filename, 'a') as f:
            f = open(filename, "r")
            lines = [line for line in f if line.strip()]
            f.close()
            lines.sort()

        if get_bool_input("Do you wish to view previous results for your class"):
            for line in lines:
                print (line)
        else:
            sys.exit("Thanks for taking part in the quiz, your teacher should discuss your score with you later")

if status == "2":
    class_number = input("Which classes scores would you like to see? Press 1 for class 1, 2 for class 2 or 3 for class 3")
    if class_number not in (1,2,3):
        sys.exit("That is not a valid class")
    filename = (class_number + "txt")
    with open(filename, 'a') as f:
        f = open(filename, "r")
        lines = [line for line in f if line.strip()]
        f.close()
        lines.sort()
        for line in lines:
            print (line)
\$\endgroup\$
4
\$\begingroup\$

Helper functions to prompt

Your get_bool_input is not very friendly: it exits on invalid input. It would be better to let the user retry. For that, simply replace the sys.exit with print.

So it seems you need a lot of prompting, often for integer. So add another helper method:

def prompt_int(prompt=''):
    while True:
        val = input(prompt)
        try:
            return int(val)
        except:
            print("Not a valid number, please try again")

Or if you want to limit input to specific choices you can do something like:

def prompt_choice(prompt, choices):
    while True:
        val = input(prompt)
        if val in choices:
            return val
        else:
            print("Not a valid choice, please try again")

Avoid duplicated code

You have some duplicated logic, in many places, for example:

if level_of_difficulty == 1:
    number_1 = random.randrange(1, 10)
    number_2 = random.randrange(1, 10)
else:
    number_1 = random.randrange(1, 20)
    number_2 = random.randrange(1, 20)

When you see repeated code like that, separate in your mind the parts that are the same and the parts that are different, and try to rewrite in a way that the same part is only written once, and replace the changing part with a variable. Like this:

if level_of_difficulty == 1:
    max_number = 10
else:
    max_number = 20
number_1 = random.randrange(1, max_number)
number_2 = random.randrange(1, max_number)

Apply this technique everywhere in your code.

Working with files

What's going on here:

    with open(filename, 'a') as f:
        f = open(filename, "r")
        lines = [line for line in f if line.strip()]
        f.close()
        lines.sort()

Opening a file to append, then inside the with block open the same file for reading? That doesn't make any sense. With the nonsense removed:

with open(filename) as f:
    lines = [line for line in f if line.strip()]
    lines.sort()

Poor naming

Many variable names don't describe their purpose well. For example "status" is hardly a good name for teacher-student choice. Perhaps "role" would be better.

\$\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.