4
\$\begingroup\$

I am building this program in Python to generate 10 random arithmetic questions of either multiplication, addition or subtraction and then saves users scores to a .txt file. I just wanted some help condensing the code as I'm really stuck as of what to do with it.

How can I combine the function (e.g. multiplication, addition and subtraction) into one?

import random
import time
import sys

ans = 0   #variable to hold question answer
question = 0   #question number
user_score = 0  #user's score
userInput = int()   #where user enters the answer
lastName = str()   #holds last name
firstName = str()   #holds first name
form = str()    #holds user's form

def function(score,name):   #writes user's information to a .txt file
    sumOfStudent = (name + ' scored ' + str(user_score))
    classNameTxt = (className, '.txt.')
    f = open(className, 'a')
    f.write(sumOfStudent + form + '\n')
    f.close()

def multiplication():   #creates a multiplication question
    global ans
    numberOne, numberTwo  = random.randint(0,20), random.randint(0,20)
    print("What is" , numberOne , "*" , numberTwo)
    ans = (numberOne * numberTwo)

def subtraction():   #creates a subtraction question
    global ans
    numberOne, numberTwo  = random.randint(0,20), random.randint(0,20)
    print("What is" , numberOne , "-" , numberTwo)
    ans = (numberOne - numberTwo)

def addition():   #creates a addition question
    global ans
    numberOne, numberTwo  = random.randint(0,20), random.randint(0,20)
    print("What is" , numberOne , "+" , numberTwo)
    ans = (numberOne + numberTwo)

operation = [multiplication,subtraction,addition]   #holds all of the opperators
randOperation = random.choice(operation)    #chooses a random operator

lastName = input("Please enter your surname: ").title()
firstName = input("Please enter your first name: ").title()
className = input("Please enter your form: ").title()
print()

def main():   #main game loop - ask questions and checks it against answer, stops are a give amount of questions
    question = 0
    user_score = 0
    randOperation = random.choice(operation)

    while True:
        try:
            randOperation()
            randOperation = random.choice(operation)
            if question >= 10:
                break
            userInput = int(input("Enter the answer: "))
            if userInput == ans:
                print("Correct!" + "\n")
                user_score += 1
                question += 1
            else:
                print("Incorrect!" + "\n")
                question += 1
        except ValueError:
            print("I'm sorry that's invalid")
            question += 1

main()    #initializes the function

print(firstName, lastName , "you scored" , user_score , "out of 10")   #shows the user's score and name

user_name = firstName + ' ' + lastName
function(user_score,user_name)

def endMenu():
    while True:
        try:  
            options = int(input('''Press '1' to view users' scores,
            press '2' to restart the test,
            press '3' to exit the game,

            Enter option here: '''))
        except ValueError:
            print("I'm sorry that was invalid...")

        if options == 3:  #exits the game...
            sys.exit()

        elif options == 2:   #starts the game loop again because it's in a function
            main()

        elif options == 1:   #displays everything on the .txt file
            f = open('userScore.txt', 'r')
            print(f.read())
            print()
            endMenu()

        else:
            print("Sorry, I don't understand. Please try again...")
            print()
            endMenu()        

endMenu()
\$\endgroup\$
4
\$\begingroup\$

Besides the obvious code repetition that you mentioned, a few salient issues deserve mentioning:

  • Your main() function is not really the main code of your program. Rather, you have some free-floating code outside of any function, and the main function is actually called endMenu(), which is surprising.
  • The endMenu() function is improperly recursive. The while True loop should suffice.
  • You use global variables a lot. While firstName and lastName are somewhat excusable (they are entered once by the user, and never change subsequently), you really shouldn't use globals for transient state like ans, question, etc.
  • function is the least-informative name possible for a function.

To address those issues, as well as the code repetition you mentioned, I'd write the program like this:

import operator
import random

OPERATIONS = [
    ('+', operator.add),
    ('-', operator.sub),
    ('*', operator.mul),
]

def random_question(binary_operations, operand_range):
    """Generate a pair consisting of a random question (as a string)
    and its answer (as a number)"""
    op_sym, op_func = random.choice(binary_operations)
    n1 = random.randint(min(operand_range), max(operand_range))
    n2 = random.randint(min(operand_range), max(operand_range))
    question = '{} {} {}'.format(n1, op_sym, n2)
    answer = op_func(n1, n2)
    return question, answer

def quiz(number_of_questions):
    """Ask the specified number of questions, and return the number of correct
    answers."""
    score = 0
    for _ in range(number_of_questions):
        question, answer = random_question(OPERATIONS, range(0, 21))
        print('What is {}'.format(question))
        try:
            user_input = float(input("Enter the answer: "))
        except ValueError:
            print("I'm sorry that's invalid")
        else:
            if answer == user_input:
                print("Correct!\n")
                score += 1
            else:
                print("Incorrect!\n")
    return score

def identify_user():
    # TODO, as an exercise for you
    pass

def display_score(first_name, last_name, class_name):
    # TODO, as an exercise for you
    pass

def menu():
    # TODO, as an exercise for you
    pass

def main():
    first_name, last_name, class_name = identify_user()
    while True:
        menu_choice = menu()
        if menu_choice == 1:        # Display score
            display_score(first_name, last_name, class_name)

        elif menu_choice == 2:      # Run quiz
            QUESTIONS = 10
            score = quiz(QUESTIONS)
            print('{first_name} {last_name}, you scored {score} out of {QUESTIONS}'.format(**locals()))

        elif menu_choice == 3:      # Exit
            break

        else:
            print("Sorry, I don't understand. Please try again...")
            print()

if __name__ == '__main__':
    main()

Observe how there are no global variables: each function accepts parameters and returns specific output values.

\$\endgroup\$
2
\$\begingroup\$

Don't initialize variables like this:

userInput = int()
lastName = str()
firstName = str()
form = str()

This is equivalent and better:

userInput = 0
lastName = ''
firstName = ''
form = ''

When opening files, use with:

with open('userScore.txt', 'r') as f:
    print(f.read())
print()
endMenu()

This deals with safe closing of the file.

\$\endgroup\$

protected by Community Jan 19 '16 at 11:45

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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