4
\$\begingroup\$

Just wondering if there's anything I can improve on, I just started with Python recently and this is one of my first programs.

# Defining Score variables 
x = 0
score = x

# Question One 
print("What is 1 + 1")
answer_1 = input("a)1\nb)2\nc)3\nd)4\n:")
if answer_1.lower() == "b" or answer_1.lower() == "2":
    print("Correct")
    x = x + 1   
else:
    print("Incorrect, 1 + 1 is 2")

# Question Two
print("Who is the 45th president of the United States?")
answer_2 = input("a)Barack Obama\nb)Hillary Clinton\nc)Donald      Trump\nd)Tom Brady\n:")
if answer_2.lower() == "c" or answer_2.lower() == "donald trump":
    print("Correct")
    x = x + 1
else:
    print("Incorrect, The 45th president is Donald Trump")

# Question Three
print("True or False... The Toronto Maple Leafs have won 13 Stanley   Cups?")
answer_3 = input(":")
if answer_3.lower() == "true" or answer_3.lower() == "t":
    print("Correct")
    x = x + 1
else:
    print("Incorrect")  

# Question Four
print("What was the last year the Toronto Maple Leafs won the Stanley   Cup?")
answer_4 = input("a)1967\nb)1955\nc)1987\nd)1994\n:")
if answer_4.lower() == "a" or answer_4 == "1967":
    print("Correct")
    x = x + 1
else:
    print("Incorrect, The last time the Toronto Maple Leafs won the Stanley Cup was 1967")

# Question Five 
print("True or False... The current Prime Minister of Canada is Pierre Elliot Tredeau?")
answer_5 = input(":")
if answer_5.lower() == "false" or answer_5.lower() == "f":
    print("Correct")
    x = x + 1
else:
    print("Incorrect, The current Prime Minster of Canada is Justin Tredeau")


#Total Score
score = float(x / 5) * 100
print(x,"out of 5, that is",score, "%")
\$\endgroup\$
  • 1
    \$\begingroup\$ Question 2: If incorrect, the first word should just be "WROOOOONG!" \$\endgroup\$ – Canadian Luke Jan 24 '17 at 18:17
4
\$\begingroup\$

1. DRY (Don't Repeat Yourself)

You should define containers for your questions and answers. This way you can avoid having to repeat the whole logic for every question.

As a first step, I would advise to put all your question texts, answer texts, choice texts and correct answer texts in lists.

Then you can write a function that iterates over these lists (at the same time) and does the actual game. For this you can use zip.

questions = ["What is 1 + 1",
             "Who is the 45th president of the United States?",
             "True or False... The Toronto Maple Leafs have won 13 Stanley   Cups?",
             "What was the last year the Toronto Maple Leafs won the Stanley   Cup?",
             "True or False... The current Prime Minister of Canada is Pierre Elliot Tredeau?"]
answer_choices = ["a)1\nb)2\nc)3\nd)4\n:",
                  "a)Barack Obama\nb)Hillary Clinton\nc)Donald Trump\nd)Tom Brady\n:",
                  ":",
                  "a)1967\nb)1955\nc)1987\nd)1994\n:",
                  ":"]
correct_choices = [{"b", "2"},
                   {"c", "donald trump"},
                   {"true", "t"},
                   {"a", "1967"},
                   {"false", "f"}]
answers = ["1 + 1 is 2",
           "The 45th president is Donald Trump",
           "",
           "The last time the Toronto Maple Leafs won the Stanley Cup was 1967",
           "The current Prime Minster of Canada is Justin Tredeau"]


def quiz():
    score = 0
    for question, choices, correct_choice, answer in zip(questions, answer_choices, correct_choices, answers):
        print(question)
        user_answer = input(choices).lower()
        if user_answer in correct_choice:
            print("Correct")
            score += 1
        else:
            print("Incorrect", answer)
    print(score, "out of", len(questions), "that is", float(score / len(questions)) * 100, "%")

if __name__ == "__main__":
    quiz()

2. OOP (Object-Oriented Programming)

A second step could be trying to encapsulate this whole object of a question into an actual Python object. That is, a question is an object that has a question(text), an answer, answer choices and correct answer choices.

You could define a class like this:

class Question:
    def __init__(self, question, answer, choices, correct):
        self.question = question
        self.answer = answer
        self.choices = choices
        self.correct = correct

But this class would be nothing but a data storage for these strings. Whenever you have this, you can use a collections.namedtuple.

>>> from collections import namedtuple
>>> Question = namedtuple("Question", "question answer choices correct")
>>> q = Question("What is 1 + 1", "1 + 1 is 2", "a)1\nb)2\nc)3\nd)4\n:", {"b", "2"})
>>> q.question
'What is 1 + 1'
>>> q[3]
{'b', '2'}

So a Question is just a tuple with nice names, so that afterwards q.question = q[0] = 'What is 1 + 1', and so on.

from collections import namedtuple
Question = namedtuple("Question", "question answer choices correct")


questions = [Question("What is 1 + 1", "1 + 1 is 2", "a)1\nb)2\nc)3\nd)4\n:", {"b", "2"}),
             ...]


def quiz():
    score = 0
    for question in questions:
        print(question.question)
        user_answer = input(question.choices).lower()
        if user_answer in question.correct:
            print("Correct")
            score += 1
        else:
            print("Incorrect", question.answer)
    print(score, "out of", len(questions), "that is",
          float(score / len(questions)) * 100, "%")

if __name__ == "__main__":
    quiz()

3. The last push

Last, but not least, you could make enumerating the choices automatic, using string.ascii_lowercase (which is simply the lowercase alphabet) and str.joining it with newlines and the ")" used to separate a letter from the choice associated with that letter.

You could also pass a list of Questions directly to the function, allowing making different quizzes (by choosing randomly 5 questions out of a pool of 100 question, for example).

Finally, you could use str.format to make the print of the final score a bit nicer.

import string
from collections import namedtuple
Question = namedtuple("Question", "question answer choices correct")


questions = [Question("What is 1 + 1", "1 + 1 is 2",
                      ["1", "2", "3", "4"], {"b", "2"}), ...]


def quiz(questions):
    score = 0
    for question in questions:
        print(question.question)
        for line in zip(string.ascii_lowercase, question.choices):
            print(") ".join(line))
        user_answer = input().lower()
        if user_answer in question.correct:
            print("Correct")
            score += 1
        else:
            print("Incorrect", question.answer)
    print("{} out of {} that is {} %".format(score, len(questions), score / len(questions) * 100))

if __name__ == "__main__":
    # Use all questions but the first
    quiz(questions[1:])
\$\endgroup\$
  • 1
    \$\begingroup\$ I don't agree with str.join, and would use format/f-strings more. But nice answer none the less, :) \$\endgroup\$ – Peilonrayz Jan 24 '17 at 16:31
  • \$\begingroup\$ haha! Thanks for the help.. Most of what your saying actually does make a little sense as I'm starting to dive into dictionaries and lists now but it's also a bit out of my ball park. Definitely something that I can look back on and understand a little bit down the road. I'll have to start reading up on OOP! \$\endgroup\$ – CantGoBackToTO Jan 24 '17 at 16:34
  • \$\begingroup\$ @StephenPrete To get more comfortable with this, try l1 = [1,2,3,4]; l2 = ["a", "b", "c", "d"]; list(zip(l1, l2)) in an interactive interpreter to see what it does. Then try it with l2 = ["a", "b", "c"] to learn about zip stopping at the end of the smaller iterable. \$\endgroup\$ – Graipher Jan 24 '17 at 16:42

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.