1
import random
import time

num_questions = 10
score = 0


print('''
Welcome to the math quiz.
You will have 3 tries and 8 seconds to answer each question.
Good luck.
''')

time.sleep(3)

for question_num in range(num_questions):
    max_chances = 3
    current_chances = 0
    num1 = random.randint(0, 9)
    num2 = random.randint(0, 9)
    correct_answer = num1 * num2
    while current_chances < max_chances:
        current_chances += 1
        user_answer = input('#%s: %s * %s = ' % (question_num+1, num1, num2))
        if int(user_answer) == correct_answer:
            print('Correct.')
            break
        else:
            if current_chances == max_chances:
                print('Out of tries. Please wait for the next question.')
                time.sleep(1)
            else: 
                print('Not quite. Try again.')
                continue
    

I have this program that asks 10 math questions with 3 tries per question. This is for a practice project I am doing from Automate the Boring Stuff with python in which the directions read:

To see how much PyInputPlus is doing for you, try re-creating the multiplication quiz project on your own without importing it. This program will prompt the user with 10 multiplication questions, ranging from 0 × 0 to 9 × 9. You’ll need to implement the following features: • If the user enters the correct answer, the program displays “Correct!” for 1 second and moves on to the next question. • The user gets three tries to enter the correct answer before the program moves on to the next question. • Eight seconds after first displaying the question, the question is marked as incorrect even if the user enters the correct answer after the 8-second limit.

I believe I've met all the requirements besides the 8-second limit. I'm not exactly sure how to implement this into my code. The book has not yet introduced asyncio or threading, some of the ideas friends of mine have suggested, and I'm not exactly sure what other way I could implement a sort of timer to this program. Would anyone be able to help? Thank you.

Logic
  • 33
  • 1
  • 4

2 Answers2

0

I think there is no way of doing this without creating another thread for timer. Because you have to do two processes(timer and input) at once.

I think threading.Timer useful for your case. It is easy. Firstly, you have to create a timer :

t = threading.Timer(8,print,["Your time is up"])

Timer with timeout of 8 seconds, when it ends, it will print specified message. Then, you have to start your timer just before input. After input, you can cancel your timer. So your input statement will be like this :

timer = threading.Timer(8,print,["Your time is up"]) # creating timer
timer.start() # starting timer
user_answer = input('#%s: %s * %s = ' % (question_num+1, num1, num2))
timer.cancel() # cancelling 

Note : When 'your time is up' message appears input still goes on, it just prints the message. There are ways to terminate it too, but they are more complicated and OS dependent. Check this answer.

Your code will be like this :


for question_num in range(num_questions):
    max_chances = 3
    current_chances = 0
    num1 = random.randint(0, 9)
    num2 = random.randint(0, 9)
    correct_answer = num1 * num2
    while current_chances < max_chances:
        current_chances += 1
        timer = threading.Timer(8,print,["Your time is up"]) # creating timer
        timer.start() # starting timer
        user_answer = input('#%s: %s * %s = ' % (question_num+1, num1, num2))
        timer.cancel() # cancelling 

        # user_answer likely to be empty, but, still user could write something,
        # because we are not terminating input process, just printing a message.
        if user_answer == ""
            if int(user_answer) == correct_answer:
                print('Correct.')
                break
            else:
                if current_chances == max_chances:
                    print('Out of tries. Please wait for the next question.')
                    time.sleep(1)
                else: 
                    print('Not quite. Try again.')
                    continue


munir.aygun
  • 414
  • 1
  • 4
  • 11
  • Since my code uses a while loop for giving the user 3 attempts per question, how would I go by with the placement of this threading? Since the actual input would be in the while current_chances < max_chances, and I can't have the entire timer in the while loop since then it would reset everytime the user attempted to answer the same question. – Logic Jul 25 '21 at 17:48
  • I've edited the answer. I think it will help you. – munir.aygun Jul 25 '21 at 18:56
0

Use the time.time() function.

import time
correct=0
Qn=0
for i in range(10):
    for j in range(10):
        time1=int(time.time())#time when this iteration started
        Qn+=1
        for k in range(3):#3 tries
            try:#as the user could enter a non int value
                userAns=int(input(f'#{Qn}:{i}*{j}=?'))
                time2=int(time.time())#time when the user entered some value
            except ValueError:#if the user did not enter a number
                if k!=2:#if the user is not on their 3rd try
                    '''if the user is not on their 3rd try as there is no need to say enter a number after 
                    the last try because the program would have already moved on'''
                    print('Enter a number!')
                    continue#to next try
            else:
                if time2-time1>=8:#if the user took more than or equal to 8 seconds to answer
                    print('Timeout!')
                    break#to the next number
                if userAns==i*j:
                    print('Correct!')
                    time.sleep(1)
                    correct+=1
                    break#to the next number
                else:
                    print('Incorrect!')
                
        else:
            print('Out of tries!')
            '''the for loop ended natuarally meaning user failed to
            enter the correct answer in the given no. of tries'''
print('score:%s/%s'%(correct,100)) 
Fquak
  • 1
  • 5