How do I get a python quiz to randomize questions? - python

My task is to make a quiz using python, with questions being stored in an external file. However, I can not figure out how to get my questions to randomize and only display 10 at a time out of the 20 possible
I have tried using import random however the syntax random.shuffle(question) does not seem to be valid. I am not sure what to do now.
The question.txt file is laid out as:
Category
Question
Answer
Answer
Answer
Answer
Correct Answer
Explanation
My code:
#allows program to know how file should be read
def open_file(file_name, mode):
"""Open a file."""
try:
the_file = open(file_name, mode)
except IOError as e:
print("Unable to open the file", file_name, "Ending program.\n", e)
input("\n\nPress the enter key to exit.")
sys.exit()
else:
return the_file
def next_line(the_file):
"""Return next line from the trivia file, formatted."""
line = the_file.readline()
line = line.replace("/", "\n")
return line
#defines block of data
def next_block(the_file):
"""Return the next block of data from the trivia file."""
category = next_line(the_file)
question = next_line(the_file)
answers = []
for i in range(4):
answers.append(next_line(the_file))
correct = next_line(the_file)
if correct:
correct = correct[0]
explanation = next_line(the_file)
time.sleep(1.5)
#beginning of quiz questions
def main():
trivia_file = open_file("trivia.txt", "r")
title = next_line(trivia_file)
welcome(title)
score = 0
# get first block
category, question, answers, correct, explanation = next_block(trivia_file)
while category:
# ask a question
print(category)
print(question)
for i in range(4):
print("\t", i + 1, "-", answers[i])
# get answer
answer = input("What's your answer?: ")
# check answer
if answer == correct:
print("\nCorrect!", end=" ")
score += 1
else:
print("\nWrong.", end=" ")
print(explanation)
print("Score:", score, "\n\n")
# get next block
category, question, answers, correct, explanation = next_block(trivia_file)
trivia_file.close()
print("That was the last question!")
print("Your final score is", score)
main()
That is most of the code associated with the program. I will be very grateful for any support available.

You can read the file and group its contents into blocks of eight. To generate unique random questions, you can use random.shuffle and then list slicing to create the groups of questions. Also, it is cleaner to use a collections.namedtuple to form the attributes of the question for use later:
import random, collections
data = [i.strip('\n') for i in open('filename.txt')]
questions = [data[i:i+8] for i in range(0, len(data), 8)]
random.shuffle(questions)
question = collections.namedtuple('question', ['category', 'question', 'answers', 'correct', 'explanation'])
final_questions = [question(*i[:2], i[2:6], *i[6:]) for i in questions]
Now, to create the groups of 10:
group_questions = [final_questions[i:i+10] for i in range(0, len(final_questions), 10)]
The result will be a list of lists containing the namedtuple objects:
[[question(category='Category', question='Question', answers=['Answer', 'Answer', 'Answer', 'Answer'], correct='Correct Answer', explanation='Explanation ')]]
To get the desired values from each namedtuple, you can lookup the attributes:
category, question = question.category, question.question
Etc.

Related

Trying to load and edit a pickled dictionary, getting an EOFError

I'm trying to modify a trivia program found in a book as part of a tutorial; I need to save the name and score of the player using a pickled dictionary. I've already created the dat file using a separate program, to avoid reading from a file that doesn't exist.
This is the code for the trivia program.
#Trivia Challenge
#Trivia game that reads a plain text file
import sys
def open_file(file_name, mode):
"""Open a file"""
try:
the_file = open(file_name, mode)
except IOError as e:
print("Unable to open the file", file_name, "Ending program.\n", e)
input("\n\nPress the enter key to exit.")
sys.exit()
else:
return the_file
def next_line(the_file):
"""Return next line from the trivia file, formatted."""
line = the_file.readline()
line = line.replace("/", "\n")
return line
def next_block(the_file):
"""Return the next block of data from the triva file."""
category = next_line(the_file)
question = next_line(the_file)
answers = []
for i in range(4):
answers.append(next_line(the_file))
correct = next_line(the_file)
if correct:
correct = correct[0]
explanation = next_line(the_file)
value = next_line(the_file)
return category, question, answers, correct, explanation, value
def welcome(title):
"""Welcome the player and get his or her name."""
print("\t\tWelcome to Trivia Challenge!\n")
print("\t\t", title, "\n")
def saving(player_name):
import pickle
f = open("trivia_scores.dat", "rb+")
highscores = pickle.load(f)
if player_name in highscores and score > highscores[player_name]:
highscores[player_name] = score
pickle.dump(highscores, f)
elif player_name not in highscores:
highscores[player_name] = score
pickle.dump(highscores, f)
print("The current high scores are as follows:")
print(highscores)
f.close()
def main():
trivia_file = open_file("trivia.txt", "r")
title = next_line(trivia_file)
welcome(title)
score = 0
#Get the first block
category, question, answers, correct, explanation, value = next_block(trivia_file)
while category:
#Ask a question
print(category)
print(question)
for i in range(4):
print("\t", i + 1, "-", answers[i])
#Get answer
answer = input("What is your answer?: ")
#Check answer
if answer == correct:
print("\nRight!", end=" ")
score += int(value)
else:
print("\nWrong!", end=" ")
print(explanation)
print("Score:", score, "\n\n")
#Get the next block
category, question, answers, correct, explanation, value = next_block(trivia_file)
trivia_file.close()
print("That was the last question!")
print("Your final score is", score)
return score
player_name = input("First, enter your name: ")
main()
saving(player_name)
input("\n\nPress the enter key to exit.")
The eponymous error occurs at this point:
def saving(player_name):
import pickle
f = open("trivia_scores.dat", "rb+")
highscores = pickle.load(f)
When the questions end, the program attempts to run the "saving" module, which (In theory) opens the trivia_scores.dat file, loads the highscores dictionary, checks to see if the player's name is in the dictionary, and if their current score is higher than the one in the file, it overwrites it.
But for some reason, when the program attempts to load the highscores dictionary, instead I get this error message.
EOFError: Ran out of input
I have never seen this error before. From some cursory googling, I got the impression that it has something to do with the program trying to read from an empty file. But that made no sense to me, since I specifically created a dat file using a different program to prevent that from happening: trivia_scores.dat is NOT an empty file. I even read from it with Python Shell to make sure.
What does this error mean, and why won't Python load the dat file?
Context: The book I'm reading from is Python for the Absolute Beginner, by Michael Dawson. This program and the challenge I'm trying to complete come from chapter 7. The program was running fine before I added the saving module.
Probably the original trivia_scores.dat file you wrote got corrupt (maybe you didn't call close() on it?). You should try creating a new file and adding a pre-populated dictionary to this file. Then try reading from this new file.

How do I allow only the latest inputs to be saved - Python

How do I implement a simple code that will only save the student's latest 3 scores? If the test is repeated later, the old score should be replaced.
Thank you.
This is the code that asks the user the questions and saves the results in the txt. files.
import random
import math
import operator as op
correct_answers = 0
def test():
num1 = random.randint(1, 10)
num2 = random.randint(1, 10)
ops = {
'+': op.add,
'-': op.sub,
'*': op.mul,
}
keys = list(ops.keys())
rand_key = random.choice(keys)
operation = ops[rand_key]
correct_result = operation(num1, num2)
print ("What is {} {} {}?".format(num1, rand_key, num2))
user_answer= int(input("Your answer: "))
if user_answer != correct_result:
print ("Incorrect. The right answer is {}".format(correct_result))
return False
else:
print("Correct!")
return True
username = input("What is your name? ")
print("Hi {}! Welcome to the Arithmetic quiz...".format(username))
class_name = input("Are you in class 1, 2 or 3? ")
correct_answers = 0
num_questions = 10
for i in range(num_questions):
if test():
correct_answers +=1
print("{}: You got {}/{} questions correct.".format(
username,
correct_answers,
num_questions,
))
class_name = class_name + ".txt" #creates a txt file called the class that the user entered earlier on in the quiz.
file = open(class_name , 'a') #These few lines open and then write the username and the marks of the student into the txt file.
name = (username)
file.write(str(username + " : " ))
file.write(str(correct_answers))
file.write('\n') #This puts each different entry on a different line.
file.close() #This closes the file once the infrmation has been written.
A much better solution would be to store the data in a different format that made everything easy. For example, if you used a shelve database that mapped each username to a deque of answers, the whole thing would be this simple:
with shelve.open(class_name) as db:
answers = db.get(username, collections.deque(maxlen=3))
answers.append(correct_answers)
db[username] = answers
But if you can't change the data format, and you need to just append new lines to the end of a human-readable text file, then the only want to find out if there are already 3 answers is to read through every line in the file to see how many are already there. For example:
past_answers = []
with open(class_name) as f:
for i, line in enumerate(f):
# rsplit(…,1) instead of split so users who call
# themselves 'I Rock : 99999' can't cheat the system
name, answers = line.rsplit(' : ', 1)
if name == username:
past_answers.append(i)
And if there were 3 past answers, you have to rewrite the file, skipping line #i. This is the really fun part; text files aren't random-access-editable, so the best you can do is either read it all into memory and write it back out, or copy it all to a temporary file and move it over the original. Like this:
excess_answers = set(past_answers[:-2])
if excess_answers:
with open(class_name) as fin, tempfile.NamedTemporaryFile() as fout:
for i, line in enumerate(fin):
if i not in excess_answers:
fout.write(line)
os.replace(fout.name, fin)
That part is untested. And it requires Python 3.3+; if you have an earlier version and are on Mac or Linux you can just use os.rename instead of replace, but if you're on Windows… you need to do some research, because it's ugly and no fun.
And now, you can finally just append the new answer, as you're already doing.

File Writing Python, How to print variable to txt file

I am trying to print a variable of a score and name to a .txt file using python.
import random
import csv
import operator
import datetime
now = datetime.datetime.now() ## gets the exact time of when the user begins the test.
def main():
global myRecord
myRecord = []
name = getNames()
myRecord.append(name)
record = quiz()
def getNames(): ## this function grabs first and lastname of the user
firstName = input ("Please enter your first name") ## asks for users name
surName = input("Please enter your surname") ## asks for users suername
space = " "
fullName = firstName + space +surName ## puts data of name together to make full name
print("Hello")
print (fullName)
myRecord.append(fullName)
return fullName ## this is a variable returned to main
def quiz():
print('Welcome. This is a 10 question math quiz\n')
score = 0 ## sets score to 0.
for i in range(10): ## repeats question 10 times
correct = askQuestion()## if the statement above if correct the program asks a question.
if correct:
score += 1## adds one to the score
print('Correct!\n')## prints correct if the user gets a question correct.
else:
print('Incorrect!\n') ## prints incorrect if the user gets a question wrong.
return 'Your score was {}/10'.format(score)
def randomCalc():
ops = {'+':operator.add, ## selects one of the three operators
'-':operator.sub, ## selects one of the three operators
'*':operator.mul,} ## selects one of the three operators
num1 = random.randint(0,12) ## samples a number between 0 and 12
num2 = random.randint(1,10) ## zero are not used to stop diving by zero
op = random.choice(list(ops.keys()))
answer = ops.get(op)(num1,num2)
print('What is {} {} {}?\n'.format(num1, op, num2)) ## puts together the num1, the operator and num2 to form question
return answer
def askQuestion():
answer = randomCalc()
guess = float(input())
return guess == answer
def myfileWrite (myrecord):
with open('Namescore.txt', 'w') as score:
score.write(fullName + '\n')
main()
here is the full code it should ask the users name, print 10 maths questions and then save the time name and score to a txt file
if you can help please do
many thanks
Your indentation is incorrect and you never actually call the function:
with open('Namescore.txt', 'w') as score:
score.write(fullName + '\n')
the code you wrote would IIRC re-make the file each time you ran the code. I believe this is the correct way to do it:
with open("Namescore.txt", "a") as file:
file.write(name, score, "\n") # I don't know what your vars are called
This will append to the file rather than rewrite :)
If you want to do it your way, the correct way would be:
def writeScore(name, score):
file = open("Namescore.txt", "a")
file.write(name, score, "\n")
file.close()
writeScore("Example Examlpus", 201)

Get blocks of text with four lines each from a text file

I am trying to create a quiz. In a text file, I have blocks consisting of subject, question, answer and an empty space (on that order). Each line represents one of those items:
Histology What do megakaryocytes originate? Platelets.
Physiology Which physiological process does not occur in Glanzmann's
thrombasthenia? Platelet aggregation.
Histology When in the erythropoietic process does the cell lose its
nucleus? When in the ortochromatophilic stage.
Physiology Which phase of hemostasis features the action of
coagulation factors? Secondary hemostasis.
Physiology What characterizes hemarthrosis? Blood in joint spaces.
Physiology Beyond being in circulation, a portion of platelets is also
stored. Where? The spleen.
Physiology Which of the platelet zones includes the submembranous
region? Peripheral zone.
I have successfully coded a program that shows the user the question and then reveals the answer when the user says so. However, I wanted to display the questions randomly. What I used to display them sequentially was inspired by Michael Dawson's book "Python programming for the absolute beginner". I followed the structure the author showed closely and it works. The code is:
#File opening function. Receives a file name, a mode and returns the opened file.
def open_file(file_name, mode):
try:
file = open(file_name, mode)
except:
print("An error has ocurred. Please make sure that the file is in the correct location.")
input("Press enter to exit.")
sys.exit()
else:
return file
#Next line function. Receives a file and returns the read line.
def next_line(file):
line = file.readline()
line = line.replace("/", "\n")
return line
#Next block function. Receives a file and returns the next block (set of three lines comprising subject, question and answer.
def next_block(file):
subject = next_line(file)
question = next_line(file)
answer = next_line(file)
empty = next_line(file)
return subject, question, answer, empty
#Welcome function. Introduces the user into the quizz, explaining its mechanics.
def welcome():
print("""
Welcome to PITAA (Pain In The Ass Asker)!
PITAA will ask you random questions. You can then tell it to
reveal the correct answer. It does not evaluate your choice,
so you must see how many you got right by yourself.
""")
def main():
welcome()
file = open_file("quizz.txt", "r")
store = open_file("store.bat", "w")
subject, question, answer, empty = next_block(file)
while subject:
print("\n")
print("Subject: ", subject)
print("Question: ", question)
input("Press enter to reveal answer")
print("Answer: ", answer)
print("\n")
subject, question, answer, empty = next_block(file)
file.close()
print("\nQuizz over! Have a nice day!")
#Running the program
main()
input("Press the enter key to exit.")
How can I group blocks of 4 lines and then randomize them? It would be even better if I could filter them by subject and question.
import random
def open_file(file_name, mode):
try:
file = open(file_name, mode)
except:
print("An error has ocurred. Please make sure that the file is in the correct location.")
input("Press enter to exit.")
sys.exit()
else:
return file
def replace_linebreaks(value):
value.replace("/", "\n")
def main():
welcome()
# store = open_file("store.bat", "w")
file = open_file("quizz.txt", "r")
questions = file.read().split('\n\n') # if UNIX line endings
file.close()
random.shuffle(questions)
for question in questions.splitlines():
subject, question, answer, empty = map(replace_linebreaks, question)
print("\n")
print("Subject: ", subject)
print("Question: ", question)
input("Press enter to reveal answer")
print("Answer: ", answer)
print("\n")
subject, question, answer, empty = next_block(file)
print("\nQuizz over! Have a nice day!")
To organize I would make a simple class or use dicts. For example:
Class implementation
class Quiz():
def __init__(self, question, answer, subject):
self.question = question
self.answer = answer
self.subject = subject
You can make an instance of those questions and create a subject for each of them, accessing them based on their attribute. As such:
q = Quiz("Question 1", "Answer 1", "Chemistry")
print(q.subject)
>>> Chemistry
You can append the new instance to a list and just randomize the list as such
import random #Look up the python docs for this as there are several methods to use
new_list = []
new_list.append(q)
random.choice(new_list) #returns a random object in the list
You can also do this with nested dictionaries and drill down based on the 'subject'
new_dict = {'subject': {'question': 'this is the question',
'answer': 'This is the answer'}}
But I feel its easier to organize by creating your own class.
Hope that helps a little...

Why does reading a line in Python not give me just the contents of that line?

I'm having trouble understanding the correct variable in the next_block(the_file) function. The program won't work correctly if the correct variable is not indexed. Hence, correct[0]. So my question is why doesn't it work correctly if it's not indexed and why can it even be indexed if an integer is not subscriptable.
The text file it uses is this:
An Episode You Can't Refuse
On the Run With a Mammal
Let's say you turn state's evidence and need to "get on the lamb." /If you wait too long, what will happen?
You'll end up on the sheep
You'll end up on the cow
You'll end up on the goat
You'll end up on the emu
1
A lamb is just a young sheep.
The Godfather Will Get Down With You Now
Let's say you have an audience with the Godfather of Soul. /How would it be smart to address him?
Mr. Richard
Mr. Domino
Mr. Brown
Mr. Checker
3
James Brown is the Godfather of Soul.
And this is the code:
# Trivia Time
# Trivia game that reads a plain text file
def open_file(file_name, mode):
"""Open a file."""
try:
the_file = open(file_name, mode)
except(IOError), e:
print "Unable to open the file", file_name, "Ending program.\n", e
raw_input("\n\nPress enter to exit..")
sys.exit()
else:
return the_file
def next_line(the_file):
"""Return the next line from the trivia file, formatted."""
line = the_file.readline()
line = line.replace("/", "\n")
return line
def next_block(the_file):
"""Return the next block of data from the trivia file."""
category = next_line(the_file)
question = next_line(the_file)
answers = []
for j in range(4):
answers.append(next_line(the_file))
correct = next_line(the_file)
if correct:
correct = correct[0]
explanation = next_line(the_file)
return category, question, answers, correct, explanation
def welcome(title):
"""Welcome the player and get his/her name."""
print "Welcome to Trivia Challenge!\n"
print title, "\n"
def main():
trivia_file = open_file("trivia.txt", "r")
title = next_line(trivia_file)
welcome(title)
score = 0
# get first block
category, question, answers, correct, explanation = next_block(trivia_file)
while category:
# ask a question
print category
print question
for j in range(4):
print j + 1, "-", answers[j]
# get answer
answer = raw_input("What's your answer?: ")
# check answer
if answer == correct:
print "\nRight!",
score = score + 1
else:
print "\nWrong.",
print explanation
print "Score:", score, "\n\n"
# get next block
category, question, answers, correct, explanation = next_block(trivia_file)
trivia_file.close()
print "That was the last question!"
print "Your final score is:", score
main()
raw_input("\n\nPress the enter key to exit.")
After correct = next_line(the_file), correct is a string like '1\n'. correct[0] then gets you a string like '1', which you later compare to the result of raw_input, which doesn't include a \n at the end. So you need to do [0] to get the first character out.
It would probably be better to use .strip() instead, because then it would potentially work for answers that aren't a single character (if you changed the game to support 10+ answers, or answers with a different kind of name), it'd be a little more obvious what's going on, and it would ignore spaces on the ends, which are definitely irrelevant in either the file or the user's input.

Categories

Resources