I have followed a tutorial on making a simple text based game in Python. I'm going to use what I learned from it to make a post-apocalyptic text adventure. Everything works, but I don't really want to use just the command console as the game. Instead, I want to use a window, which I know can be done with Tkinter. I just don't know how.
What I'm asking is if there's a way to add a GUI or window to my existing functions. The code is below:
#A simple text-based game test
global table
table=0
def start():
print 'Welcome'
global gold
gold=0
lobby()
def lobby():
print 'You are in the lobby.'
command=prompt()
if command=='north':
bedroom()
elif command=='gold':
currentGold()
lobby()
elif command=='end':
return
else:
lobby()
def prompt():
x=raw_input('Type a command: ')
return x
def currentGold():
global gold
print 'current gold: ', gold
def bedroom():
global gold, table
print 'You are in the bedroom'
command=prompt()
if command=='south':
lobby()
elif command=='bed':
print 'You return to your bed and find nothing'
bedroom()
elif command=='table':
if table==0:
print 'You go to the table and find 50 gold'
gold=gold+50
table=1
bedroom()
else:
print 'There is nothing else on the table'
bedroom()
elif command=='gold':
currentGold()
bedroom()
elif command=='end':
return
else:
bedroom()
start()
Basically, you start in a lobby, and then explore a bedroom (not really, it's just a simple test). I'd appreciate anyone's help or input.
In general, a GUI program has to be rewritten as an event loop, rather than just a sequence of code.
For example, if you write a function which just waits forever for input, then the entire GUI is waiting forever for input, which means you can't respond to mouse drags or anything else, and your window manager will display a beachball or pop up a "dead program" dialog or in some other way alert the user that your program is "frozen".
Instead, you have to write a function which just puts up an input dialog, attaches a handler or callback that gets run when the input comes in to the dialog, and then returns.
So, code that looks like this:
def lobby():
print 'You are in the lobby'
command=prompt()
if command == 'north':
bedroom()
elif command=='gold':
currentGold()
lobby()
elif command=='end':
return
else:
lobby()
… has to be split in half, like this:
def lobby():
display_text('You are in the lobby')
prompt_window = PromptWindow(handler = lobby_handler)
prompt_window.show()
def lobby_handler(command):
if command == 'north':
bedroom()
elif command=='gold':
currentGold()
lobby()
elif command=='end':
return
else:
lobby()
If this doesn't make sense, you probably want to follow some tutorials to build some simple GUI apps first, and only then come back to converting your existing program into a GUI app.
Just splitting functions in half is the quick & dirty way to turn sequential code into event-based code, but it isn't always the best. It's a great way to end up in "callback hell".
For example, what if currentGold is popping up a dialog and waiting for the user to click it, and we don't want to go back to lobby until they click it? The only way to make this work is for lobby_handler to pass lobby to currentGold, so currentGold can pass it to currentGoldHandler. And what if currentGold_handler needs to access local variables from currentGold? You have to define currentGold_handler locally so you can use it as a closure, or use functools.partial to bind them in. And so on. Before you know it, you've got code indented 60 characters, inconsistently using some callback-passing convention that you didn't design until you'd written 100 functions, 40 of which violate it in some subtle way.
Related
I'm taking an online-coure in Udemy
Doing my coffee-machine project, I made some functions like
def machine_input():
coffee_needs = input("What would you like?, espresso, latte or cappuccino\n").lower()
if coffee_needs == 'report':
print(f"The current resource values \n Water:{resources['water']} ml \n milk:{resources['milk']} \n coffee:{resources['coffee']}")
return False
coffee_machine()
else:
return coffee_needs
and
def coffee_machine():
while keep_working:
coffee_needs = machine_input()
check_resources(coffee_needs)
total_money = get_coins()
check_transaction(total_money, coffee_needs)
dimming_resources(coffee_needs)
if not check_transaction:
coffee_machine()
but in this case, two functions didn't work as I expected.
It show me an error when I input 'report' in machine_input().
I'd like to restart the function 'coffee_machine()' in 'machine_input()'
But now I'm thinking that I may not be able to inter-refer two functions.
Like coffee_machine in machine_input in coffee_machine in machine_input in coffee_machine in machine_input in....
Is it possible to do like this in python?
(I'm doing this with 3.10.2 ver)
It looks like you're doing unintended recursion. You're actually creating multiple separate coffee machines that spawn even more coffee machines. Unless it's supposed to be a weird coffee machine factory, it only seems that your code is doing what you think it is.
What I assume you mean by inter-referencing is just going back and forth between the two functions.
You don't need to explicitly go back from machine_input() by calling coffee_machine() there. Just return something to finish the function and you'll be back wherever you called it from. Otherwise you're spawning a totally new coffee machine, inside the first one.
You don't have to call coffee_machine() in your coffee_machine(). You're already in a while loop, so I'm guessing you wanted to continue by starting over on the same coffee machine. Otherwise you're creating a totally new coffee machine, again.
def machine_input():
coffee_needs = input("What would you like?, espresso, latte or cappuccino\n").lower()
if coffee_needs == 'off':
print('Turning off th machine')
return False
elif coffee_needs == 'report':
print(f"The current resource values \n Water:{resources['water']} ml \n milk:{resources['milk']} \n coffee:{resources['coffee']}")
return True
else:
return coffee_needs
def coffee_machine():
while keep_working:
coffee_needs = machine_input()
if not coffee_needs:
print('No coffee needs specified.')
continue
check_resources(coffee_needs)
if not check_resources:
print('Not enough resources.')
continue
total_money = get_coins()
check_transaction(total_money, coffee_needs)
dimming_resources(coffee_needs)
if not check_transaction:
print('Insufficient funds.')
continue
Consider tea.
You are basically trying to build a state machine, but the bad way (by cross-referencing functions).
To keep this simple, you could use a while loop with a global variable for the state ("working", "input" and "stop" here for example), which you modify accordingly in each of the state (= each function) and use it to select which function to run next.
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
I'd like to start out that I'm new to programming. I've never taken any classes on it. I just decided it sounded interesting and try it.
Anyway, I've been reading the "Learn Python the Hard Way" online and have gotten to exercise 36. This exercise involves making my own text-based game. Now, for the question. When is an appropriate time to use and modify global variables? I just started my adventure and want to add things that the player has to do before other events happen, such as pull a lever in a different room before the gate in the first room opens. And if the player wishes to return to the first room later on, the gate and lever still be triggered.
Here's the code so far. Mind you it's not fleshed out. Just wanted to know if it worked.
print "You are a lone adventurer with the bounty to clear out the crypt."
print "You come with nothing but your sword and a compass."
print "You enter the crypt."
print "To the north you have a gated portaculas and rooms to the west and east."
print "What do you do?"
gate = False
lever = False
def entrance():
global gate
while True:
choice = raw_input('> ')
if choice == 'west':
guard_shack()
elif choice == 'east':
lever_rm()
elif choice == 'north' and gate == False:
print "The gate is still closed and locked."
entrance()
elif choice == 'north' and gate == True:
fountain_rm()
else:
entrance(gate)
def lever_rm():
global lever
global gate
print "You enter the room."
print "What do you do"
while True:
choice = raw_input('> ')
if 'search' in choice:
print "You look around the room and notice a lever on the opposite wall."
elif "pull lever" in choice:
print "You pull the lever."
lever = True
gate = True
elif choice == 'west':
entrance()
else:
print '> '
def fountain_rm():
print "you're in the lever room!"
entrance()
Unfortunately, many tutorials (and professors) teach bad code in the name of simplicity (and they usually never bother to teach the right way later). In this case, the problem is exacerbated by the fact that you are directly executing top-level code instead of putting it in a main function and using if __name__ == '__main__': main() at the end.
You should try to avoid all global state that can be mutated. It's okay to declare constants, or even lists/sets/dicts that you're not allowed to change. But everything else should be either passed as a function parameter, or stored as an attribute on self of some class.
If nothing else, think about how you would write unit tests in the presence of mutable global variables (hint: it's impossible).
To transform code like you've given, indent everything and add a heading class CryptGame:, add self as the first argument to every function, and replace all variables declared global foo with self.foo.
I am creating a text-RPG taking inspiration from older text adventures where the player enters an English command; such as 'pick up sword' and the like.
I have established a simple; enter 'A' to do this and enter 'B' to do this, but I would like to expand my system for more freedom.
I need to create a system that; when the player types in a command the program picks out key words.
I assume this would be achievable via the 'in' command.
Here is my code:
print "What would you like to do??"
input_loop_sleep = str('')
choice_sleep = raw_input(str('>>>'))
loop_sleep = False
table_time = False
bed_time = False
error_time = False
while loop_sleep == False:
if str('sleep') in choice_sleep or str('bed') in choice_sleep or str('goodnight') in choice_sleep or str('Sleep') in choice_sleep or str('tired') in choice_sleep:
while bed_time == False:
print "you decide to go back to sleep"
time.sleep(1)
print "..."
time.sleep(1)
print ""
time.sleep(1)
print "darkness"
time.sleep(1)
print ""
print "you wake up..."
time.sleep(1)
print "it is now 9:15am"
time == int(9.15)
time.sleep(1)
print "You are standing in your room, slightly more refreshed."
time.sleep(1)
print "there is a table with some things on it, stairs, and a wardrobe... with the doors wide open..."
time.sleep(1)
print "that's strange... you swear that they were shut when you went to sleep..."
break
else:
bed_time == True
break
bed_loop_choice = raw_input('>>>')
elif str('bedside') in choice_sleep or str('table') in str(choice_sleep):
while table_time == False:
print "You rub your eyes and pick up some belongings from a"
print "bedside table."
time.sleep(1)
print "Map added!"
time.sleep(1)
print "100 gold added!"
time.sleep(1)
print "Leather Bag added!"
cash == int(100)
time.sleep(1)
Map == str('map of', str(province))
Inventory == [str(Map)]
container == str('leather bag')
print "your", str(container), str("contains a"), str(Map), str('and'), str(cash)
break
else:
table_time == True
break
else:
print "invalid command!"
when I run the code, no matter what I type in it always goes with the 'sleep' option.
I probably just made some simple mistake!
can you please help me with what I did wrong and how I can fix it.
To answer your question about why the sleep loop is repeated all the time:
You're controlling the loop via
while bed_time == False:
but you never set bed_time to True in your loop (only in the else clause, but that clause is only executed when the loop exits normally, not when it's exited via break, as you're now doing - therefore bed_time will never change).
Furthermore, direct comparisons to a boolean value are usually frowned upon. The idiomatic way (in most languages, not just Python) would be while not bedtime:.
You should probably read some beginners' programming books and/or the Python tutorial before embarking on such a big project. There are several issues in your code that convey the impression that you really need to get a grasp on some basic programming principles and Python idioms.
For example,
int(9.15)
is not a good way to store a time - the result will be 9.
You're then using time == int(9.15), which means "compare the module time to the integer 9". I guess you meant time = int(9.15) which is already bad for the reasons stated above, but there would be even another problem: You would be overwriting the module name time, which will cause the subsequent time.sleep(1) command to fail with an AttributeError.
There's no need for most str() calls in your code because you're using it on objects that already are strings. Where you're not, it's incorrect: str('map of', str(province)) will raise TypeError (str takes only one argument).
You're using uppercase variable names for objects that aren't class instances.
Etc., etc...
I think this should be sufficient to sort out the problem
In [1]: str('bed') in "bedside"
Out[1]: True
So when you write bedside it gets inside the sleep option if condition and hence you are getting wrong answer .
You should write :
if str('bed') == choice_sleep or *other conditions* :
then got inside the sleep option
P.S: I am assuming you have imported the time module .
P.P.S: I checked the code with entering table it is working fine .