8
\$\begingroup\$

I made this quiz but the code seems to be really lengthy. Can anyone find a way to make my code shorter? Considerably shorter would be best but anything helps. I think the main problem is the repeating of the questions.

choice=raw_input("What type of quiz do you want to do: maths or science? ") 
topic=open("topic.txt", "a+") 
topic.write(choice + '\n') 
topic.close() 
difficulty=raw_input("What difficulty do you want to play on: easy, medium or hard? ") 
diff=open("difficulty.txt", "a+") 
diff.write(difficulty + '\n') 
diff.close()

score = 0


def str_answercheck(score, user_answer, str_answer):
    if user_answer.upper()==str_answer:
        print("Well done")
        score+=1
    else:
        print ("Wrong, it was "+str_answer)
    raw_input("Press enter to continue")
    return score

def int_answercheck(score, user_answer, int_answer):
    if int(user_answer.upper())==int_answer:
        print("Well done")
        score+=1
    else:
        print ("Wrong, it was "+str(int_answer))
    raw_input("Press enter to continue")
    return score

if choice.lower() == "maths" and difficulty.lower() == "easy": 
    easym=open("mathseasy.txt" , "r") 
    lines = easym.readlines() 
    print lines[0]
    print lines[1]
    print("A. 4"+'\n'+"B. 6") 
    str_answer_one="A"
    int_answer_one=4
    user_answer_one=raw_input("Select your answer. ")
    if user_answer_one.isdigit()==True:
        first_score=int_answercheck(score, user_answer_one, int_answer_one)
    else:
        first_score=str_answercheck(score, user_answer_one, str_answer_one)

    print lines[2]
    print("A. 5"+'\n'+"B. 6") 
    str_answer_two="A"
    int_answer_two=5
    user_answer_two=raw_input("Select your answer. ")
    if user_answer_two.isdigit()==True:
        second_score=int_answercheck(first_score, user_answer_two, int_answer_two)
    else:
        second_score=str_answercheck(first_score, user_answer_two, str_answer_two)

    print lines[3]
    print("A. 15"+'\n'+"B. 20") 
    str_answer_three="B"
    int_answer_three=20
    user_answer_three=raw_input("Select your answer. ")
    if user_answer_three.isdigit()==True:
        third_score=int_answercheck(second_score, user_answer_three, int_answer_three)
    else:
        third_score=str_answercheck(second_score, user_answer_three, str_answer_three)

    print lines[4]
    print("A. 13"+'\n'+"B. 15") 
    str_answer_four="A"
    int_answer_four=13
    user_answer_four=raw_input("Select your answer. ")
    if user_answer_four.isdigit()==True:
        fourth_score=int_answercheck(third_score, user_answer_four, int_answer_four)
    else:
        fourth_score=str_answercheck(third_score, user_answer_four, str_answer_four)

    print lines[5]
    print("A. 100"+'\n'+"B. 110") 
    str_answer_five="B"
    int_answer_five=110
    user_answer_five=raw_input("Select your answer. ")
    if user_answer_five.isdigit()==True:
        fifth_score=int_answercheck(fourth_score, user_answer_five, int_answer_five)
    else:
        fifth_score=str_answercheck(fourth_score, user_answer_five, str_answer_five)




if choice.lower() == "maths" and difficulty.lower() == "medium": 
    mediumm=open("mathsmedium.txt" , "r") 
    lines = mediumm.readlines() 
    print lines[0]
    print lines[1]
    print("A. 30"+'\n'+"B. 35") 
    str_answer_one="A"
    int_answer_one=30
    user_answer_one=raw_input("Select your answer. ")
    if user_answer_one.isdigit()==True:
        first_score=int_answercheck(score, user_answer_one, int_answer_one)
    else:
        first_score=str_answercheck(score, user_answer_one, str_answer_one)

    print lines[2]
    print("A. 100"+'\n'+"B. 110") 
    str_answer_two="B"
    int_answer_two=110
    user_answer_two=raw_input("Select your answer. ")
    if user_answer_two.isdigit()==True:
        second_score=int_answercheck(first_score, user_answer_two, int_answer_two)
    else:
        second_score=str_answercheck(first_score, user_answer_two, str_answer_two)

    print lines[3]
    print("A. 13"+'\n'+"B. 15") 
    str_answer_three="A"
    int_answer_three=13
    user_answer_three=raw_input("Select your answer. ")
    if user_answer_three.isdigit()==True:
        third_score=int_answercheck(second_score, user_answer_three, int_answer_three)
    else:
        third_score=str_answercheck(second_score, user_answer_three, str_answer_three)

    print lines[4]
    print("A. 30"+'\n'+"B. 32") 
    str_answer_four="B"
    int_answer_four=32
    user_answer_four=raw_input("Select your answer. ")
    if user_answer_four.isdigit()==True:
        fourth_score=int_answercheck(third_score, user_answer_four, int_answer_four)
    else:
        fourth_score=str_answercheck(third_score, user_answer_four, str_answer_four)

    print lines[5]
    print("A. 21"+'\n'+"B. 29") 
    str_answer_five="B"
    int_answer_five=29
    user_answer_five=raw_input("Select your answer. ")
    if user_answer_five.isdigit()==True:
        fifth_score=int_answercheck(fourth_score, user_answer_five, int_answer_five)
    else:
        fifth_score=str_answercheck(fourth_score, user_answer_five, str_answer_five)

Here is the data in mathseasy.txt:

Welcome to the easy maths quiz.
What's 2+2?
What's 11-6?
What's 5*4?
What's 26/2?
What's 11*10?

Here is the data in mathsmedium.txt:

Welcome to the medium maths quiz.
What's 5*6?
What's 79+31?
What's 26/2?
What's 4*8?
What's 50-21
\$\endgroup\$
  • \$\begingroup\$ A possibly more useful way of moving score around is to define it as a global variable because then you don't have to return it from your answer checkers or send it to them which will make your code more readable \$\endgroup\$ – 13ros27 Dec 15 '17 at 21:25
  • \$\begingroup\$ It might make it easier to use if you stored the answers in the same text file as the questions - say alternating lines \$\endgroup\$ – 13ros27 Dec 16 '17 at 20:52
7
\$\begingroup\$

Here are some ways to shorten your code:

  1. Your str_ and int_answercheck are basically the same function, except for one boolean expression. Instead of passing in the values and comparing them, why not write an underlying function that takes a boolean value and does the work:

    def check_answer(was_correct):
        if was_correct:
            print("Well done")
            score+=1
        else:
            print ("Wrong, it was "+str_answer)
        raw_input("Press enter to continue")
        return score
    
    def str_answercheck(user_answer, str_answer):
        return check_answer(user_answer.upper() == str_answer.upper())
    
    def int_answercheck(user_answer, int_answer):
        return check_answer(int(user_answer) == int_answer)
    
  2. You wrote two answercheck functions, but you always use an if statement to determine which function to call. Put that logic into a single answercheck function:

    def answercheck(user_answer, str_answer, int_answer):
        if user_answer.isdigit():
            return int_answercheck(user_answer, int_answer)
        else:
            return str_answercheck(user_answer, str_answer)
    
  3. Instead of updating a global score, perform the addition if the answer check returns true. This doesn't make your code shorter, but it's good coding style (avoid global variables when not required):

    if answercheck(user_answer, str_answer, int_answer):
        score += 1
    
  4. You're printing lines from the file, then printing allowable answers from the program source code. This means that the test file and the source code are in sync - they're not independent of each other. Since the source code is constant, why not put the possible answers in the test file:

    Welcome to the easy maths quiz.
    What's 2+2?
    A. 4
    B. 6
    What's 11-6?
    A. 5
    B. 6
    

    If you make these changes, you can convert this code:

    print lines[0]
    print lines[1]
    print("A. 30"+'\n'+"B. 35") 
    str_answer_one="A"
    int_answer_one=30
    user_answer_one=raw_input("Select your answer. ")
    if user_answer_one.isdigit()==True:
        first_score=int_answercheck(score, user_answer_one, int_answer_one)
    else:
        first_score=str_answercheck(score, user_answer_one, str_answer_one)
    

    Into code like this:

    header = lines[0]
    
    print header
    
    print lines[1]    # question
    print lines[2]    # answer 1
    print lines[3]    # answer 2
    
    user_answer = raw_input("Select your answer: ")
    
    str_answer = "A"
    int_answer = 30
    
    if answercheck(user_answer, str_answer, int_answer):
        score += 1
    
  5. If only you had a way of reading the correct answers from the input file! Oh, wait! You do:

    Welcome to the easy maths quiz.
    What's 2+2?
    A. 4
    B. 6
    Answer: A. 4
    What's 11-6?
    A. 5
    B. 6
    Answer: A. 5
    

    By providing the answers in the file, you can make your code more general. (Note: There is an obvious looping solution. However, I don't see any loops in your code, so I'm assuming you haven't reached that stage in your learning.)

    def question(num, lines):
    
        i = num * 4 + 1
    
        print lines[i]     # question
        print lines[i + 1] # answer 1
        print lines[i + 2] # answer 2
    
        user_answer = raw_input("Select your answer: ")
        answers = lines[i + 3].split()  # "answer:", "a.", "5"
        str_answer, int_answer = answers[1][0], int(answers[2])
    
        return answercheck(user_answer, str_answer, int_answer):
    

    This will let you ask each question and return true/false if the answer is correct:

    score = 0
    if question(0, lines):
        score += 1
    if question(1, lines):
        score += 1
    if question(2, lines):
        score += 1
    

    (The looping version should be obvious, when you get there.)

  6. You are handling the various degrees of difficulty as separate blocks of code. That was necessary since your code had the answers built in. But with the answers encoded in the questionnaire files, you no longer have to do that. Instead, just open the file and handle the questions in one place:

    questions = subject.lower() + difficulty.lower() + ".txt"
    
    with open(questions) as infile:
        lines = infile.readlines()
    
    print lines[0]
    
    score = 0
    if question(0, lines):
        score += 1
    if question(1, lines):
        score += 1
    if question(2, lines):
        score += 1
    
\$\endgroup\$
  • \$\begingroup\$ thanks a lot, but i'm kinda confused. can you perhaps take a small section of my code and change it into the ways you told me to do it. i would really appreciate it! if you can't do it, please let me know so i can stop waiting for a response. thanks \$\endgroup\$ – Ibibat Dec 17 '17 at 19:01
2
\$\begingroup\$

A shorter way to do the answer checking is to do them both together:

def answercheck(score, user_answer, str_answer, int_answer):
    if user_answer.upper() == str_answer or user_answer.upper() == int_answer:
        print("Well done")
        score+=1
    else:
        print ("Wrong, it was "+str(answer))
    raw_input("Press enter to continue")
    return score

NOTE: This will mean the rest of the code will have to be edited to just always send to answercheck

\$\endgroup\$
  • \$\begingroup\$ I tried doing this, but I'm really confused as to what I have to pass into the function when i refer back to it in each question, since i'm allowing A or 4 to be the correct answer \$\endgroup\$ – Ibibat Dec 16 '17 at 17:13
  • \$\begingroup\$ Ah, my mistake, I will edit to allow both \$\endgroup\$ – 13ros27 Dec 16 '17 at 17:17
  • \$\begingroup\$ thank you! any other ways to make it shorter? \$\endgroup\$ – Ibibat Dec 16 '17 at 19:10

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.