Python - Simple While loop with IF statements - python

I am trying to make a script to check if an argument was entered. If an argument was entered it uses that number for the timer, if no argument was entered, then by default, the timer should equal 3.
This while loop will count down from the given number & print each value as it counts down. When it gets to the end it will output 'blast off!'
#!/usr/bin/env python3
import sys
timer = int(sys.argv[1])
while timer != 0:
if len(sys.argv[1]) == 0:
timer = 3
elif len(sys.argv[1]) == int(sys.argv[1]):
timer = int(sys.argv[1])
print (timer)
timer = timer - 1
print ('blast off!')
My homework checking script gives me an error of IndexError: list index out of range - related to the first timer = int(sys.argv[1])
I am not sure how exactly I am supposed to "convert an empty string into an integer"
Thank you

Check that you actually have an argument:
timer = int(sys.argv[1]) if len(sys.argv) >= 2 else 3

You are attempting to index into a position in the list that does not exist and getting an IndexError: list index out of range.
Lets call your file countdown.py.
When you run python3 countdown.py then sys.argv == ['countdown.py'].
If you ran python3 countdown.py 5 then sys.argv == ['countdown.py', 5]
Would recommend looking into the argparse library as this is the pythonic way to parse arguments from the command line.
For example your code would look like this:
#!/usr/bin/env python3
import argparse
def countdown(timer):
[print(tick) for tick in reversed(range(1, timer+1))]
print('blast off!')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Countdown to blast off!')
parser.add_argument('timer', type=int, nargs='?', default=3)
args = parser.parse_args()
countdown(args.timer)

I'm guessing that your script is called without an argument which will fail because len(sys.argv) == 0.
If the user doesn't specify a value then set timer to whatever.
import sys
##################
if len(sys.argv) > 1:
timer = int(sys.argv[1])
else:
timer = 0
##################
while timer != 0:
if len(sys.argv[1]) == 0:
timer = 3
elif len(sys.argv[1]) == int(sys.argv[1]):
timer = int(sys.argv[1])
print (timer)
timer = timer - 1
print ('blast off!')
Or even better:
try:
timer = int(sys.argv[1])
except:
timer = 0

Related

math quiz with a time limit (simultaneous functions) - advanced python

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
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)
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

Running one function without stopping another

How can I run a timer while asking for user input from the console? I was reading about multiprocessing, and I tried to use this answer: Python: Executing multiple functions simultaneously. When I tried to get it going, it gave me a bunch of framework errors.
Right now it runs start_timer(), but then stops it when it runs cut_wire().
Here's my start_timer function:
def start_timer():
global timer
timer = 10
while timer > 0:
time.sleep(1)
timer -= 1
sys.stdout.write ("There's only %i seconds left. Good luck. \r" % (timer))
sys.stdout.flush()
cut_wire()
if timer == 0:
print("Boom!")
sys.exit()
and this is the cut_wire function:
def cut_wire():
wire_choice = raw_input("\n> ")
if wire_choice == "cut wire" or wire_choice == "Cut Wire":
stop_timer()
else:
print("Boom!")
sys.exit()
Of course it stops running when it plays the cut_wire function because "raw_input" command reads the text and wait for the user to put the text and press enter.
My suggestion is to check for they key press "Enter" and when then key was press, read the line. If the key wasn't press, just continue with your timer.
Regards.
Instead of using raw_input() use this function taken from here.
def readInput( caption, timeout = 1):
start_time = time.time()
sys.stdout.write('\n%s:'%(caption));
input = ''
while True:
if msvcrt.kbhit():
chr = msvcrt.getche()
if ord(chr) == 13: # enter_key
break
elif ord(chr) >= 32: #space_char
input += chr
if len(input) == 0 and (time.time() - start_time) > timeout:
break
print '' # needed to move to next line
if len(input) > 0:
return input
else:
return ""
Thearding option
To make sure that both functions run completely simultaneously you can use this example of threading event:
import threading
event = threading.Event()
th = theading.Thread(target=start_timer, args=(event, ))
th1 = theading.Thread(target=cut_wire, args=(event, ))
th.start()
th1.start()
th.join()
th1.join()
In your function you can set an event using event.set(), check it using event.is_set() and clear it using event.clear().
Only addressing your concerns, here is a quick fix using threading :
import time
import sys
import os
def start_timer():
global timer
timer = 10
while timer > 0:
time.sleep(1)
timer -= 1
sys.stdout.write ("There's only %i seconds left. Good luck. \r" % (timer))
sys.stdout.flush()
#cut_wire() ==> separate call is easier to handle
if timer == 0:
print("Boom!")
os._exit(0) #sys.exit() only exits thread
def cut_wire():
wire_choice = raw_input("\n> ")
if wire_choice == "cut wire" or wire_choice == "Cut Wire":
stop_timer()
else:
print("Boom!")
os._exit(0) #same reason
if __name__ == '__main__':
import threading
looper = threading.Thread(target=start_timer)
looper.start()
cut_wire()

How do I have numbers increment slowly over a course of time throughout runtime

I am trying to make a text based game in which the user is a pilot in space. I want to create a movement system but am unsure how to do it. I want the user to be able to put in the desired grid coordinates, and his vehicle will begin to change its grid coords to get closer and closer to the ones he inputted.
Now, to do this I will probably need multithreading and a time element. But I am unsure how I can use a time element. Any advice is greatly appreciate, i'm just trying to learn here. Thanks guys!
from Gundam2 import Mobilesuits
#Main Variable/Object declarations:
Leo1=Mobilesuits(100,100,"Leo","leo desc","dockpit desc",100,[100,100,100])
Leo2=Mobilesuits(100,100,"Leo","leo desc","dockpit desc",100,[300,100,100])
Leo3=Mobilesuits(100,100,"Leo","leo desc","dockpit desc",100,[100,150,100])
currentmobilesuit=Leo1
#Main Function declarations
def commands(user_input,currentmobilesuit):
if user_input == "radar":
currentmobilesuit.radar()
elif user_input == "commands":
print("Command list:\nradar")
else:
print("Invalid command\nType 'commands' for a list of valid commands")
#Main execution
while True:
commands(raw_input(),currentmobilesuit)
class Mobilesuits:
#class global variables/methods here
instances = [] #grid cords here
def __init__(self,armor,speed,name,description,cockpit_description,\
radar_range, coordinates):
Mobilesuits.instances.append(self)
self.armor=armor
self.speed=speed
self.name=name
self.description=description
self.cockpit_description=cockpit_description
self.radar_range=radar_range
self.coordinates=coordinates
def can_detect(self, other):
for own_coord, other_coord in zip(self.coordinates, other.coordinates):
if abs(own_coord - other_coord) > self.radar_range:
return False
return True
def radar(self):
for other in Mobilesuits.instances:
if other is not self and self.can_detect(other):
print "%s detected at %s" % (other.description, other.coordinates)
Games typically have a "master loop" of some kind; yours does here:
#Main execution
while True:
commands(raw_input(),currentmobilesuit)
The simplest thing to do is to count in the loop:
#Main execution
turn_count = 0
while True:
commands(raw_input(),currentmobilesuit)
turn_count += 1
If you wanted the real time taken to have some impact on the counter, or be the counter, you can get the current time from the time module calling time.time().
#Main execution
import time
time_start = time.time()
time_elapsed = 0
while True:
commands(raw_input(),currentmobilesuit)
time_elapsed = time.time() - time_start
A couple other thoughts:
Make a Game class, and put the turn counter and game loop in that.
Have the commands function return a number that is the number of time units that took place during the command; for example, entering an invalid command might take 0 turns, while repairing a robot might take 5.
#Main execution
turn_count = 0
while True:
turns_taken = commands(raw_input(),currentmobilesuit)
turn_count += turns_taken
You can use non-blocking I/O. This will help you avoid the complications of threading. Here's your sample code implemented with a non-blocking read of stdin:
#!/usr/bin/python
import sys
import select
call_count = 0
#Main Function declarations
def commands(user_input):
global call_count
if len(user_input) > 0:
print('call count: ' + str(call_count) + ' user entered: ' + user_input)
def raw_input_no_block():
global call_count
call_count = call_count + 1
input_avail = select.select([sys.stdin], [], [], 0.1)[0] #wait for 0.1 seconds
if input_avail:
return sys.stdin.readline()
else:
return ''
#Main execution
while True:
commands(raw_input_no_block())

Performing an action as python script closes

I was wondering if it was possible to perform an action at any given point in a basic python script, so say when it is close. I have the following code to find prime numbers (Just for fun)
number = 1
primelist = []
nonprime = []
while number < 1000:
number += 1
for i in range(number):
if i != 1 and i != number and i !=0:
if number%i == 0:
nonprime.append(number)
else:
primelist.append(number)
nonprimes = open("nonprimes.txt", "w")
for nonprime in set(primelist) & set(nonprime):
nonprimes.write(str(nonprime) + ", ")
nonprimes.close()
So basically i wanted to run the last part as the script is stopped. If this isn't possible is there a way where say i press "space" while the program is running and then it saves the list?
Cheers in advance :)
EDIT:
I've modified the code to include the atexit module as suggested, but it doesn't appear to be working. Here it is:
import time, atexit
class primes():
def __init__(self):
self.work(1)
def work(self, number):
number = 1
self.primelist = []
self.nonprime = []
while number < 20:
time.sleep(0.1)
print "Done"
number += 1
for i in range(number):
if i != 1 and i != number and i !=0:
if number%i == 0:
self.nonprime.append(number)
else:
self.primelist.append(number)
nonprimes = open("nonprimes.txt", "w")
for nonprime in set(self.primelist) & set(self.nonprime):
nonprimes.write(str(nonprime) + ", ")
nonprimes.close()
def exiting(self, primelist, nonprimelist):
primelist = self.primelist
nonprimelist = self.nonprime
nonprimes = open("nonprimes.txt", "w")
for nonprime in set(self.primelist) & set(self.nonprime):
nonprimes.write(str(nonprime) + ", ")
nonprimes.close()
atexit.register(exiting)
if __name__ == "__main__":
primes()
While I'm pretty certain the file object does cleanup and flushes the stuff to file when it is reclaimed. The best way to go about this is to use a with statement.
with open("nonprimes.txt", "w") as nonprimes:
for nonprime in set(primelist) & set(nonprime):
nonprimes.write(str(nonprime) + ", ")
The boiler plate code of closing the file and such is performed automatically when the statement ends.
Python has an atexit module that allows you to register code you want executed when a script exits:
import atexit, sys
def doSomethingAtExit():
print "Doing something on exit"
atexit.register(doSomethingAtExit)
if __name__ == "__main__":
sys.exit(1)
print "This won't get called"

A timer to the infinity

I'm working on a script, which will print a timer and was trying to reuse a function like this:
def timer(m,x):
for i in range(1,x):
sys.stdout.write('\r%s\b%d' % (m,i))
sys.stdout.flush()
sleep(1)
sys.stdout.write('\r \b')
Now, the part of the script, where I want a timer to be displayed is like this:
host_alive = "ping -c1 myServer"
cmdStat, cmdOut = commands.getstatusoutput(host_alive)
while True:
if cmdStat != 0:
(cmdStat,cmdOut) = commands.getstatusoutput(host_alive)
print "Still NOT ready!!"
else:
break
How can I print a timer without specifying a range()? Is there any workaround?
Cheers!!
def timer(m):
i = 0
while True:
sys.stdout.write('\r%s\b%d' % (m,i))
sys.stdout.flush()
sleep(1)
sys.stdout.write('\r \b')
i = i + 1
First of all commands module is deprecated and replaced by subprocess module.
Secondly, to represent infinity in python you can use float('inf'). Its symbolic that you are referring to infinity.
def timer(m):
i = 0
while i<float('inf'): #this is symbolic and in essence similar to while True
sys.stdout.write('\r%s\b%d' % (m,i))
sys.stdout.flush()
sleep(1)
sys.stdout.write('\r \b')
i += 1
A while loop would probably be a better option here. Ex:
def timer(max):
while counter > 0:
print '%d seconds remain' % max
sleep(1)
max -= 1

Categories

Resources