1

So I would like to run two programs, a timer and a math question. But always the input seems to be stopping the timer funtion or not even run at all. Is there any ways for it to get around that? I'll keep the example simple.

import time

start_time = time.time()
timer=0
correct = answer
answer = input("9 + 9 = ") 
#technically a math question here
#so here until i enter the input prevents computer reading the code
while True:
    timer = time.time() - start_time
    if timer > 3:
#3 seconds is the limit
    print('Wrong!')
quit()

So recap i would like the player to answer the question in less than 3 seconds.

after the 3 seconds the game will print wrong and exit

if the player answer within three seconds the timer would be 'terminated' or stopped before it triggers 'wrong' and quit

hope you understand, and really appreciate your help

Yo LEE
  • 55
  • 1
  • 5

2 Answers2

0

On Windows you can use the msvcrt module's kbhit and getch functions (I modernized this code example a little bit):

import sys
import time
import msvcrt


def read_input(caption, timeout=5):
    start_time = time.time()
    print(caption)
    inpt = ''
    while True:
        if msvcrt.kbhit():  # Check if a key press is waiting.
            # Check which key was pressed and turn it into a unicode string.
            char = msvcrt.getche().decode(encoding='utf-8')
            # If enter was pressed, return the inpt.
            if char in ('\n', '\r'): # enter key
                return inpt
            # If another key was pressed, concatenate with previous chars.
            elif char >= ' ': # Keys greater or equal to space key.
                inpt += char
        # If time is up, return the inpt.
        if time.time()-start_time > timeout:
            print('\nTime is up.')
            return inpt

# and some examples of usage
ans = read_input('Please type a name', timeout=4)
print('The name is {}'.format(ans))
ans = read_input('Please enter a number', timeout=3)
print('The number is {}'.format(ans))

I'm not sure what exactly you have to do on other operating systems (research termios, tty, select).

Another possibility would be the curses module which has a getch function as well and you can set it to nodelay(1) (non-blocking), but for Windows you first have to download curses from Christopher Gohlke's website.

import time
import curses


def main(stdscr):
    curses.noecho()  # Now curses doesn't display the pressed key anymore.
    stdscr.nodelay(1)  # Makes the `getch` method non-blocking.
    stdscr.scrollok(True)  # When bottom of screen is reached scroll the window.
    # We use `addstr` instead of `print`.
    stdscr.addstr('Press "q" to exit...\n')
    # Tuples of question and answer.
    question_list = [('4 + 5 = ', '9'), ('7 - 4 = ', '3')]
    question_index = 0
    # Unpack the first question-answer tuple.
    question, correct_answer = question_list[question_index]
    stdscr.addstr(question)  # Display the question.

    answer = ''  # Here we store the current answer of the user.
    # A set of numbers to check if the user has entered a number.
    # We have to convert the number strings to ordinals, because
    # that's what `getch` returns.
    numbers = {ord(str(n)) for n in range(10)}

    start_time = time.time()  # Start the timer.

    while True:
        timer = time.time() - start_time

        inpt = stdscr.getch()  # Here we get the pressed key.
        if inpt == ord('q'):  # 'q' quits the game.
            break
        if inpt in numbers:
            answer += chr(inpt)
            stdscr.addstr(chr(inpt), curses.A_BOLD)
        if inpt in (ord('\n'), ord('\r')):  # Enter pressed.
            if answer == correct_answer:
                stdscr.addstr('\nCorrect\n', curses.A_BOLD)
            else:
                stdscr.addstr('\nWrong\n', curses.A_BOLD)

        if timer > 3:
            stdscr.addstr('\nToo late. Next question.\n')

        if timer > 3 or inpt in (ord('\n'), ord('\r')):
            # Time is up or enter was pressed; reset and show next question.
            answer = ''
            start_time = time.time()  # Reset the timer.
            question_index += 1
            # Keep question index in the correct range.
            question_index %= len(question_list)
            question, correct_answer = question_list[question_index]
            stdscr.addstr(question)

# We use wrapper to start the program.
# It handles exceptions and resets the terminal after the game.
curses.wrapper(main)
Community
  • 1
  • 1
skrx
  • 19,980
  • 5
  • 34
  • 48
  • There is no input and even when i did put a name in it the code ignores the input and continues running – Yo LEE Mar 19 '17 at 22:54
  • Do you run it from the command-line or an IDE like IDLE or PyCharm? And are you using Windows? You have to use the Windows command-line. – skrx Mar 19 '17 at 23:04
  • @Yo LEE let me know if it works. I'll add an example which uses curses later. – skrx Mar 20 '17 at 14:00
  • I've just added the curses example. I hope you can understand it (better read the documentation). I also don't have much experience with it, so no guarantee ;). You could also use a GUI framework like tkinter or the pygame library, but you'll need to spend a little time to get used to them as well. – skrx Mar 20 '17 at 17:47
0

Use time.time(), it returns the epoch time (that is, the number of seconds since January 1, 1970 UNIX Time). You can compare it to a start time to get the number of seconds:

start = time.time()
while time.time() - start < 60:
    # stuff

You can have a timer pull you out of your code at any point (even if the user is inputting info) with signals but it is a little more complicated. One way is to use the signal library:

import signal
def timeout_handler(signal, frame):
    raise Exception('Time is up!')
signal.signal(signal.SIGALRM, timeout_handler)

This defines a function that raises an exception and is called when the timeout occurs. Now you can put your while loop in a try catch block and set the timer:

signal.alarm.timeout(60)
try:
    while lives > 0
        # stuff
except:
    # print score
ARAT
  • 884
  • 1
  • 14
  • 35
aldboss12
  • 1
  • 3