Memory game timer implementation and error handling - python-3.x

I've implemented a memory game where the user has to sort numbers in his head while a timer of 5 sec is running.
Please see code below:
from random import randint
from threading import Timer
def timeout():
print("Time over\n#####################\n")
while True:
list = []
for i in range(5):
list.append(randint(1000, 10000))
t = Timer(5, timeout)
t.start()
print(list)
print('[ 1 , 2 , 3 , 4 , 5 ]')
solution = sorted(list)[2]
print('please select the 3rd largest number in this row (1-5):')
input_string = input()
selection = int(input_string)
if solution == list[selection - 1]:
print("Your guess is correct\n")
else:
print("Your guess is wrong\n")
t.join()
Here is the game interaction itself (please ignore the syntax highlighting):
USER#HOST:~/$ python3 memory_game.py
[8902, 8655, 1680, 6763, 4489]
[ 1 , 2 , 3 , 4 , 5 ]
please select the 3rd largest number in this row (1-5):
4
Your guess is correct
Time over
#####################
[5635, 3810, 1114, 5042, 1481]
[ 1 , 2 , 3 , 4 , 5 ]
please select the 3rd largest number in this row (1-5):
4
Your guess is wrong
Time over
#####################
[6111, 1430, 7999, 3352, 2742]
[ 1 , 2 , 3 , 4 , 5 ]
please select the 3rd largest number in this row (1-5):
23
Traceback (most recent call last):
File "memory_game.py", line 24, in <module>
if solution == list[selection - 1]:
IndexError: list index out of range
Time over
#####################
Can anybody help me with these things:
1. 'Time over' should only be written if the player needs more than 5 sec for the answer. If the player solves it in time the next challenge should appear silently.
2. If the player does not write any guess and presses 'Enter' the program terminates with error message:
Traceback (most recent call last):
File "memory_game.py", line 22, in
selection = int(input_string)
ValueError: invalid literal for int with base 10:''
3. If the player enters any random number the program quits with an 'Index out of range error' - I couldn't find out where to put try: except:
Any help would be appreciated - Thanks!

As for your questions:
You can accomplish that with t.cancel() (stop the timer and do not call the function) instead of t.join() (wait until the thread has finished; this will ALWAYS result in a timeout, of course)
(and 3.) These are basically the same -- put the query message and all input handling into a while loop, and break out of it once you know that the input is valid
...
As an extra, the "time over" message wasn't really doing anything useful (e.g. you could still enter a valid answer after the time over occurred. I "fixed" that in a brute force way by making the program exit if the timeout is hit. Instead of doing that, you can also use a global variable to store whether the timeout was hit or not and handle that in your input accordingly (make sure to make it threadsafe using e.g. a mutex).
In general, it might be easier to turn around the structure of the program -- let the main thread handle the timeout and verification of the input, let the thread only handle the input (this way, it's easy to kill the thread to stop the input from being handled).
Of course, using the select module, one could implement this even nicer without threads (have one pipe that gets written to by a thread/timer, and the standard input, then select both for reading and it will block until either user input or the timeout occurs).
And maybe someone will post a nice asyncio-based solution? ;)
Here's the modified solution (modifying only as little as possible to get it to work, one could refactor other parts to make it nicer in general):
import random
import threading
import os
def timeout():
print("Time over\n#####################\n")
# You can't use sys.exit() here, as this code is running in a thread
# See also: https://stackoverflow.com/a/905224/1047040
os._exit(0)
while True:
list = []
for i in range(5):
list.append(random.randint(1000, 10000))
t = threading.Timer(5, timeout)
t.start()
print(list)
print('[ 1 , 2 , 3 , 4 , 5 ]')
solution = sorted(list)[2]
while True:
try:
print('please select the 3rd largest number in this row (1-5):')
input_string = input()
selection = int(input_string)
chosen = list[selection - 1]
# If we arrive here, the input was valid, break out of the loop
break
except Exception as e:
# Tell the user that the input is wrong; feel free to remove "e"
# from the print output
print('Invalid input:', e)
if solution == chosen:
print("Your guess is correct\n")
else:
print("Your guess is wrong\n")
# Make sure to cancel the thread, otherwise guessing correct or wrong
# will block the CLI interface and still write "time out" at the end
t.cancel()

Related

EOFError() triggered when calling input() after using aioconsole.ainput()

I am encountering a strange error with a mini game I have created as part of a quiz application I am building to run in terminal. The mini game requires processes to run asynchronously, so I defined two coroutines to create it: one that prompts for input (using aioconsole.ainput(), an asynchronous input function) and one that executes a loop until input is entered. The mini game works fine on its own (included it in another program I made where it ran smoothly) and executes without error in the current application I am building, but after the mini game has executed, whenever I call the normal input() function, I receive an EOF error:
Traceback (most recent call last):
File "/Users/[...]/quiz_game.py", line 449, in <module>
quiz()
File "/Users/[...]/quiz_game.py", line 433, in quiz
prev_score = show_score(playerList,prev_score)
File "/Users/[...]/quiz_game.py", line 375, in show_score
input('Hit enter to continue.')
EOFError
I am 99.99% certain that the error has to do with some residual effect of executing the mini game's code, probably a residual effect of using aioconsole.ainput() because the code worked fine before I added the mini game into it, and distinct calls of input() in different locations within the overall code have triggered this error.
Here's the code of the mini game:
async def getInput():
global flag
flag = False
task = asyncio.create_task(loop())
await aioconsole.ainput()
flag = True
await task
return task.result()
async def loop():
count = 0
x = 20
while not flag:
count += 1
print('Hit enter at 20:' + '#' * (count % (x + 1)) + ' ' * (x - count %
(x + 1)),
end='\r')
await asyncio.sleep(0.1)
return count
def minigame():
print('\nScore 20 to get a powerup.')
for i in range(1,4):
print(f'---ATTEMPT {i}---')
result = asyncio.run(getInput()) % 21
print('Score:' + str(result))
if result == 20:
print('SUCCESS!!!')
time.sleep(2)
return
else:
print(
random.choice([
'I said 20, moron!', 'Can you not count?',
'Tut, tut, tut. Such a disappointment.'
]))
print('Try again\n')
print('FAIL. You have exhausted your 3 attempts.')
time.sleep(2)
Short Description of Mini Game:
The mini game displays an increasing number of hashtags until it hits twenty hashtags and then starts over at zero hashtags. If the user hits enter at exactly twenty hashtags, they win the game.
Anyways, any help that you could provide would be greatly appreciated!

Why do the code shows tons of error whenever i type in alphabet input

I am new to python and I have an upcoming assignment that creates a menu that creates a function whenever the users enter the input. Here is the problem, whenever I enter a number the code shows a normal invalid option. For alphabetic input, however, it started to appear tons of errors. Does anyone know how to solve this issue
import turtle
wn = turtle.Screen()
poly = turtle.Turtle()
wn.setup(1000, 600)
poly.pensize(2)
poly.fillcolor('lightblue')
poly.penup()
poly.goto(-400, 15)
poly.pendown()
def menu():
print(' *********************************')
print('1. Draw polygons')
print('2. Draw a flower')
print('3. Exit')
task = int(input('Enter an option (1/2/3): '))
return task
def draw_shape(t, sides):
for i in range(0, sides):
t.forward(50)
t.stamp()
t.left(360 / sides)
t.forward(50)
def draw_flower(t, sides):
for i in range(0, sides):
t.left(90)
t.forward(100)
t.left(137.5)
t.forward(60)
t.left(80)
t.forward(70)
das = menu()
if das == 1:
for angle in [10, 9, 8, 7, 6, 5, 4, 3]:
poly.penup()
poly.forward(100)
poly.pendown()
poly.begin_fill()
draw_shape(poly, angle)
poly.end_fill()
elif das == 2:
poly.pencolor('cyan')
wn.bgcolor('light yellow')
poly.speed(4)
poly.penup()
poly.goto(0, 0)
poly.pendown()
draw_flower(poly, 52)
poly.forward(-100)
elif das == 3:
print('Program exists. Have a nice day')
exit()
else:
print('Invalid option')
. Draw polygons
2. Draw a flower
3. Exit
Enter an option (1/2/3): sa
Traceback (most recent call last):
File "C:/Users/jonny/PycharmProjects/untitled2/Polygon and flowers.py", line 40, in <module>
das = menu()
File "C:/Users/jonny/PycharmProjects/untitled2/Polygon and flowers.py", line 18, in menu
task = int(input('Enter an option (1/2/3): '))
ValueError: invalid literal for int() with base 10: 'sa'
Your Python interpreter is basically telling you that it cannot parse 'sa' into an int, which is to be expected right?
When prompted to enter an option, if you enter sa, input(...) returns exactly that: sa, as a string.
At that point in your script, task = int(input(...)) essentially becomes task = int('sa').
Exceptions
Now put yourself in the shoes of function int(): you receive a string, and you must return an integer.
What do you do when the input string, 'sa' for that matter, does not correctly represent an integer?
You cannot return an integer, because that would imply that you parsed the string successfully.
Returning something else than an integer would make no sense (and would be a pain to work with).
So you throw an exception: the execution flow is interrupted, and a specific kind of object, an exception, is thrown.
Exception handling
When a function throws an exception, it is interrupted: it does not finish running, it does not return anything, and the thrown exception is forwarded to the calling function. If that function decides to catch that exception (i.e. to handle it), then good, the normal execution flow can resume at that point.
If it decides not to handle the exception, then that function is interrupted too and the exception is forwarded yet again to the calling function. It continues in a similar fashion until the exception is caught, or until "no calling function is left", at which point the Python interpreter takes over, halts the execution of your script, and displays info about that exception (which is what happened in your case).
A first solution
If you're new to Python, maybe you shouldn't worry too much about handling exceptions right now. More generally, if you try to handle every possible case when it comes to user input, you're in for a wild ride.
For the sake of completeness though:
In order for your code to do what you expect, replace the das = menu() line with this:
try: # Enter a section of code where exceptions may be thrown
das = menu() # menu() may throw an exception because of the int(...) inside
except: # 'Catch' any exception that was thrown using an `except` block
das = -1 # Set a dummy, invalid value
With this code, if menu() throws an exception (when you enter sa for example), it will be caught: the try block will be interrupted, and the except block will be executed. das will receive value -1, which by the rest of your code is invalid, and thus Invalid option will be displayed. This is much better than having your whole script halted!
On the other hand, if no exception is thrown by menu(), the try block will reach its end normally, and the except block will not be executed.
A better solution
However, this is not ideal. The exception should not be handled around menu(), it should be handled around int(...) inside your menu function.
You could do this as an exercise: first handle the exception inside menu, and then try to loop over the int(input(...)) statement until a valid value is entered by the user.
There again, exception handling is not necessarily trivial and can be hard to get right, especially for beginners. So don't get frustrated if it seems like a not-so-useful overcomplication to you, there will come a point where you realize you can't go without them.
You can read more about exceptions here: https://www.w3schools.com/python/python_try_except.asp or here if you want a more comprehensive tutorial: https://docs.python.org/3/tutorial/errors.html
Hope this helps. :)

Defining function difficulties ["NameError: name 'number' is not defined"]

Okay, trying to make a simple game of Guessing Numbers but I can't find the mistake in this code. Still pretty new to python so probably the reason why but I can't figure out what is wrong with it.
import random
from time import sleep
def start():
print("Welcome To The Guessing Game \n Try to guess the number I'm thinking of \n Good luck!")
selectRandomNumber()
guessCheck(number, numberInput=1)
def restart():
print("Creating new number ...")
sleep(1)
print("OK")
selectRandomNumber()
guessCheck(number,numberInput=1)
def selectRandomNumber():
number = random.randint(0,1000)
tries = 0
return
def tryAgain():
while True:
try:
again = int(input("Do you want to play again? y/n:"))
except ValueError:
print("Couldn't understand what you tried to say")
continue
if again == "y" or "yes":
print("Awesome! Lets go")
restart()
elif again == 'n' or "no":
print("Goodbye!")
break
else:
print("Not a valid option")
continue
def guessCheck(number,numberInput=1):
while True:
try:
numberInput = int(input("What number do you think it is?: "))
except ValueError:
print("Couldn't understand that. Try again")
continue
if numberInput > number:
print("Too high")
tries += 1
continue
elif numberInput < number:
print("Too low")
tries += 1
continue
elif numberInput == number:
print("Congrats! You got my number")
tryAgain()
number = selectRandomNumber()
print(number)
start()
Every time I try to run the program I keep getting the same mistake.
It tells me:
Traceback (most recent call last):
File "python", line 60, in <module>
start()
File "python", line 8, in start
guessCheck(number, numberInput)
NameError: name 'number' is not defined
Don't quite understand what that means.
Some help would be appreciated. Thanks!
* UPDATE *
Was able to fix the part about defining the variable but now new problem happened where when I try to run
Same code as before but added
guessCheck(number,numberInput=1)
and also added the variable number at the end
number = selectRandomNumber()
print(number)
start()
when I run it I get this
None # this is from `print(number)` so instead of getting a number here I'm getting `None`
Welcome To The Guessing Game
Try to guess the number I'm thinking of
Good luck!
What number do you think it is?:
The Traceback is telling you this:
We got to start().
start() called guessCheck().
We tried to pass two pieces of information to guessCheck(): the variable names number and numberInput.
We don't have those variables defined yet! numberInput doesn't get defined until once we've already started guessCheck(), and number isn't actually defined anywhere.
As Manoj pointed out in the comments, you probably want number to hold the output of selectRandomNumber(). So, instead of just calling selectRandomNumber() in start(), try number = selectRandomNumber() instead.
You can add a print(number) on the line right after that to make sure number has a value assigned to it.
Now number has a value, going into your call to guessCheck(). That still leaves numberInput undefined though. You can set a default value for function arguments like this:
guessCheck(number, numberInput=1)
That way, when guessCheck is called but numberInput hasn't been defined yet, it will automatically give it the value 1 until you set it explicitly.
You may encounter other issues with your code the way it is. My advice would be to start really simply - build up your game from each individual piece, and only put the pieces together when you're sure you have each one working. That may seem slower, but trying to go too fast will cause misunderstandings like this one.

Why are we still stuck on this function despite threading?

I want to make a function that waits for input, and if nothing is input in 2 seconds, skips the input and moves on to the rest of the function.
I tried this function from another thread:
import time
from threading import Thread
answer = None
def check():
time.sleep(2)
if answer != None:
return "ayy"
print("Too slow")
return "No input"
Thread(target = check).start()
answer = input("Input something: ")
print(answer)
This code asks for input, and if no input is added in 2 seconds it prints "too slow". However it never moves on to print(answer), I think it keeps waiting for user input.
I want to ask for user input and if it takes too long, it just takes input = None and moves on to the functions underneath it. I looked at timeout methods involving signal, but that's only for linux and im on a windows.
Your assumption is right. The input() call is waiting for the user to submit any input which in your case never happens.
A cross platform solution would be to make use of select():
import sys
import select
def timed_input(prompt, timeout=10):
"""
Wait ``timeout`` seconds for user input
Returns a tuple:
[0] -> Flag if timeout occured
[1] -> User input
"""
sys.stdout.write(prompt)
sys.stdout.flush()
input, output, error = select.select([sys.stdin], [], [], 2)
if input:
return True, sys.stdin.readline().strip()
else:
return False, None
timed_input('Input something: ', timeout=2)
That's a dirty prototype. I suggest to use exceptions for the timeout or a more intuitive return value for the function.

Python3 Compile Error

Error message:
FE..
************ MISSING LINE ***************
file 1 line number: 2
missing line in file 2, no match for file 1 line:
'=================== ROUND 1 ===================\n'
*****************************************
F..
======================================================================
ERROR: test_combat_round (__main__.TestRPG)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_rpg.py", line 64, in test_combat_round
self.assertIsNone(combat_round(self.rich, self.thompson))
File "/Users/ajsmitty12/Desktop/ISTA130/rpg.py", line 124, in combat_round
player1.attack(player1, player2)
AttributeError: 'int' object has no attribute 'attack'
======================================================================
FAIL: test_attack (__main__.TestRPG)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_rpg.py", line 42, in test_attack
self.assertEqual(out, buf.getvalue())
AssertionError: 'Rich attacks Thompson!\n\tHits for 5 hit points!\n\tThompson h[24 chars]g.\n' != 'Rich attacks Thompson (HP: 10)!\n\tHits for 5 hit points!\n\tT[33 chars]g.\n'
- Rich attacks Thompson!
+ Rich attacks Thompson (HP: 10)!
? +++++++++
Hits for 5 hit points!
Thompson has 5 hit points remaining.
======================================================================
FAIL: test_main (__main__.TestRPG)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_rpg.py", line 117, in test_main
self.assertTrue(compare_files('rpg_main_out_correct.txt', out))
AssertionError: False is not true
----------------------------------------------------------------------
Ran 7 tests in 0.006s
FAILED (failures=2, errors=1)
Correctness score = 57.14285714285714%
Description:
-This program will simulate combate for a simple Role Playing Game (RPG).
- The game has a single type of character, the Fighter.
-Each inidividual Fighter has an integer value caled "hit points" that represents his/her current health (the amount of damage he/she can sustain before death)
-It will simulate combat between 2 Fighers.
-Combat is divided into rounds:
-During each round each combatant will make 1 attempt to stike the other.
-We will use random numbers to simulate rolling dice, the higher number attacks first, and the lower number goes 2nd if still alive.
-If there is a tie, the players will go at the same time, they will attack eachother at the same moment.
-During a simultaneous attack both Fighters will get to attack even if one (or both) if (are) killed during that round.
-The progam will use a random number to determine whether an attack attempt is successful or not.
-Each successful attack will inflict damage on the opponent.
-To simulate damage, we'll reduce the opponent's hit points by another random number
-When a Fighters hit points are reduced to 0 (or less than 0) that player is considered to be dead.
-Combat round continue until one(or both) combatants are dead.
'''
I am having a hard time figuring out how to call my instances for Fighter that I created within the class too my combat function, that is outside of the class.
I am also wondering if I am comparing player1 and player2's random numbers correctly. When messing around with it I tried using:
player1 = Fighter()
player2 = Fighter()
When doing this my error message would said that Fighter() > Fighter() are not comparable.
Lastly, not quite sure why my test_attack is failing.
My code (so far):
import random
class Fighter:
def __init__(self, name):
'''
-This is my initializer method, which takes 2 parameters:
-Self
-Name (a string, the name of a fighter)
-This method will set a name attribute to the value of the name parameter
-It will also set a hit_points attribute to 10 (all fighters begin life with 10 hit points).
'''
self.name = name
self.hit_points = 10
def __repr__(self):
'''
-This method takes 1 parameter, self.
-It returns a string showing the name and hit points of the instances in the following format:
Bonzo (HP: 9)
'''
result = str(self.name) + " (HP: " + str(self.hit_points) + ")"
return result
def take_damage(self, damage_amount):
'''
-This method takes 2 parameters:
-self (the Fighter instance that calls the method)
-damage_amount (an integer representing the number of hit points of damage that have just been inflicted on this Fighter):
-The method should first decrease the hit_points attribute by the damage_amount.
-Next, it should check to see if the Fighter has died from the damage:
-A hit_points value that is 0 or less indicates death, and will print a message like the following:
\tAlas, Bonzo has fallen!
-Otherwise, it will print a message like the following (in this example, the player had 5 hit points left over after damage)
\tBonzo has 5 hit points remaining.
-The method returns nothing.
'''
self.hit_points = self.hit_points - damage_amount
if self.hit_points <= 0:
print('\t' + 'Alas, ' + str(self.name) + ' has fallen!')
else:
print('\t' + str(self.name) + ' has ' + str(self.hit_points) + ' hit points remaining.')
def attack(self, other):
'''
-This method takes 2 parameters:
-self
-other (another Fighter instance being attacked by self)
-The method will print the name of the attacker and attacked in the following format:
Bonzo attacks Chubs!
-Next, determine whether the attack hits by generating a random integer between 1 and 20
-Use the randrange function from the random module to get this number.
-A number that is 12 or higher indicates a hit:
- For an attack that hits, generate a random number between 1 and 6(using random.randrange) to represent the amount of damage inflicted by the attack
***Do NOT use the from random import randrange sytanze (randrange works like range, not like randint (think about the upper bounds you choose)).
- Print the amount of damage inflicted like the following:
\tHits for 4 hit points!
-Invoke the take_damage method on the victim (i.e. on other), passing it the amount of damage inflicted.
-For an attack that misses, print the following message:
\tMisses!
-This method returns nothing.
'''
self.other = Fighter(self.name)
print(str(self.name) + ' attacks ' + str(other) + '!')
attack = random.randrange(1, 20)
if attack >= 12:
damage_amount = random.randrange(1, 6)
print('\t' + 'Hits for ' + str(damage_amount) + ' hit points!')
other.take_damage(damage_amount)
else:
print('\t' + 'Misses!')
def is_alive(self):
'''
-This method takes 1 parameter, self.
-It returns True if self has a positive number of points, False otherwise.
'''
if self.hit_points > 0:
return True
else:
return False
def combat_round(player1, player2):
'''
-This function takes 2 parameters:
-The 1st is an instance of Fighter.
-The 2nd is another instance of Fighter.
-It determines which of the 2 fighters attacks 1st for the round by generating a random interger (use randrange) between 1 and 6 for each fighter:
-if the numbers are equal:
-print the following message:
Simultaneous!
-Have each figher instance call his attack method on the other figher (the 1st figher in the argument list must attack first to make the test work correctly)
-if one number is larger:
-the fighter with the larger roll attacks 1st (by calling his attack method and passing it the figher being attacked).
-if the 2nd fighter survives the attack (call is_alive), he then attack the 1st.
-This function returns nothing.
'''
Fighter.name = player1
Fighter.name = player2
player1 = random.randrange(1, 6)
player2 = random.randrange(1, 6)
if player1 == player2:
print('Simultaneous!')
player1.attack(player2)
if player1 > player2:
player1.attack(player2)
if player2.is_alive() == True:
player2.attack(player1)
if player2 > player1:
player2.attack(player1)
if player1.is_alive() == True:
player1.attack(player2)
You class looks okay, but the combat_round function has some problems.
You were on the right track with player1 = Fighter() etc..
player1 = Fighter(name = 'Jeff')
player2 = Fighter(name = 'Bill')
But now you want to choose one to go first. You cant just compare them with >, <, == without customizing special methods like __eq__() because Python doesn't know which attribute to compare. How about setting an attribute then comparing it:
player1.speed = random.randrange(1,6)
player2.speed = random.randrange(1,6)
Now you can write tests like this:
if player1.speed == player2.speed:
print("Simultaneous!")
Also, if the function combat_round() takes instances of fighter, then you dont need to instantiate them inside the function. But, if the function takes strings that are the names of the fighters then you would do it like my example.

Resources