Update
Actually the version with select()
that is explained in this answer only works in Unix, because in Windows this function only supports sockets descriptors and not files (nor standard input).
Therefore this answer is not useful for the user who apparently works in Windows. However, I decided to leave it in case it was useful to other users.
A solution that does work in Windows appears at the end of the original response, in the Windows solution
section
Original answer
It is not necessary to enter multi-threaded programming to do what you want. You can use the function select()
.
This function allows you to wait in a list of "objects" by which you could receive data (they can be files, sockets or, in your case, the keyboard) until either one of those objects has received data, or Expend the waiting time. In either case select()
ends its execution and returns a list containing the objects for which data has been received (if the list is empty is that it was not received by any, and therefore the waiting time it ran out).
In your case, you could pass to select()
a list with a single object: the standard input (the keyboard). If the return of select()
that object is in the list returned, is that there are data waiting to be read from the keyboard, and simply call raw_input()
to read them (which will not be blocked waiting for anything, because there is something). Otherwise, the time has run out.
The following is a function that implements these ideas:
def espera_respuesta(tiempo_max=5):
print "> ",
sys.stdout.flush()
entrada = sys.stdin.fileno()
listo, _ ,_ = select.select([entrada], [], [], tiempo_max)
if entrada in listo:
respuesta = raw_input()
return respuesta
else:
return None
The first two lines are to print the "prompt" inviting the user to write something. Then we prepare the object "input" and we pass it to select()
(this function actually expects three separate lists, but the other two do not use them and they are empty). The last parameter is the maximum time that select will wait.
select()
returns three lists, of which again I am only interested in the first one (the other two will come empty). If entrada
is in the returned list, there is data. I read them. If not, it is that time has run out. Return None
to indicate that the user did not write anything.
You would use this function from your program, for example:
if __name__ == '__main__':
wt = True
while wt:
cls()
randint = random.randint(0, len(mw) - 1)
print('corrige: {}\npuntos: {}'.format(mw[randint], count))
yourw = espera_respuesta()
if yourw is None:
print("\n¡Tiempo agotado!")
time.sleep(2)
continue
check = corrector(gw[randint], yourw)
count += check
if count >= 5:
wt = False
print("ganaste")
elif count <= -3:
wt = False
print("perdiste")
Notice how instead of your previous raw_input()
there is now a call to espera_respuesta()
. When it returns, if you returned None
, you print a time-out message (we paused the program a couple of seconds so that the user has time to read it before you clear the screen again) and return to the beginning of the loop with continue
. Note that in this case we do not penalize the player, because we did not pass the corrector to his response. Remove the continue
if you want the answer to be verified (and since this was None
, it will not be the same as expected and you would be penalized).
Another improvement
Instead of storing the list of words well and badly in two separate variables, you could have a dictionary whose keys were the wrong words and the values the corrected version. I think that it is easier to add vocabulary to the game. The variable would be for example like this:
vocabulario = {
'chiko': 'chico',
'cinfonia': 'sinfonia',
'embidia': 'envidia',
'kansiones': 'canciones',
'majia': 'magia',
'mucica': 'musica',
'presio': 'precio',
'vanda': 'banda',
'yenar': 'llenar'
}
To choose one at random, you can use random.choice(list(vocabulario))
. The expression list(vocabulario)
converts the dictionary into a list, keeping only the keys (the wrong words), and random.choice()
returns a random element from that list. Once you have the misspelled word, your good version would be vocabulario[mal]
. That is:
mal = random.choice(list(vocabulario))
bien = vocabulario[mal]
Solution in Windows
In windows the solution with select()
is not valid. A multi-threaded solution would also be quite complex since, although it is easy to make another thread wait a while and print "Time out", it is not so easy to interrupt the main thread that will still be waiting in raw_input()
for the user to enter something.
It is easier to make a function that reads from the keyboard "key to key", in a loop, timing while reading, and ending the reading if the available time runs out.
This would be a possible implementation (specific to Windows, since it uses the msvcrt
module):
import msvcrt
def espera_respuesta(tiempo_max=5):
print "> ",
sys.stdout.flush()
horafinal = time.time() + tiempo_max
resultado = [] # Iremos almacenando aqui las teclas pulsadas
while True:
if msvcrt.kbhit(): # Si hay tecla pulsada
tecla = msvcrt.getche() # la leemos
if tecla == '\r': # si es retorno de carro
print()
return ''.join(resultado) # Retornamos la cadena leida
resultado.append(tecla) # Si no, metemos la tecla a la lista
time.sleep(0.1) # breve pausa para no saturar la CPU
if time.time() > horafinal: # Si se agotó el tiempo, volvemos
return None