I'm creating a text based adventure game and when the user's health gets to 0, I ask them if they want to restart the game. This works fine - however, I am duplicating my code a lot inside this while loop and want to know how to create a function which I can call to restart the game.
This is the code I use to restart:
if health <= 0:
print('You died. Do you want to restart? (y/n)')
ans = input().lower()
if ans == 'y':
continue
else:
break
There are a lot of ways you could do this, but this is one.
The following function will return True if the user enters 'yes', and False otherwise:
def continue_prompt():
typewriter('You now have 0 health and lose the game.')
print('\nDo you want to restart? (yes/no)')
return input().lower() == 'yes':
The below code will short circuit if health isn't 0, so the function will only be called in the event that the character has died. Then it will continue or break depending on the return value of continue_prompt.
if health <= 0 and continue_prompt():
continue
else:
break
You could also customize the prompt by having continue_prompt accept one or more arguments, if you wanted it to say different things for different causes of death, for instance.
You could use a custom exception and raise it when healths is 0. Then it's possible to move all the health affecting to a reusable function that handles death. Just call the function to change health instead of adding or subtracting
Define the exception:
class DeceaseException(Exception):
pass
And a function for handling health
def affect_health(gain):
global health
health += gain
if health <= 0:
raise DeceaseException
Wherever the health is affected you put for example:
# health decreases
affect_health(-10)
And in main function:
while True:
try:
# main game code
except DeceaseException:
print('You died. Do you want to restart? (y/n)')
ans = input().lower()
if ans == 'y':
continue
else:
break
I'm assuming this is just sitting inside of a while loop?
If so, one option could be
if not health:
typewriter('You now have 0 health and lose the game.')
restart = input('\nDo you want to restart? (yes/no)')
if restart.lower() == 'yes':
continue
Related
I am very new to using classes and creating functions within it. I have been trying to write a bit more elegant code to the point where the only line to be 'executed' is 1 line, 'o.pag' in this case.
However, my pag function is just not working out. To my eyes, though I've made several variations of the same and even tried to turn it into an series of 'if' statements, gets stuck on "Would you like to play again'?.
Here the problematic but:
def pag(self):
o.game()
o.q()
while p_again == 'yes':
o.game()
o.q(). <<<<<<THIS IS WANT WONT EXECUTE AFTER TYPING 'YES'
if p_again != 'yes':
break
Here is the complete code sample. I am wondering if I caused the problem myself by trying to put everything into functions. Any help would be greatly appreciated.
import random
a = random.randint(1, 9)
#a = 4
again = ''
y = 'yes'
n = 'no'
g= 0
p_again = ''
class olv(object):
def glogic(self):
if g > a:
print('You guessed too HIGH.')
elif g < a:
print('You guessed too LOW.')
else:
print(g)
def game(self):
global g
while g != a:
g = int(input('Guess a number between 1 and 9! '))
o.glogic()
if g == a:
print('Wow, you guessed it.')
def q(self):
global p_again
p_again = str(input('Would you like to play again? '))
def pag(self):
o.game()
o.q()
while p_again == 'yes':
o.game()
o.q()
if p_again != 'yes':
break
o = olv()
o.pag()
you need to put in inside function not out side the function.
code should look like this.
def pag(self):
o.game()
o.q()
while p_again == 'yes':
o.game()
o.q(). <<<<<<THIS IS WANT WONT EXECUTE AFTER TYPING 'YES'
if p_again != 'yes':
break
I believed that this guessing game can run without using classes. From your code, it shows that your basics towards classes is not familiar enough.
You can try to make this guessing game without using classes first. Then you can try to implement the game using classes.
Here is an example of using classes for this game:
import random
p_again = ""
class olv:
def __init__(self):
self.answer = random.randint(1, 9)
self.guess = 0
def game(self):
while self.guess != self.answer:
self.guess = int(input('Guess a number between 1 and 9: '))
if self.guess > self.answer:
print('You guessed too HIGH.')
elif self.guess < self.answer:
print('You guessed too LOW.')
elif self.guess == self.answer:
print("Wow, you guessed it")
else:
print("You should guess between 1 and 9")
while True:
o = olv()
o.game()
p_again = str(input('Would you like to play again? ')).lower()
if p_again != "yes":
break
As pointed out in the comments, you have most of the code logic in there; however there is some confusion about OOP. One great thing to learn is to abandon (at least mostly) global variables, and use classes so that they can keep state of things. Another great suggestion I have is to use meaningful names, for variables and methods.
Now, it is a bit difficult to comment on each line of your code, so I took the liberty to rewrite it a bit. Keep in mind, there are dozen ways to do the same thing, so mine is not the best, nor (hopefully) the worse. I'll add comments to the code:
import random
class Game(object):
# First, let's use meaningful names
def __init__(self):
# Being a class, we can have an init method, and we can use it to initialize
# some variables that we will use to maintain "state". This means, we will give
# the class the job to remember what these values are during the course of the
# program, instead of storing them as global variables
#
# These variables are accessible by ALL methods in the class, using `self.`
self.value_to_guess = -1 # Note: we make value_to_guess and user_input different on purpose
self.user_input = 0 # so that play_game can loop without too much extra code
self.continue_play = True
def game_logic(self):
# This method is in charge of the game logic, which is "compare the values and print something"
# In your original code you did it for only two cases (>, <); it would be a good idea to also
# consolidate the case of equality
if self.user_input > self.value_to_guess:
print('You guessed too HIGH.')
elif self.user_input < self.value_to_guess:
print('You guessed too LOW.')
else:
print('Wow, you guessed it.')
def game_play(self):
# This method is in charge of the game play, making sure that a new random value is
# set at the beginning of a cycle, and that the user is asked every time for a value,
# until the user guesses. Every time, `game_logic` is called so that it will print the
# appropriate message on screen
#
self.value_to_guess = random.randint(1, 9)
while self.value_to_guess != self.user_input:
self.user_input = int(input('Guess a number between 1 and 9! '))
self.game_logic()
def question(self):
# This method will ask the user the question, and return the answered string straight out
return str(input('Would you like to play again? '))
def play(self):
# This method in charge of running games+questions, until the user stops answering 'yes'
self.game_play()
while self.question() == 'yes':
self.game_play()
if __name__ == '__main__':
my_game = Game() # Create a new Game object
my_game.play() # Ask the game object `my_game` to start playing
It's a lot of code, but I hope all the comments will help.
Just what you asked for
The problem manifests like this:
You play the game until you guessed the right number. Note that g == a now.
The asks "Do you want to play again?"
game() is called
But since g == a, game() will immediately quit
So, what's the problem? Well, for a new game, a needs to get a new random value.
Other stuff to make you a better programmer
To me it looks like you're typing in a poor text editor. You use short variable names. If you use an IDE which is made for Python, you'll get code completion and you can have better names that actually express what things are. a could be answer, g could be guess etc. Try JetBrains PyCharm. They have a community edition which is already great.
To me, glogic() is incomplete. The "Wow you guessed it" part is in game(). How about
def glogic(self):
if g > a:
print('You guessed too HIGH.')
elif g < a:
print('You guessed too LOW.')
else:
print('Wow, you guessed it.')
The code depends on global variables. Since you already have a class, all the variables could be included there. Please read about def __init__(self) and member variables like self.answer etc.
In q(), you convert the user input into a string. That's unnecessary, because it is already a string.
In pag(), code is duplicate. The logic to execute o.game() and then o.q() is coded twice. If you set p_again = "yes" initially, you can get rid of that duplication.
It seems n, y and again are unused.
To make user input a bit more robust, you could .lower() the input just in case the user enters some captital letters.
Same for numbers: if the user enters a letter, the code will fail with a ValueError. Use try: ... except ValueError: ... to make the game more robust.
I could rewrite the whole code for you, but I think it's worth doing that yourself.
In my text-adventure game, when I have
Would You like To Play (yes/no)yes
Ah! You are being chased by something! You need to go fast!
Should you take the bike or the skateboard (bike/skateboard)skateboard
Ah! You are being chased by something! You need to go fast!
Should you take the bike or the skateboard (bike/skateboard)
This means when I choose skateboard once, the question repeats, but if I chose bike, it goes on normally. This is the code:
def chooseAnswer():
answer=''
while answer.lower().strip() !='yes' and answer.lower().strip() !='no':
answer = input('Would You like To Play (yes/no)')
return answer
def chooseAnswer1():
answer1=''
while answer1.lower().strip() !='bike' and answer1.lower().strip() !='skateboard':
answer1= input('''Ah! You are being chased by something! You need to go fast!\nShould you take the bike or the skateboard (bike/skateboard)''')
return answer1
#Branches off 1 if choose Bike
def chooseAnswer11():
answer11=''
while answer11.lower().strip() !='right' and answer11.lower().strip() != 'left':
answer11= input('''You see two paths. Quickly, you have to decide which path to go on! The left one is dark and looks like it goes down a cave. The right one goes up a hill and into sunlight.(left/right)''')
return answer11
#Branches Off 1 if choose skateboard
def chooseAnswer12():
answer12=''
while answer12.lower().strip() !='sleep' and answer12.lower().strip() != 'awake':
answer12= input('''Quickly you hop on your skateboard, heading for woods.\nYou settle for the night in the woods.\nYou see the mysterious thing that is search for you.\nDo you sleep or stay awake?(awake/sleep)''')
return answer12
if chooseAnswer()=='yes':
if chooseAnswer1()=='bike':
if chooseAnswer11()=='right':
if chooseAnswer111()=='TBD':
print('TBD')
elif chooseAnswer112()=='TBD':
print('TBD')
elif chooseAnswer11=='left':
if chooseAnswer121()=='TBD':
print('TBD')
elif chooseAnswer122()=='TBD':
print('TBD')
elif chooseAnswer1()=='skateboard':
if chooseAnswer12()=='awake':
Anyone see why the input prompt is repeating twice?
The issue is being caused by calling the chooseAnswer1 function in your conditionals.
if chooseAnswer()=='yes':
# First call of chooseAnswer1
if chooseAnswer1()=='bike':
...Code Removed for Readability...
# second call of chooseAnswer1
elif chooseAnswer1()=='skateboard':
if chooseAnswer12()=='awake':
To fix this you'll want to assign the function call to a variable and then compare the object it returns to that variable in your conditional. You'll likely want to do this for all functions throughout your program:
# Single call of chooseAnswer
answer0 = chooseAnswer()
if answer0 == 'yes':
# Single call of chooseAnswer1
answer1 = chooseAnswer1()
if answer1 == 'bike':
...Code Removed for Readability...
# Since the variable is assigned to the returned object,
# the function is not called again in the elif condition.
elif answer1 == 'skateboard':
answer12 = chooseAnswer12()
if answer12 == 'awake':
Also Python 3.8's new walrus := operator may also be useful here, but I'm still using 3.7 so I'm going to leave it to someone else to provide code examples on how to use it in this situation.
Edit: OP said in a comment to my answer:
So I ran into another problem with this. I assigned all the functions variables, so when I think it goes in order I put the variables in. What I'm saying is when the first question pops up, it answers it as though I said yes and even though I say skateboard, it gives me the bike path.
My response is too long to post into a comment due to how long the url is for this python tutor link, but I recommend they compare their placement of their function calls to where I placed them in my example:
answer0 = chooseAnswer()
if answer0 == 'yes':
answer1 = chooseAnswer1()
if answer1 == 'bike':
answer11 = chooseAnswer11()
if answer11 == 'right':
print("right path")
elif answer11 == 'left':
print("left path")
elif answer1 == 'skateboard':
answer12 = chooseAnswer12()
if answer12 == 'sleep':
print("sleep path")
elif answer12 == 'awake':
print("awake path")
The key detail here is placement of the function calls.
Is there a way to make my code repeat after a "death" in my text based game without a systemexit.exit()?
I have tried to search loops but nothing, I have looked at other games online, and I have searched this website for anything that can help
if option==1:
print('\nyou break the front bericade but as you walk in you see a laser streatching across the door frame a little too late, you died')
thisdict["death"] = True
print('\nyou died')
SystemExit.exit()
I want it to just say you died then start you at the beginning and say so, but a system exit doesn't do that very well.
Use a loop, prepare your games state dictionary and call your game-function.
When you die, return out of it, ask for continue and create a "new" game state dict:
def game(starter):
# do stuff
lr = input(f"Hello {starter['name']} - nothing to see but go Left or Right? [L,R]: ").lower()
if lr == "left":
return # doing anything is futile, you die anyhow
else:
return # doing anything is futile, you die anyhow
def main():
state = {"name": "someone"}
while True:
game(state)
if not input("You are dead - Play again? [y,n]").lower() == "y":
break
# create new games state dict (just altering the name here)
state["name"] += "_again"
print("bye")
main()
Output:
Hello someone - nothing to see but go Left or Right? [L,R]: l
You are dead - Play again? [y,n] y
Hello someone_again - nothing to see but go Left or Right? [L,R]: r
You are dead - Play again? [y,n] y
Hello someone_again_again - nothing to see but go Left or Right? [L,R]: nothing
You are dead - Play again? [y,n] n
bye
Hello I am just starting to try to teach my self python and with one of the resources I read, I saw this dice game to make. So I did the basic but then I wanted to make it more full. My idea was to add a loop and have after each round it would prompt the user to enter at first q but now 0 to try to determine if it is an error in my input.
def gamestate():
print('enter 0 if you would like to quit anything else to continue')
game = input()
print(game == 0) # diagnostic to check if value is correct
print(type(game)) #diagnostic to make sure type is correct
print(game != str(0))
def play():
print('do you want to play a game enter yes to start')
game = '1' #filler value
game=input()
str(game)
if game == "yes": #confirms start of the game
Dice()
else:
print('Ok Goodbye') #plays game anyways will fix after loop issue
gamestate()
______________________________________________________________
while game !=str(0): #cannot escape loop for some reason
if game == str(0) :
break #to break
Dice()
gamestate()
print('ok good bye')
___________________________________________________________
play()
First, sorry if code long for this, but what I expect is 0 as an input to break the loop, what I get is having to kill my console process in spyder in order to stop this from looping
You have the variable name game at 2 different variable scopes and so they have different state. Try returning a game from gamestate() and comparing the value
Short description of the scoping rules?
def gamestate():
print('enter 0 if you would like to quit anything else to continue')
game = input()
print(game == 0) # diagnostic to check if value is correct
print(type(game)) #diagnostic to make sure type is correct
print(game != str(0))
return game
while game !=str(0): #cannot escape loop for some reason
if gamestate() == str(0) :
break #to break
Dice()
print('ok good bye')
You have to return game value from gamestate function and assign it in while loop. Check below code:
def gamestate():
print('enter 0 if you would like to quit anything else to continue')
game = input()
print(game == 0) # diagnostic to check if value is correct
print(type(game)) #diagnostic to make sure type is correct
print(game != str(0))
return game
def play():
print('do you want to play a game enter yes to start')
game = '1' #filler value
game=input()
str(game)
if game == "yes": #confirms start of the game
Dice()
else:
print('Ok Goodbye') #plays game anyways will fix after loop issue
gamestate()
while game !=str(0): #cannot escape loop for some reason
if game == str(0) :
break #to break
Dice()
game = gamestate()
print('ok good bye')
play()
In order to compare with zero as a string you need merely do something like if game == "0":. The issue you may be running into is that of extra whitespace chars like "\n". If you use input().trimspace() you'll remove extraneous chars and do comparisons with the values you want to.
Also another problem in the code is that it will enter the while loop if game does not equal "0" and so the if condition that follows will automatically not be met. So break is never hit.
You need to modify your program to convert input to int and not converting 0 to string at multiple places.changes needs to be done in while loop and gamestate function
I'm practice classes and inheritance in a program idea that I came up with myself. Basically Im making an arcade game menu simulator that can play two modes, single player and multiplayer. Every time I enter a choice, either 1 or 2, the menu displays a couple times and then it proceeds to accept the input, I only want the menu to be displayed once. Heres my code:
# Suppose you are at an arcade and you and your friend want to play a multiplayer game that requires UI.
# Make the game ask for the users name and age to see if they can play, make the program so that it can add a friend.
# If any of the players are under the age of 18, they are not allowed to play, otherwise proceed.
# **EXTRA CREDIT** --> Add a functionality which adds the players to a list until the list reaches 4 players, then stop adding to the list.
# arcade_game.py
import sys
# give the user a greeting
import self as self
lst = []
class menu:
def __init__(self, ready):
self.ready = ready
#display menu
#classmethod
def display_menu(self):
print("Pick from one of the choices below, type in the corressponding number")
print("1. single player \n"
"2. Multiplayer")
choice = int(input("Enter your choice here: "))
return choice
# ready or not function to see if the user is ready to play
def ready_or_not(self):
# see if user types 1 or 2 with try & except
try:
# ask user if they are ready
self.ready = int(input("Are you ready to play? Type 1 for yes, 2 for no"))
self.display_menu()
except ValueError:
print("You did not type 1 or 2, please try again!")
# add players class
class player(menu):
# add a default player to __init__(), **(since there has to be at least one player)**
def __init__(self, ready, player1):
super().__init__(ready)
self.player1 = player1
# single player method
def set_name(self):
self.player1 = input("Enter your name for single player mode")
print("Lets play! ", self.player1)
# multiplayer method
def set_names(self):
try:
self.player1 = input("Enter your name to begin")
lst.append(self.player1)
# add another player to continue
while len(lst) <= 4:
add = input("Add player here: ")
lst.append(add)
if len(lst) == 4:
print("Player limit reached!")
break;
except ValueError:
print("You didnt enter valid input, please try again")
# get the names of the players only if 1 is picked from display_menu() above, including player1
def check_choice(self):
if self.display_menu() == 1:
self.set_name()
elif self.display_menu() == 2:
self.set_names()
else:
print("Exiting....")
print("Goodbye!")
sys.exit(0)
m = menu("yes")
m.ready_or_not()
p = player("yes", "test")
p.check_choice()
ready_or_not calls self.display_menu():
def ready_or_not(self):
# see if user types 1 or 2 with try & except
try:
# ask user if they are ready
self.ready = int(input("Are you ready to play? Type 1 for yes, 2 for no"))
self.display_menu()
except ValueError:
print("You did not type 1 or 2, please try again!")
check_choice also calls self.display_menu() at least once, and twice if you type anything other than 1 the first time:
def check_choice(self):
if self.display_menu() == 1:
self.set_name()
elif self.display_menu() == 2:
self.set_names()
else:
print("Exiting....")
print("Goodbye!")
sys.exit(0)
Your top-level code calls ready_or_not() on one menu instance:
m = menu("yes")
m.ready_or_not()
… and check_choice() on another:
p = player("yes", "test")
p.check_choice()
So, your program displays the menu twice, and then a third time if you type anything but 1.
If you don't want the menu displayed two or three times, don't call the method two or three times.
If you want to display the menu only once and remember the choice, instead of displaying it two or three times, you need to use that self.ready attribute that you create in ready_or_not, instead of calling the method again.
However, that still isn't going to work as-is, because your class design is weird. You've made two separate instances, m and p, each of which has its own independent attributes. I'm not sure why player inherits from menu in the first place (or why display_menu is a #classmethod, or why it calls its parameter self rather than cls if it is one, and various other things), but, given that a player is a menu in your design, you probably just want a single player instance, like this:
p = player("yes", "test")
p.ready_or_not()
p.check_choice()
And then, you can change check_choice like this:
def check_choice(self):
if self.choice == 1:
self.set_name()
elif self.choice == 2:
self.set_names()
else:
print("Exiting....")
print("Goodbye!")
sys.exit(0)
Took me a while to figure out, but it seems like when you are done with display_menu() your calling ready_or_not() so you need to remove display_menu() from ready or not like this
# ready or not function to see if the user is ready to play
def ready_or_not(self):
# see if user types 1 or 2 with try & except
try:
# ask user if they are ready
self.ready = int(input("Are you ready to play? Type 1 for yes, 2 for no"))
# self.display_menu()
except ValueError:
print("You did not type 1 or 2, please try again!")
EDIT
looks like im late to the party