Trying to Make a Quiz - python

So I am trying to run a program on python3 that asks basic addition questions using random numbers. I have got the program running however, I wanted to know if there was a way I could count the occurrences of "Correct" and "Wrong" so I can give the person taking the quiz some feedback on how they did.
import random
num_ques=int(input('Enter Number of Questions:'))
while(num_ques < 1):
num_ques=int(input('Enter Positive Number of Questions:'))
for i in range(0, (num_ques)):
a=random.randint(1,100)
b=random.randint(1,100)
answer=int(input(str(a) + '+' + str(b) + '='))
sum=a+b
if (answer==sum):
print ('Correct')
else:
print ('Wrong.')

You could use a dict to store counts for each type, increment them when you come across each type, and then access it after to print the counts out.
Something like this:
import random
stats = {'correct': 0, 'wrong': 0}
num_ques=int(input('Enter Number of Questions:'))
while(num_ques < 1):
num_ques=int(input('Enter Positive Number of Questions:'))
for i in range(0, (num_ques)):
a=random.randint(1,100)
b=random.randint(1,100)
answer=int(input(str(a) + '+' + str(b) + '='))
sum=a+b
if (answer==sum):
print ('Correct')
stats['correct'] += 1
else:
print ('Wrong.')
stats['wrong'] += 1
print "results: {0} correct, {1} wrong".format(stats['correct'], stats['wrong'])

import operator
import random
def ask_float(prompt):
while True:
try: return float(input(prompt))
except: print("Invalid Input please enter a number")
def ask_question(*args):
a = random.randint(1,100)
b = random.randint(1,100)
op = random.choice("+-/*")
answ = {"+":operator.add,"-":operator.sub,"*":operator.mul,"/":operator.div}[op](a,b)
return abs(answ - ask_float("%s %s %s = ?"%(a,op,b)))<0.01
num_questions = 5
answers_correct = sum(map(ask_question,range(num_questions)))
print("You got %d/%d Correct!"%(answers_correct,num_questions))

Related

Python - looking for a way to make this more efficient

I'm relatively new to python and I'm looking for a way to make this code more efficient. Thank you!
import random
import string
import time
ques = []
anss = []
nums = string.digits
def que():
for i in range(10):
num0 = random.choice(nums)
num1 = random.choice(nums)
ans = num0 + '+' + num1
ques.append(ans)
ans = int(num0) + int(num1)
anss.append(ans)
global length
length = len(ques)
def main():
global counter
counter = 0
for i in range(10):
global answer
answer = int(input('What is ' + ques[i] + '?\n>>'))
if answer == anss[i]:
print('Correct!')
counter += 1
else:
print('Wrong!')
def score():
x = str((counter / length*100))
x = 'You got ' + x + '%'
return x
if __name__ == '__main__':
que()
main()
time.sleep(0.5)
print('Please wait while we calculate your score.')
time.sleep(1)
print(score())
Before, I tried just having the answers and questions in a single list - but after one loop it'd add one to the index something like this.
for i in range(10):
if answer == questions[i+1]:
print('correct')
i += 1 #This is meant to skip the answer part of the list and goto the next question but i couldnt get it to work.
Well... I just change your score method like
def score():
return 'You got ' + str((counter / length*100)) + '%'
In order to return the full string and avoid the x variable.
It is a little longer but what I think cleaner.
You can test each part without main.
main contains the algorithm.
from random import choice
from string import digits
def main() -> None:
score = 0
questions_limit = 10
for i in range(questions_limit):
first_operand = get_random_operand()
second_operand = get_random_operand()
expected_answer = first_operand + second_operand
question = get_question(first_operand, second_operand)
actual_answer = get_actual_answer(question)
correct = is_correct(expected_answer, actual_answer)
print_result(correct)
score = get_score(score, correct)
score_percentage = get_score_percentage(score, questions_limit)
print_score(score_percentage)
def get_random_operand() -> int:
return int(choice(digits))
def get_question(first_operand: int, second_operand: int) -> str:
return 'What is ' + str(first_operand) + ' + ' + str(second_operand) + ' ?\n>>'
def get_actual_answer(question: str) -> int:
return int(input(question))
def is_correct(expected_answer: int, actual_answer: int) -> bool:
return expected_answer == actual_answer
def print_result(correct: bool) -> None:
message = "Correct!" if correct else "Wrong!"
print(message)
def get_score(score: int, correct: bool) -> int:
if correct:
score +=1
return score
def get_score_percentage(score, questions_limit) -> str:
return str(score / questions_limit * 100)
def print_score(score: str) -> None:
print('You got ' + score + '%')
if __name__ == '__main__':
main()
If you were wanting to have the questions and answers in the same array then I would suggest doing something like:
for i in range(0,10,2):
if answer == questions[i+1]:
print("correct")
However the length of the array should be made to match having a question for each answer. The 2 at the end of range will mean the iterator(i) goes up by 2 each loop
It does not directly answer your question, but I'd suggest that you use the global keyword much less:
For answer you don't need it at all, because you use answer nowhere outside of main.
For length I would instead suggest you define length = 10 below nums at the top of your code and then replace all occurrences of 10 by length (which I would rather call n_questions).
That way you can easily change the number of questions with one single change of your code, and as you know that the loop in que will run up to length, there is no need to count length up.
Having counter global in main in okayish, although many would argue that it'd be better practice to return counter in main and define score to accept counter (better n_correct, for example) as argument like this (reusing Alfa Rojos answer):
def score(counter):
return 'You got ' + str((counter / length*100)) + '%'
I didn't completely rewrite it, but I would move everything to one loop, much cleaner for changes that way and have a counter that counts the number the user gets correct.
Here is a partial rewrite of your function que
def que():
for i in range(10):
num0,num1 = int(random.choice(nums)), int(random.choice(nums))
ans = num0 + num1
ques.append(ans)
anss.append(ans)
length = len(ques)
return length

Parsed value isn't displaying the correct value when displayed

I am trying to increase the correct answer count by 1 every time the users answer is correct. However, when parsed into the display_result() function, the correct function displays "0 correct"
I haven't been able to get this working no matter how I try to wiggle around it, so any help is really appreciated.
code removed for academic integrity
If the user has answered 1 of 3 questions correctly, I expect the answer to be "You have answer 1 questions out of 3 correctly."
Currently, it would display you have answered 0 questions out of 3 correctly"
In menu_option() you never modify count, so it stays at 0. Two simple fixes. Change to:
count = check_solution(user_solution, real_solution, count)
return count
Or just
return check_solution(user_solution, real_solution, count)
One other thing I noticed: in get_user_input() you need to return the result of the recursive calls:
else:
print("Invalid input, please try again")
return get_user_input()
There are a number of problems:
you are doing correct = menu_option(option, correct) when instead you should be accumulating the correct scores like correct +=
in the menu_option you are never assigning to count, I presume it should be count = check_solution(...)
you shouldn't do return option for index == 5 because that will add to the correct.
Finally the code run as I expected(python3.6+ is required):
#!/usr/bin/env python3
import random
def get_user_input():
while True:
try:
index = int(input("Enter your choice: "))
if 0 < index < 6:
return index
except ValueError:
print("Invalid input, should be Integer.\n")
else:
print("Invalid input, please try again")
def get_user_solution(problem):
while True:
print("Enter your answer")
user_solution = input(f"{problem} = ")
try:
return float(user_solution)
except ValueError:
print("Invalid input, should be float\n")
def check_solution(user_solution, solution, count):
if user_solution == solution:
print("Correct.")
return count + 1
else:
print("Incorrect.")
return count
def menu_option(index, count):
first_num = random.randrange(1, 21)
second_num = random.randrange(1, 21)
if index == 1:
problem = f"{first_num} + {second_num}"
real_solution = first_num + second_num
print(real_solution)
user_solution = get_user_solution(problem)
return check_solution(user_solution, real_solution, count)
if index == 2:
problem = f"{first_num} - {second_num}"
real_solution = first_num - second_num
print(real_solution)
user_solution = get_user_solution(problem)
return check_solution(user_solution, real_solution, count)
if index == 3:
# blah blah blah, repeated code but removed for neatness
pass
if index == 5:
option = 5
return option
def display_result(total, correct):
if total == 0:
print("You answered 0 questions with 0 correct")
print("Your score is 0.0%")
else:
percentage = round(correct / total * 100, 2)
print(
f'You answered {total} questions with {correct} correct.\n'
f'Your score is {percentage}%'
)
def display_intro():
pass
def display_menu():
pass
def display_separator():
print('-'*20)
def main():
display_intro()
display_menu()
display_separator()
option = get_user_input()
total = 0
correct = 0
while option != 5:
total = total + 1
correct = menu_option(option, correct)
option = get_user_input()
print("Exit the quiz.")
display_separator()
display_result(total, correct)
if __name__ == "__main__":
main()

Go back to specific number in a for-loop

I have the following code:
def five_numbers():
my_list = []
for i in range(1, 6):
user_nr = check_if_number_is_1_to_25(input("Number " + str(i) + ": "))
my_list.append(user_nr)
return my_list
def check_if_number_is_1_to_25(number):
if number.isalpha():
print("Enter a number between 1 and 25.")
# Here I want to go back to five_numbers() and the number x (for example number 4)
Now I want to check if the input contains any letters. If it has, I want to print a message and then I want to go back to the number that the user was on earlier. I've tried to return five_numbers() but then the user will start from the beginning.
I appreciate all the help.
Add a keyword arg for num and default it to None:
def five_numbers(num=None):
my_list = []
if num is None:
for i in range(1, 6):
user_nr = check_if_number_is_1_to_25(input("Number " + str(i) + ": "))
my_list.append(user_nr)
else:
# do other stuff with num (4) here...
return my_list
def check_if_number_is_1_to_25(number):
if number.isalpha():
print("Enter a number between 1 and 25.")
five_numbers(4)
You can use a while loop to keep asking the user for a valid input until the user enters one. You should also make the check function raise an exception instead so the caller can catch the exception and retry the input:
def five_numbers():
my_list = []
for i in range(1, 6):
while True:
user_nr = input("Number " + str(i) + ": ")
try:
check_if_number_is_1_to_25(user_nr)
break
except ValueError as e:
print(str(e))
my_list.append(user_nr)
return my_list
def check_if_number_is_1_to_25(number):
if number.isalpha():
raise ValueError('Enter a number between 1 and 25.')
Don't use a for loop, use a while loop with the list length as its condition. Make the check function return a boolean and use it to decide whether to append to the list.
def five_numbers():
my_list = []
while len(my_list) < 5:
user_nr = input("Number {}: ".format(len(my_list)+1))
if check_if_number_is_1_to_25(user_nr):
my_list.append(user_nr)
else:
print("Enter a number between 1 and 25.")
return my_list
def check_if_number_is_1_to_25(number):
return number.isdigit() and (1 <= float(number) <= 25)

Adding score + name in each cell in excel file

I'm trying to get this problem fixed. I'm creating a program which stores the score in a spreadsheet but when it does, it does not write the score and name in different cell. I have tried adding the column string/row string but always getting error, some guide and help will be appreciated.
So far this is what I have done:
!(http://postimg.org/image/6zn9l43bj/)!
I tried to get a heading saying name and the users name below in each cell and same with score and need some starting point/help
ClassA = open('classa.csv', 'w')
ClassB = open('classb.csv', 'w')
ClassC = open('classc.csv', 'w')
start = True
while start:
user =(input("What is your name?"))
form =(input("Which Group are you in A/B or C ")).lower()
import re
import random
from random import randint
score = 0
for i in range(3):
first_num = random.randint(1,10)
second_num = random.randint(1,10)
choice = (first_num+second_num)
if choice == first_num+second_num:
print ("Question (%d)" % (i+1))
print (first_num)
print("Add (+)")
print(second_num)
check = True
while check:
answer = (input('enter the answer: '))
if not (re.match('-?[0-9]\d*(\.\d+)?$', answer)):
print("Only input numbers")
else:
check = False
answer = int(answer)
if answer == (choice):
print("It's correct!")
score +=1
else:
print("It's wrong!")
print ("The correct answer is: ", choice)
print('')
print(user)
if form == 'a':
ClassA.write("Name: "+str(user) + ' ' + "Score: "+str(score)+"/10" + '\n')
elif form == 'b':
ClassB.write("Name: "+str(user) + ' ' + "Score: "+str(score)+"/10" + '\n')
elif form == 'c':
ClassC.write("Name: "+str(user) + ' ' + "Score: "+str(score)+"/10" + '\n')
yesorno = input("Restart?, Y or N ").lower()
if yesorno == 'y':
start = True
elif yesorno == 'n':
start = False
ClassA.close()
ClassB.close()
ClassC.close()
Thanks
A bit of background: CSV stands for Comma Separated Values, oddly enough Values are quite often separated by semicolons in CSV files and, in fact, (I think) Excel won't recognise columns if you use commas, but it will if you use semicolons. Edit: As pointed out in the comments, this probably depends on regional settings, if in your country the decimal point is usually a comma (e.g. most of Europe) use ;, if it's usually a point use , as separator.
So, back to your question: you are not separating your values, use this instead (notice the semicolon):
ClassA.write("Name: "+str(user) + '; ' + "Score: "+str(score)+"/10" + '\n')
I wouldn't recommend writing the Name: and Score: prefixes either, I would go with a header row (Name; Score; Max Score) and something like this:
ClassA.write("{0}; {1}; {2}\n".format(user, score, max_score))
See also: str.format

determinating if the input is even or odd numbers

Hello I am trying to write a program in python that asks the user to input a set of numbers of 1's and 0's and I want the program to tell me if I have and even number of zeros or an odd number of zeros or no zero's at all. Thanks for your help!!
forstate = "start"
curstate = "start"
trans = "none"
value = 0
print "Former state....:", forstate
print "Transition....:", trans
print "Current state....", curstate
while curstate != "You hav and even number of zeros":
trans = raw_input("Input a 1 or a 0: ")
if trans == "0" and value <2:
value = value + 1
forstate = curstate
elif trans == "1" and value < 2:
value = value + 0
forstate = curstate
curstate = str(value) + " zeros"
if value >= 2:
curstate = "You have and even number of zeros"
print "former state ...:", forstate
print "Transition .....:", trans
print "Current state....", curstate
Looks like you're trying to do a finite state machine?
try:
inp = raw_input
except NameError:
inp = input
def getInt(msg):
while True:
try:
return int(inp(msg))
except ValueError:
pass
START, ODD, EVEN = range(3)
state_next = [ODD, EVEN, ODD]
state_str = ['no zeros yet', 'an odd number of zeros', 'an even number of zeros']
state = START
while True:
num = getInt('Enter a number (-1 to exit)')
if num==-1:
break
elif num==0:
state = state_next[state]
print 'I have seen {0}.'.format(state_str[state])
Edit:
try:
inp = raw_input
except NameError:
inp = input
START, ODD, EVEN = range(3)
state_next = [ODD, EVEN, ODD]
state_str = ['no zeros yet', 'an odd number of zeros', 'an even number of zeros']
def reduce_fn(state, ch):
return state_next[state] if ch=='0' else state
state = reduce(reduce_fn, inp('Enter at own risk: '), START)
print "I have seen " + state_str[state]
It sounds like homework, or worse an interview questions, but this will get you started.
def homework(s):
counter = 0
if '0' in s:
for i in s:
if i == '0':
counter = counter + 1
return counter
don't forget this part over here
def odd_or_even_or_none(num):
if num == 0:
return 'This string contains no zeros'
if num % 2 == 0
return 'This string contains an even amount of zeros'
else:
return 'This string contains an odd amount of zeros'
if you call homework and give it a string of numbers it will give you back the number of 0
homework('101110101')
now that you know how many 0s you need to call odd_or_even_or_none with that number
odd_or_even_or_none(23)
so the solution looks like this
txt = input('Feed me numbers: ')
counter = str( homework(txt) )
print odd_or_even_or_none(counter)
try:
inp = raw_input
except NameError:
inp = input
zeros = sum(ch=='0' for ch in inp('Can I take your order? '))
if not zeros:
print "none"
elif zeros%2:
print "odd"
else:
print "even"
The simple solution to your problem is just to count the zeros, then print a suitable message. num_zeros = input_stream.count('0')
If you're going to build a finite state machine to learn how to write one, then you'll learn more writing a generic FSM and using it to solve your particular problem. Here's my attempt - note that all the logic for counting the zeros is encoded in the states and their transitions.
class FSMState(object):
def __init__(self, description):
self.transition = {}
self.description = description
def set_transitions(self, on_zero, on_one):
self.transition['0'] = on_zero
self.transition['1'] = on_one
def run_machine(state, input_stream):
"""Put the input_stream through the FSM given."""
for x in input_stream:
state = state.transition[x]
return state
# Create the states of the machine.
NO_ZEROS = FSMState('No zeros')
EVEN_ZEROS = FSMState('An even number of zeros')
ODD_ZEROS = FSMState('An odd number of zeros')
# Set up transitions for each state
NO_ZEROS.set_transitions(ODD_ZEROS, NO_ZEROS)
EVEN_ZEROS.set_transitions(ODD_ZEROS, EVEN_ZEROS)
ODD_ZEROS.set_transitions(EVEN_ZEROS, ODD_ZEROS)
result = run_machine(NO_ZEROS, '01011001010')
print result.description

Categories

Resources