I am trying to create a multiple choice quiz using python. I have an external .txt file that has 20 questions in and I want it to select 10 random questions from that file, which it currently does. The file has the layout:
1,Who was the first man to walk on the moon?,A.Michael Jackson,B.Buzz Lightyear,C.Neil Armstrong,D.Nobody,C
The problem i'm having is that I don't want it to print the same question twice.
The only way I can think to solve this is to add detail[0], which is the question number, to a list defined in python and then check within that list to make sure that the question number isn't duplicated.
import random
qno = []
def quiz():
i = 0
global score #makes the score variable global so it can be used outside of the function
score=0 #defines the score variable as '0'
for i in range (1,11): #creates a loop that makes the program print 10 questions
quiz=open('space_quiz_test.txt').read().splitlines() #opens the file containing the questions, reads it and then splits the lines to make them seperate entities
question=random.choice(quiz)
detail = question.split(",")
print(detail[0],detail[1],detail[2],detail[3],detail[4],detail[5])
print(" ")
qno.append(detail[0])
print(qno)
if detail[0] in qno is False:
continue
qno.append(detail[0])
print(qno)
elif detail[0] in qno is True:
if detail[0] not in qno == True:
print(detail[0],detail[1],detail[2],detail[3],detail[4],detail[5])
print(" ")
qno.append(detail[0])
print(qno)
while True:
answer=input("Answer: ")
if answer.upper() not in ('A','B','C','D'):
print("Answer not valid, try again")
else:
break
if answer.upper() == detail[6]:
print("Well done, that's correct!")
score=score + 1
print(score)
continue
elif answer.upper() != detail[6]:
print("Incorrect, the correct answer is ",detail[6])
print(score)
continue
quiz()
When I run this code I expect that no question is repeated twice but it always seems to do that, i'm struggling to think of a way to do this. Any help would be grateful, Thank you!
Use this:
questions = random.sample(quiz, 10)
It will select a random sublist of length 10, from the quiz list.
Also:
You should read the file, and make the question list outside the loop, then just loop over the questions:
with open('space_quiz_test.txt') as f:
quiz = f.readlines()
questions = random.sample(quiz, 10)
for question in questions:
...
Read all the questions:
with open('space_quiz_test.txt') as f:
quiz = f.readlines()
Shuffle the list of questions in place:
random.shuffle(quiz)
Loop on the shuffled list:
for question in quiz:
print(question)
This is because random.choice can give the same output more than once. Instead of using random.choice try
random.shuffle(list) and then choosing the first 10 records from the shuffled list.
quiz=open('space_quiz_test.txt').read().splitlines()
random.shuffle(quiz)
for question in quiz[1:11]:
detail = question.split(",")
print(detail[0],detail[1],detail[2],detail[3],detail[4],detail[5])
You can accomplish this by drawing the questions all at once with choice without replacement, then iterating over those.
import numpy as np
quiz=open('space_quiz_test.txt').read().splitlines() #opens the file containing the questions, reads it and then splits the lines to make them seperate entities
questions=np.random.choice(quiz, size=10, replace=False)
for question in quesions: #creates a loop that makes the program print 10 questions
#rest of your code
Instead of opening the file 10 times, get 10 questions from it and loop asking them:
def get_questions(fn, number):
with open(fn) as f:
# remove empty lines and string the \n from it - else you get
# A\n as last split-value - and your comparisons wont work
# because A\n != A
q = [x.strip() for x in f.readlines() if x.strip()]
random.shuffle(q)
return q[:number]
def quiz():
i = 0
global score # makes the score variable global so it can be used outside of the function
score=0 # defines the score variable as '0'
q = get_questions('space_quiz_test.txt', 10) # gets 10 random questions
for question in q:
detail = question.split(",")
print(detail[0],detail[1],detail[2],detail[3],detail[4],detail[5])
print(" ")
# etc ....
Doku:
inplace list shuffling: random.shuffle
There are several other things to fix:
# global score # not needed, simply return the score from quiz():
my_score = quiz() # now my_score holds the score that you returned in quiz()
...
# logic errors - but that part can be deleted anyway:
elif detail[0] in qno is True: # why `is True`? `elif detail[0] in qno:` is enough
if detail[0] not in qno == True: # you just made sure that `detail[0]` is in it
...
while True:
answer=input("Answer: ").upper() # make it upper once here
if answer not in ('A','B','C','D'): # and remove the .upper() downstream
print("Answer not valid, try again")
else:
break
Related
I'm new to python so don't be surprised it if is something really basic, but i've been trying to write this code that asks math questions and then saves the scores in order to show them again at the start of the loop, but it doesn't save the scores. what should i change?
this is the code
scores = []
names = []
while True:
f = open("highscore.txt", "r")
for line in f:
line = line.strip("\n")
line = line.split(" ")
names.append(line[0])
scores.append(int(line[1]))
print(f.read())
for pos in range(len(names)) :
print(pos + 1, names[pos], scores[pos])
f.close()
score = 0
print("hello, welcome to maths game")
print("\nQuestion 1: what is 2 x 2 x 2?")
answer = int(input("your answer >"))
if answer == 8:
print("correct")
score = score + 1
print("your score is ", score)
else:
print("incorrect")
print("the score is ", score)
print("\nQuestion 2: what is 34 x 2?")
answer = int(input("your answer >"))
if answer == 68:
print("correct")
score = score + 1
print("your score is", score)
else:
print("incorrect")
print("the score is", score)
name = input("what is your name?")
position = 0
for compare_score in scores :
if score < compare_score:
position = position + 1
scores.insert(position, score)
names.insert(position, name)
scores = scores[:5]
names = names[:5]
f = open("highscore.txt", "w")
for pos in range (len(names)):
f.write(names[pos] + " " + scores[pos])
it doesn't give any kind of error message, just loops back and doesn't save the names, neither the scores
You have a for-loop on scores that adds a new item to the scores list at each iteration. The for-loop will never reach the end of the list because there is always 'one more'.
Alain T.'s answer has already stated the root cause that you experienced. The loop never stops which appears to you as "freezing" since there are no outputs or indicators that you (as a user/developer) see that the loop still runs. So actually nothing freezes here .. it just runs forever.
For that reason I wanted to add a short note how to drill down the problem your own the next times..
Keyword here is clearly: debugging.
Debugging means: "finding out what your code does while being executed"
A very simple but (at least for small programs) quite effective approach is using one or more print() statements. These can be used to display the value of variables, the property of an object or just some statement like print("I am before the loop") to know where execution runs/stops..
A possible would be: (look at the print statements)
while True:
print("in while") #<-- this one
...
print("before loop") #<-- this one
for compare_score in scores :
print("in loop") #<-- this one repeats....
if score < compare_score:
position = position + 1
scores.insert(position, score)
names.insert(position, name)
scores = scores[:5]
names = names[:5]
print("After loop") #<-- never see this one
f = open("highscore.txt", "w")
for pos in range (len(names)):
f.write(names[pos] + " " + scores[pos])
Running your program again should print out:
in while
before loop
in loop
in loop
in loop
in loop
in loop
in loop
in loop
...
and so on... So what you know now is:
Everything before the loop at least executed
The loop runs forever .
So now it would be time to dig a little deeper inside the loop.
Most interesting would be to examine the variable on which the loop exit
depends.
In your case that is the length of the scores list:
for compare_score in scores:
So the loop runs until there are no more scores to compare left in the scores list.
So it might be a good idea to print() the length of the list to check if and how it decreases until there are no more scores to compare.
So add something like this:
Check the two print() statements containing len(scores)
for compare_score in scores:
print("in loop")
if score < compare_score:
position = position + 1
scores.insert(position, score)
names.insert(position, name)
scores = scores[:5]
names = names[:5]
print(len(scores)) #<--- this one
# or a bit nicer as f-string:
print(f"len score: {len(scores)}") #<--- this one
print("After loop")
Both are displaying the length of the scores list.
The former one just does it a little nicer.
There is a lot more to debugging. Many tools like VSCode, Pycharm, etc. support a lot more sophisticated methodologies to step through code, set breakpoints, inspect objects and variables ..
But for small ans simple projects and when the focus is on learning, instant feedback and repeating. At least to my mind. Print() debugging gives you a lot of insight in a very easy and simple manner.
Oh, and if you read until here:
"Welcome to the community" Just jokin', welcome !! ;)"
I wrote this little piece of code to practice loops and nested conditionals. My initial intention was to create two empty lists which would be progressively filled by appending the sorted input.
However, when I initialized the lists as at_risk = [] and safe = [], nothing came out as output. What did I do wrong?
When I put an element in them (quotation marks or a string), the code runs without problem.
i = 10
at_risk = []
safe = []
while i > 0:
for kids in at_risk:
question = input("Have you ever been questioned by the police, Y/N? or Q to end: ").upper()
name = input("What is your name? ")
age = int(input("How old are you?"))
if question =="Y":
at_risk.append(name)
print("This young kid", name, "is at risk.")
if age < 15:
print("These young kids", at_risk, "are at heightened risk.")
elif question == "N":
safe.append(name)
print("This kid",name," is safe.")
print("These kids",safe,"are safe.")
else:
if question == "Q":
exit(0)
The issue lies solely within these lines of code:
at_risk = []
safe = []
while i > 0:
for kids in at_risk:
You initialize your loop with for kids in at_risk, however at_risk is empty, therefor it will never begin looping. simply change at_risk and it should work!
So I'm having this problem in which my code is inconsistently failing at random times, this has been answered before:(Python 3.7.3 Inconsistent code for song guessing code, stops working at random times now I have had to add a leaderboard to this song guessing game I am doing. I randomly choose a number of which is used to find the artist and song. If right, it will remove the song and artist to prevent dupes and carry on. Here is the code:
loop = 10
attempts = 0
ArtCount = len(artist)
for x in range (ArtCount):
print(ArtCount)
randNum = int(random.randint(0, ArtCount - 1))
randArt = artist[randNum]
ArtInd = artist.index(randArt)# catches element position
songSel = songs[randNum]
print (randNum)
print ("The artist is " + randArt)
time.sleep(0.5)
songie = songSel
print( "The songs first letter be " + songSel[0])
time.sleep(0.5)
print("")
question = input("What song do you believe it to be? ")
if question == (songSel):
songs.remove(songSel)
artist.remove(randArt)
print ("Correct")
print ("Next Question")
if attempts ==0:
points = points + 5
print("+5 Points")
print("")
if question != (songSel):
loop = loop + 1
attempts = attempts + 1
print("")
print("Wrong,", attempts, "questions wrong, careful!")
print("")
time.sleep(0.5)
if attempts == 5:
break
print("GAME OVER")
Pardon my mess, I'm just starting off in making large code, will clean up when finished. I've had the additional problem of having the count controlled loop as 10 (the amount of questions) then having to go pas the loop when you get a question wrong, I've tried having it loop by the amount of songs in the list and I've also tried making a variable that +1 when you get it wrong so you have space to answer but that doesn't work either. After implementing the leaderboard it now doesn't remove any songs (I was messing with the indentation to make the leaderboard print every time.) The error I randomly get is;
randArt = artist[randNum]
IndexError: list index out of range
I'm never sure why this is the code that is the problem, I'm not even sure if it's necessary.
Please, don't use
randNum = int(random.randint(0, ArtCount - 1))
You may easily get a random artist by using:
randArt = random.choice(artist)
The problem is your code had modify the artist array length when you remove item on true answer. You need to get the right artist count after you change.
for x in range (ArtCount):
print(ArtCount)
count = len(artist) # get the new length here
randNum = int(random.randint(0, count - 1)) # use the new length here instead of your old ArtCount
I am just starting out with python, this is part of my first program. The if statement at the end always prints 'Please try again' even if the strings are identical and should output 'Well done'.
I have added print(usranswer) print(correctans) to make sure both strings are equal, which they are, i have also added usranswer.strip() correctans.strip() to remove any non existent white spaces, and the code is still not outputting the right result.
Any other suggestions would be helpful, Thanks.
fail = 0
Qnumber = 1
while fail != 2:
import random
q = random.randint(1,5)
with open("answers.txt", "r") as answers:
for _ in range(q):
answer = answers.readline()
with open("questions.txt", "r") as initials:
for _ in range(q):
question = initials.readline()
print("question number")
print(str(Qnumber))
print('Please guess the name of this song, the name of the artist and the first letter in each word of the song title are below')
print(question)
usranswer = str(input())
correctans = str(answer)
usranswer.strip()
correctans.strip()
print(usranswer)
print(correctans)
if correctans == usranswer:
print('Well done')
score = score + 3
else:
fail = fail + 1
print('Please try again')
Even when both correctans and userans are exactly equal it still prints Please try again when it should print Well Done
Your .strip() calls return the stripped version of the string but you don't assign them to anything
You could try this
usranswer = input().strip()
correctans = answer.strip()
I am having some problems creating a quiz for my son for his revision. It is a quiz which reads a text file, and displays questions in a random order. The quiz stops when he has answered every question twice.
So he can time himself, how would I add a timer in there which starts when the first question is displayed, and prints when he has answered every question correctly twice? I am using python 3.3.
Here is the code so far:
import random
import time
#open the text file
myfile = open("james.txt")
mylines = myfile.read().splitlines()
wrong = 0
#make blank lists
questions = []
answers = []
scores = []
#Seperate the file into Question and answer
for linenumber in range(0, len(mylines), 2):
questions.append(mylines[linenumber])
answers.append(mylines[linenumber+1])
scores.append(0)
#Ask Question
for questionnumber in range(0,len(questions)):
while scores[questionnumber] <2:
questions.append(mylines[linenumber])
questionnumber = random.randint(0,len(questions))
print(questions[questionnumber])
print(scores)
#Generate Possible Answer
possibleanswers = []
possibleanswers.append(answers[questionnumber])
for answerposition in range(1,3):
randomnum = random.randint(0,len(answers)-1)
while answers[randomnum] in possibleanswers:
randomnum = random.randint(0,len(answers)-1)
possibleanswers.append(answers[randomnum])
#Shuffle Answers
random.shuffle(possibleanswers)
for answernumber in range(0,len(possibleanswers)):
print(answernumber+1,possibleanswers[answernumber])
input1 = int(input())
givenanswer = possibleanswers[input1-1]
if givenanswer == answers[questionnumber]:
print("Yes")
scores[questionnumber] = scores[questionnumber]+1
else:
print("No, the answer was",questionnumber)
wrong = wrong+1
questionnumber = random.randint(0,len(questions))
randint is inclusive, so it can return any number up to and including len(questions). If it returns the largest possible number, then you'll get an IndexError. For instance, if questions is three elements long, then questions[3] will be out of range.
Reduce the allowable range of your random numbers:
questionnumber = random.randint(0,len(questions)-1)
Edit: there also appears to be a typo in your #ask question loop, that causes questions to grow larger than answers.
for questionnumber in range(0,len(questions)):
while scores[questionnumber] <2:
questions.append(mylines[linenumber])
# ^^^ this line
This appears to be a copy-paste mistake. It doesn't make much sense to append to questions here, since it should already be fully populated after the #Seperate code. I suggest removing this line.