How to set input time limit for user in game?

I was wondering how I can make a program with input of MAXIMUM 5 seconds(e.g he can send input after 2 seconds) in python I decided to do a SIMPLE game where you basically have to rewrite a word below 5 seconds. I know how to create input and make it wait EXACTLY 5 SECONDS, but what I want to achieve is to set maximum time of input to 5 seconds so if a user types an answer in let’s say 2 seconds he will go the next word. Could you tell me the way to achieve my goal. Thanks in advance!(I might respond the next day in case of getting an answer because I have to go sleep)

The code I have right now

for word in ["banana","earth","turtle","manchester","coctail","chicken"]:

    # User gets maximum of 5 seconds to write the word,
    # if he does it before 5 seconds pass ,he goes to next word (does not have to wait exactly 5 seconds, he   
    # can send input in e.g 2 seconds)      
    # if he does not do it in 5 seconds he loses game and it is finished


    user_input = input(f"Type word '{word}': ")

    #IF the word is correct go to next iteration
    if(user_input==word):
        continue

    #If the word is incorrect finish the game
    else:
        print("You lost")
        break

I tried to do it with threading.Timer() but it doesn’t work

import threading

class NoTime(Exception):
    pass

def count_time():
    raise NoTime

for word in ["banana","earth","turtle","manchester","coctail","chicken"]:
    try:


        #Create timer which raises exception after 5 seconds
        timer = threading.Timer(5,count_time)
        timer.start()

        user_input = input(f"Type word '{word}': ")
        #if timer hasn't lasted 5 seconds then destroy it in order to prevent unwanted exception
        timer.cancel()

        if user_input==word:
            print("Correct")
        else:
            print("Incorrect, you LOSE!")
            break

    except NoTime:
        print("You run out of time, you lose")
        break

The error i get

Traceback (most recent call last):
  File "C:\Users\papit\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1038, in _bootstrap_inner
    self.run()
  File "C:\Users\papit\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1394, in run
    self.function(*self.args, **self.kwargs)
  File "C:\Users\papit\OneDrive\Pulpit\Programming\Python Bro Course\Math\second\threading_training.py", line 7, in count_time
    raise NoTime
NoTime

There are two ways I can think of.

  1. Use the signal.alarm() function
  2. Use non-blcking I/O

For (1) - untested!
a) Set up a signal handler for the SIGALARM signal.
b) in a try block call signal.alarm() then input()
c) If the input completes cancel the signal.alarm()
d) if the SIGALARM fires you can ignore the signal and
catch the I/O expection from input()

For (2) - also untested!
Use the select function from the select module.
Set the sys.stdin.fileno() to be non_blocking.
Then you can call select on sys.stdin.fileno() (== 1) with a timeout.
When select returns check to see if ther are a timeout of the stdin is readable.
To read what the user typed call os.read().
That will allow you to read a line once its typed.
If you use the termio module you can set it up so that the terminal to can be
reading 1 char at a time without blocking.

I was wondering how I can make a program with input of MAXIMUM 5
seconds(e.g he can send input after 2 seconds) in python I decided to
do a SIMPLE game where you basically have to rewrite a word below 5
seconds. I know how to create input and make it wait EXACTLY 5 SECONDS,

Do you? How would you do that?

but what I want to achieve is to set maximum time of input to 5 seconds
so if a user types an answer in let’s say 2 seconds he will go the next
word. Could you tell me the way to achieve my goal. Thanks in
advance!
(I might respond the next day in case of getting an answer because I
have to go sleep)

That’s fine, we’re all in different timezones here.

The code I have right now

 import threading

 class NoTime(Exception):
     pass

 def count_time():
     raise NoTime

 for word in ["banana","earth","turtle","manchester","coctail","chicken"]:
     try:
         #Create timer which raises exception after 5 seconds
         timer = threading.Timer(5,count_time)
         timer.start()
         user_input = input(f"Type word '{word}': ")
         #if timer hasn't lasted 5 seconds then destroy it in order to prevent unwanted exception
         timer.cancel()

All this is fine so far. Except for the try/except.

         if user_input==word:
             print("Correct")
         else:
             print("Incorrect, you LOSE!")
             break
     except NoTime:
         print("You run out of time, you lose")
         break

The difficulty is that the Timer runs in a separate Thread, so when
it raises an exception that exception unwinds the separate Thread’s
stack, not your main programme stack.

The error i get

Traceback (most recent call last):
 File "C:\Users\papit\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1038, in _bootstrap_inner
   self.run()
 File "C:\Users\papit\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1394, in run
   self.function(*self.args, **self.kwargs)
 File "C:\Users\papit\OneDrive\Pulpit\Programming\Python Bro Course\Math\second\threading_training.py", line 7, in count_time
   raise NoTime
NoTime

See that in this traceback, your main programme is not part of the call
stack. Instead, count_time is called directly from the threading
library. This is characteristic of the code being run in its own thread.

Basicly, you can’t interrupt a Thread. The best you get to do is to
set variables and poll them, or run parallel things in Threads and
collect their results.

What you could do is:

  • make a Queue
  • run the input() in its own Thread, and have it put the input
    result to the Queue
  • kick off a Timer to put a distinctive value such as None onto the
    Queue

The main programme reads from the queue: whichever happens first (the
input or the timer) will be the first value you get from the
Queue. Cancel the timer. Test the value.

This does still leave the input blocking, which can be a problem for
further input.

Cheers,
Cameron Simpson cs@cskk.id.au

One possibility is to use a prebuilt event loop with time-delay callbacks: 1) tkinter and ‘after’ functions; asyncio and call_later functions. There are likely solutions to using either on stackoverflow. IDLE uses tkinter after functions to display completions and call hints after a delay.

Hi, thanks for answer, unfortunatelety I haven’t succeeded in finding solution yet. I read something about signal.alarm() and it turned out that it doesn’t work on windows. I haven’t tried your second advice yet because I have to learn these things and it takes some time. I will give myself few days to solve that problem and If i don’t manage to do it I will seek for more help.

Thanks for answer, I haven’t tried tkinter solution yet(of course I will do it but I don’t think that i can use tkinter module to structure my console program), I learned something about asyncio but not enough to solve that problem. I will try to solve that problem in few days and if I don’t succeed i will look for more help.

Hi, thanks for answer but in your solution you say that “This does still leave the input blocking, which canbe a problem for further input.” and that’s the main problem because I wanted to throw an exception in order to get rid of that input.

I realized that the blocking nature of input() is a problem for using event loops. This 50+ years old tech is not meant for timeout interaction. But stackoverflow searh for [python] input timeout has multiple answers.

The first hit python - Keyboard input with timeout? - Stack Overflow
has multiple answers for both unix and windows. We don’t need to add more answers here.

That said, are you required to use a console/terminal? In a tkinter app, one can display a label type xyz and a non-blocking entry box [ ] and start a timer. If the entry event occurs first, stop the timer, else remove the entry box.

Aye.

Since you’re on a console it looks like Barry’s second suggestion is the
most feasible:

  • make the input nonblocking
  • do a timedout read of some kind

Basicly you need to write an input()-like function which embodies a
timeout.

Since you’re on Windows the termios base solutions probably won’t be
available to you as it is a UNIX specific API. Basicly, you need a
Windows supported read-with-timeout function of some kind which works in
the console. I believe there’s more than one kind of Windows console
these days, the ancient cmd.exe and the modern and I gather quite good
Windows Terminal. You might do better with the latter, but I don’t know.

Cheers,
Cameron Simpson cs@cskk.id.au