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

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...

Related

How do I get a python quiz to randomize questions?

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.

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.

In Python, how do you scan for a specific number

Hello I am making a game out of python, and I made the game write data on a text document, and is there a way I can code so if the text file says a person named Bob is on level 4, then make the program start on level 4. I have tried using for loop to do the job, but it wont work. It will not initiate the text file and just goes over to level 1.
Here is the game code(for reading and writing:
import os
#---------------------------
os.system("color a")
#---------------------------
def ping(num):
os.system("ping localhost -i", num, ">nul")
def cls():
os.system("cls")
#--------------------------
print("the game")
ping(2)
cls()
print("1: New Game")
print("2: Continue")
print("3: Credits")
while True:
choice=input("Enter")
if choice==1:
name=input("Enter your name")
firstsave=open("data.txt", "W")
firstsave.write(name, " ")
# there will be the game data here
elif choice==2:
opendata=file("data")
#opening the file
while True:
''' is the place where the file scanning part needs to come.
after that, using if and elif to decide which level to start from.(there are a total of 15 levels in the game)
'''
The text file:
User Level
Bob 5
George 12
You haven't given quite enough information, but here's one approach:
elif choice == 2:
with open("x.txt") as f:
f.readline() # skip the first line
for lines in f: # loop through the rest of the lines
name, level = line.split() # split each line into two variables
if name == playername: # assumes your player has entered their name
playlevel(int(level)) # alternatively: setlevel = level or something
break # assumes you don't need to read more lines
This assumes several things, like that you know the player name, and that the player has only one line, the name is just a single word, etc. Gets more complicated if things are different, but that's what reading the Python documentation and experimenting is for.
Also note that you use 'w' to write in choice 1, which will (over)write rather than append. Not sure if you meant it, but you also use different filenames for choice 1 and 2.

Saving/Loading lists in Python

I am new to python (and programming in general) and am making a database/register for a typical class. I wanted the user to be able to add and remove pupils from the database, I used lists primarily for this but have hit a stump.
Whenever I restart the program the list the user has modified returns back to the defualt list I specified in the code. I looked around the internet and tried to save the list onto a seperate txt file. However the txt file also goes back to the defualt every time I restart the program. I would like you to please give me a way to save the changes made to the list and keep them that way. Here is the code (it's not very good):
def menu():
print "*****************CLASS REGISTER*****************"
print "Press 1 See The List Of Pupils"
print "Press 2 To Add New Pupils"
print "Press 3 To Remove Pupils"
print "Press 0 To Quit \n"
filename = open('pupil.txt','r')
pupil = ["James Steele", "Blain Krontick", "Leeroy Jenkins", "Tanvir Choudrey"]
def see_list(x):
print x
def add_pupil(x):
print "You have chosen to add a new pupil.\n"
option = raw_input("Please type the childs name.")
x.append(option)
filename = open('pupil.txt','w')
filename.write('\n'.join(pupil))
filename.close()
print option, "has been added to the system."
return x
def delete_pupil(x):
print "You have chosen to remove a pupil.\n"
option = raw_input("Please type the childs name.")
if option in x:
x.remove(option)
filename = open('pupil.txt','w')
filename.write('\n'.join(pupil))
filename.close()
print option, "has been removed from the system."
else:
print "That person is not in the system."
return x
one = 1
while one != 0:
menu()
option = input()
if option == 1:
see_list(pupil)
elif option == 2:
add_pupil(pupil)
elif option == 3:
delete_pupil(pupil)
elif option == 0:
break
else:
print "That is not a valible choice."
filename = open('pupil.txt','w')
filename.write('\n'.join(pupil))
filename.close()
if option == 0:
quit
Well, you just open the pupil.txt file but never read back its contents. You need something like this:
filename = open('pupil.txt', 'r')
contents = filename.read()
filename.close()
pupil = [name for name in contents.split('\n') if name]
Also, you will need to handle the case when the pupil.txt file does not exist; this can be done with a try..except block around the IO calls.
Finally, as one of the comments has mentioned above, have a look at the pickle module, which lets you store a Python object in a file in Python's internal format (which is not really readable, but saves you a lot of hassle).
Not related to your question directly, but this:
one = 1
while one != 0:
...
is silly. All you need is:
while True:
...
This is what a database is for. Use sqlite - a simple file-based database the libraries for which come bundled with python.

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