In my program I want to interact with user and ask him to press specific letters to do some things (I think that logic of this game is not relevant for my question). When I start game and as first letter I press 'q' program exit immediately, but when I play for a while (use few times 'g' and 'r') I have to press few times 'q' too before I could exit program (Every time I'm getting the same prompt as on the beginnig of the game "Enter g to start ... ")
I'm using Canopy and Python 2.7.
t_h = ''
def pg(wl):
global t_h
result = raw_input("Enter g to start new game, r to replay last game, or q to end game: ")
possible_choices = ["g", "r", "q"]
if result in possible_choices:
if result == 'g':
t_h = dh(n)
ph(t_h, wl, n)
if result == 'r':
if t_h == '':
print 'You have not played a game yet. Please play a new game first!'
else:
ph(t_h, wl, n)
if result == 'q':
return None
else:
print "Invalid letter."
return pg(wl)
The function pg calls itself recursively on any non possible_choice case (since only q returns directly) -- that is, on the return pg(wl) line.
The situation you describe implies that either one of, or both ph and dh are calling pg again.
This means that for every non q input you will have a prompt for a new question on the stack from ph (or ph and dh) and one from the recursive call to pg. This will cause the exact behaviour you describe, where one q wont suffice to exit. With the code you posted -- that is, without dh and ph -- it is not possible to know precisely, but this is the logical conclusion.
If you want the possibility to exit immediately you would have to use a simple infinite loop with break in case of q instead of recursion.
Another possibility is following #PauloScardine's idea of using exit(), if what you want is really to exit the whole process. Again, with the piece of code you posted, it is not possible to know whether this is possible (pg being called directly from a main function).
It's difficult to tell without seeing more of your code (specifically code for dh and ph), but I'd guess that pg is being called from one of those functions, or some other function in your code.
Related
I am making a small text-based game in Python. It involves many inputs and so to avoid bugs, there are a few things I have to check every time an input exists. Naturally, to speed up the process I wanted to put the code into a def in order to simplify the writing process. When I put the code in the def, it red underlines the continue and break commands (meaning they are incorrect), and if you run the code using the def name, a Traceback occurs. I have tried putting the def section at the beginning of the program, after the while True: (The program is supposed to run infinitely until a certain action is taken that breaks the loop) I have also made sure to try putting it under any variables referenced and in the loop so that no part of it is not defined and so that everything would work if I were to just put the code in there.
Here is the code I am trying to put into a def.
def input_bugs():
if letter_one.lower() == "done" and total_count == 0:
print("You have to play at least one game!")
continue
elif letter_one.lower() == "done":
break
elif len(letter_one) > 1:
print("Sorry, you gotta pick a single letter, no more. Otherwise, type 'done' to end the game and see your stats.")
continue
Here is the Traceback I get every time I try to run it.
line 20
continue
^^^^^^^^
SyntaxError: 'continue' not properly in loop
At this point, I don't even care if I have to write it out every time, I can just copy and paste. I am simply curious as to why it doesn't work so that I know in the future. In case you could not tell, I am pretty new to programming so I want to learn from any mistake I make. Also sorry if I referred to some things in the wrong way. Hopefully, you understood what I meant.
I am using Python 3.6.4 on a Windows 7 system (I have other systems like Win 10 and Android but this is my starting point).
INKEY$, for those not familiar with BASIC (pretty much any flavor), is a function that checks the keyboard buffer, returning that data as a string or a null/empty value ("") if there was no data, and clears the buffer. The length of the returned string depends on the data in the buffer, normally 0, 1, or 2 on a single keystroke (a fast typist could fill the small buffer between checks in the old days). The Enter key was not needed (unless that was what you were looking for) or processed and the program did not pause unless programmed to do so.
Pauser:
a$=""
while a$=""
a$=inkey$
wend
Flow Interrupter:
a=0
while a < 1000
a=a+1
print a
a$=inkey$
if a$<>"" then exit while
wend
Quick parser:
a$=inkey$
if a$<>"" then
rem process input
rem like arrow keys/a w s z for directional movement
rem useful for games and custom editors
end if
I am wanting to know if Python has a simple cross platform function (ie not 10+ lines of code unless in an importable module/class) that is the equivalent to the INKEY$ function? Also, I am not wanting to import the gaming module(s), just want an INKEY$ function equivalent (simple, straight forth, small).
import inkey
a = inkey.inkey()
Update #1:
After I installed the readchar module and corrected a reported error by Python (stdout.write(a) needs to be stdout.write(str(a)) as the variable 'a' appears to be returned as a byte string from readchar() function) when using the code listed by Mr. Stratton below, I only get continuous stream of b'\xff' and console echoed characters if there where any keypresses.
Stripping it down to use only the function doesn't help either:
from readchar import readchar
from sys import stdout
import os
#I like clear screens, and I can not lie
os.system('cls') # For Windows
#os.system('clear') # For Linux/OS X
def inkey():
"INKEY$ function"
return readchar()
#let the processing hit the floor, elsewhere
b=0
step = 1
while b < 1000:
b = b + step
print(b)
#convert bytes to integers
a = int.from_bytes(inkey(), "big")
#remember I keep getting b'\xff' (255) when buffer is empty
if chr(a) == "a":
step = step - 1
a = 255 #don't stop
if chr(a) == "l":
step = step + 1
a = 255 #don't stop
if a != 255:
break
It is supposed to count b from 0 to 999, stopping on almost any keypress, 'a' decreases the step, 'l' increases it. Instead, it prints the keypress either before or after the value of b depending on timing and continues until b = 1000. Nothing I did made a difference.
While the Pauser function can be replaced with an input() (i = input("Press Enter key to continue")) the other two variants can't be changed so easily it seems.
The closest to what you’re looking for is probably the readchar library.
Here’s an example that resembles the old BASIC logic:
from readchar import readchar
from sys import stdout
a = ' '
while ord(a) not in [3,24,27]:
a = readchar()
stdout.write(a)
stdout.flush()
if ord(a) == 13:
stdout.write("\n")
Those numbers that break the loop are CTRL-C, CTRL-X, and ESC respectively. The number 13 is the carriage return; the example writes a line feed following each carriage return to avoid overwriting text.
The equivalant to the old ASC(A$) in BASIC is ord(a) in Python. (And CHR$(A$) is chr(a).)
Note that this will block on reading. If no keypress is waiting, Python will stop on the line a = readchar() until a key is pressed. To get the full effect of BASIC’s INKEY$, you’ll need to verify that some data is waiting before reading it. You can do this using Python’s select library.
from readchar import readchar
from sys import stdin, stdout
from select import select
def inkey():
if select([stdin,],[],[],0.0)[0]:
return readchar()
return ''
a = ''
timer = 0
while a != 'Q':
a = inkey()
if a != '':
stdout.write(a)
stdout.flush()
if ord(a) == 13:
stdout.write("\n")
timer += 1
if timer > 1000000:
print("Type faster, human!")
timer = 0
This defines an inkey function and returns the empty string if nothing is waiting. If something is reading, it returns readchar(). Every 1,000,000 times through the loop, it tells the human on the other end to type faster.
In this version, it quits on a capital letter “Q”, as this does not block CTRL-C, CTRL-X, and ESC from breaking out of the program altogether.
This may have trouble if you’re using Windows, as select.select at least at one time did not work on Windows. I have no means of testing that, however.
You may also want to look at pynput if you don’t need it to look exactly like BASIC. The canonical means to do this in Python is probably to set up an event that calls a function or method. Your script goes on doing its thing, and if that function (or method) is invoked, it cancels or modifies the action of the main loop.
I would like to know why this code does not work; it should exit at the "GAME OVER" point, but it continues to my next defined function.
I have tried other variations on exit() such as: sys.exit(), quit() and SystemExit.
run_attack = input("What do you do: Run/Attack\n")
run = ['run', 'Run', 'RUN']
attack = ['attack', 'Attack', 'ATTACK']
run_attack = 1
while run_attack < 10:
if run_attack == ("run") or ("Run") or ("RUN"):
print ("You turn to run from the wolf but he quickly pounces
you...")
time.sleep(2)
print("You are quickly ripped apart and just about get to see
yourself be eaten.")
print("GAME OVER")
break
exit() #This is where the game should exit, yet after input it
continues to the next function
elif run_attack == ("attack") or ("Attack") or ("ATTACK"):
print("You brace yourself for a bite and have no time to reach"
"for any kind of weapon form your backpack.")
time.sleep("2")
input("You clock the dog hard, twice on the muzzle.")
print("The dog recoils in pain and retreats back to the woods.")
print("You quickly start running as you assume there will be a den in the woods.")
break
else:
input("Type Run or Attack...")
You have several problems in your code; why did you write this much without testing it?
First, you read the user's input, immediately replace is with 1, and then try to test it (incorrectly) as if it were still a string. Your posted code has several syntax errors, so I have some trouble reproducing the problem. However, the immediately obvious problem is here:
break
exit() # This is where ...
You can't get to the exit statement, as you break from the loop just before you can get there.
I strongly recommend that you back up to a few lines and use incremental programming: write a few lines at a time, debug those, and don't continue until they do what you want.
Also look up how to test a variable against various values. Your if statement is incorrect. Instead, try the list inclusion you're trying to set up:
if run_attack in run:
...
elif run_attack in attack:
...
I took the liberty of rewriting your whole program to show you a few things wrong with it and a few tricks. I've done it without the loop, since you never use it anyway... you can add the while loop later once you've mastered it, but you should really go back to basics on some things here:
run_attack = input("What do you do: Run/Attack\n")
if run_attack.lower() == "run":
print("""some
stuff
with
multiple
lines and GAME OVER""")
exit()
elif run_attack in ("attack", "Attack", "ATTACK"):
print("""some
stuff
with
multiple
lines""")
else:
input("Type Run or Attack...")
Some notes:
Using """ for strings enables you to write multiple lines without multiple print statements
Using str.lower() on strings makes everything easy to compare because you only have to compare it to the lowercase version of each string. However for attack you can notice I used a different inclusion test, without multiple conditions. Either way works here.
Like the other answer here (and many comments), you should use only exit() to leave the program entirely, or only break to exit the loop and continue to other code that's beneath the entire loop.
When you rewrite your loop, with a condition like while number_of_turns < 10 don't forget to add 1 to the number of turns on each loop, otherwise that condition is always True and you'll have an infinite loop...
I'm actually quite surprised this code had any resemblance to the behavior you expected from it, my suggestion is to go back over to the basics of python, learn loops, string methods, basic commands. The rest is already said in the other answer here (which is better than mine, frankly) just wanted to add some ideas.
I thoroughly searched for an answer to my question but couldn't find anything that would explain my results. I truly hope that anyone of you can point me in the right direction.
At the moment I am trying to program a text-based adventure game using Python 3 in order to better understand the language.
While doing so I created a function that should ask the user for input and print a specific statement depending on the users input. In case the users input is invalid the function should then keep asking for input until it is valid.
Unfortunately the function only seems to keep asking for input, without ever executing the if/elif statements within the function. Due to no errors being shown I am currently at a loss as to why this is the case...
print("If You want to start the game, please enter 'start'." + "\n" +
"Otherwise please enter 'quit' in order to quit the game.")
startGame = True
def StartGame_int(answer):
if answer.lower() == "start":
startGame = False
return "Welcome to Vahlderia!"
elif answer.lower() == "quit":
startGame = False
return "Thank You for playing Vahlderia!" + "\n" + "You can now close
the window."
else:
return "Please enter either 'r' to start or 'q' to quit the game."
def StartGame():
answ = input("- ")
StartGame_int(answ)
while startGame == True:
StartGame()
You fell into the scoping trap: you are creating a new variable startGame inside the function that is discarded after you leave it. You would instead need to modify the global one:
def StartGame_int(answer):
global startGame # you need to specify that you want to modify the global var
# not create a same-named var in this scope
# rest of your code
This other SO questions might be of interest:
Python scoping rules
Asking the user for input until they give a valid response
Use of global keyword
and my all time favorite:
How to debug small programs (#1) so you enable yourself to debug your own code.
The last one will help you figure out why your texts that you return are not printed and why the if does not work on 'r' or 'q' and whatever other problems you stumble into. It will also show you that your if are indeed executed ;o)
Other great things to read for your text adventure to avoid other beginner traps:
How to copy or clone a list
How to parse a string to float or int
How to randomly select an item from a list
First off, forgive me, because I'm incredibly new at this Python thing (I'm really an HTML/CSS kind of guy, but I'm trying to branch out). This is probably an elementary sort of question, but we all have to start somewhere, right?
I'm putting together a very basic Python program that will select a random letter from a string of letters and print it out every time someone hits the any key. The whole thing is pretty simple, and currently returns a random letter, but doesn't wait for a keypress to do so, and stops after completing the function runs once.
import random
letterlist = 'abcd'
def random_letter(letters):
print ('Press enter for a random letter...')
print random.choice(letters)
random_letter(letterlist)
Output should look like this:
Press enter for a random letter.
'b'
Press enter for a random letter.
(and so on...)
It's clear that whatever I need to do ought to fall inside of random_letter someplace. I've been googling around and have found lots of references to raw_input and mvscrt, but I'm not entirely sure what I need. It's entirely possible that I'm just asking the question wrong.
I'm assuming assuming that I need some sort of loop going on to keep this running indefinitely.
Thanks in advance!
For starters, you need a loop somewhere to continue prompting the user. You also need some sort of exit condition for the loop. This loop can be inside the function like so:
def random_letter(letters):
while True:
x = raw_input('Press enter for a random letter...')
if x == 'done':
break
print random.choice(letters)
random_letter('abcdef')
Notice that inside the loop we use raw_input to prompt the user to type something, anything, then press enter. When the user types done and hits enter, we break out of the loop using break.
An alternative would be to wrap your existing function in a loop and take care of the prompting outside the function.
def random_letter(letters):
print random.choice(letters)
while True:
x = raw_input('Press enter for a random letter...')
if x == 'done':
break
random_letter('abcdef')
If you don't have to use a loop, and thinking of capturing keyboard events, then there are no cross-platform ways of doing it: Is there a cross-platform python low-level API to capture or generate keyboard events?
For Windows, there is pyHook: http://pyhook.wiki.sourceforge.net/
You can look into the code of pyKeyLogger: http://pykeylogger.wiki.sourceforge.net/
Or a dirty way, capture interrupts: Capture keyboardinterrupt in Python without try-except