While statement ignoring function - python

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.

Related

Creating a Python game, working with returns in-between multiple modules

No matter how many times I google variations of my question, I cannot seem to find a solution. I am a beginner programmer, trying to build a game that randomly generates events as you progress through the stages. The problem I am running into are return statements, and passing the values between different modules. Each method for each file are inside of classes. They are all static methods, and calling these methods is not my problem. It is transferring the value of the variables. I'm not sure where I am going wrong, whether it is how I am structuring it, or if I just don't understand how these return statements work.
This is the first File I am starting from. Print statements will be filled out after everything functions properly.
def story():
print("---Intro Story Text here--- ... we will need your name, Traveler. What might it be?")
user_prompt = Introduction.PlayerIntroduction
name = user_prompt.player_info(1)
print(f"Welcome {name}!")
print(f"----After name is received, more story... how old might you be, {name}?")
age = user_prompt.player_info(2)
This is the file I am trying to get the values from. File: Introduction, Class: PlayerIntroduction
#staticmethod
def player_info(funct_select):
if funct_select == 1:
name = PlayerIntroduction.get_player_name()
player_name = name
elif funct_select == 2:
age = PlayerIntroduction.get_player_age()
player_age = age
return player_name, player_age
#staticmethod
def get_player_name():
print("\n\n\nWhat is your name?")
players_name = input("Name: ")
while True:
print(f"Your name is {players_name}?")
name_response = input("Yes/No: ")
if name_response == "Yes" or name_response == "yes":
name = "Traveler " + players_name
break
elif name_response == "No" or name_response == "no":
print("Let's fix that.")
PlayerIntroduction.get_player_name()
else:
print("Please respond with 'Yes' or 'No'.")
return name
#staticmethod
def get_player_age():
print("\n\n\nHow old are you?")
age = input("Age: ")
while True:
print(f"Your age is {age}?")
age_response = input("Yes/No: ")
if age_response == "Yes" or age_response == "yes":
break
elif age_response == "No" or age_response == "no":
print("Let's fix that.")
PlayerIntroduction.get_player_age()
else:
print("Please respond with 'Yes' or 'No'.")
return age
I would like to use the values for "name" and "age" throughout multiple modules/multiple methods within my program. But in order to get those values, I need to assign a variable to the function call.. Resulting in prompting the user to re-enter their name/age at later stages in the game. My idea to combat this was in the first method of this module, creating a conditional statement "if 'example' == 1: 'run the name prompt' and elif == 2: run age prompt, thinking the initial run with the arguments defined would run these prompts, store the values into the variables (name, age), and finally pass the values to the new variables that are NOT assigned to the function call (p_name, p_age), avoiding triggering the user prompt over and over. Ultimately, this failed, and as the code sits now I am getting:
UnboundLocalError: local variable 'player_age' referenced before assignment
Why is this? The only instance 'player_age' is called that is reachable at this point is in the return statement, indented in-line with the conditional statement. The code should read (If I understand incorrectly, please explain) from top to bottom, executing in that order. The 'if' condition is met, so it should run that. If I were to define 'player_name' and 'player_age' as null at the top of this method to avoid this error, then every time I would need to reference these values initially entered by the user, they would be re-assigned to 'null', negating everything I am trying to do.
Thank you all for your patience, I tried to explain what I was doing and my thought process the best I could. Any feedback, criticism, and flaws within my code or this post are GREATLY appreciated. Everything helps me become a better programmer!! (:

Creating a function to restart while loop

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

my code is not working and it only says syntax error

i tried couple of things but does not work. the problem is in the 11th line(that's what python said). i am still 14 and do not have a big knowladge on programing. hope i can get some help
def crowbar():
print("nice, so you picked the crowbar, and now you have to get out of the car")
randomNum = random.randint(1,10)
if randomNum == [2,4,6,8,10]:
print(" shoo, those 2 people are walking away. now it's the time to get out of here")
elif randomNum == [1,3,5,7,9]:
print("they are coming at your direction. you have to do something and fast")
choise2 = int(input("""
1)hit them with the crowbar
2)pretent that you are still unconscious"""))
if choise2 == 1: #the problem has something to do with this line
print("ok")
elif choise2 == 2:
print("not ok")
I just refactored your code and fixed several things. The program should run without any problems.
I inline commented the issues and refactorings I did:
#Added the import statement to make sure code is runnable if copy&pasted
import random
def crowbar():
print("nice, so you picked the crowbar, and now you have to get out of the car")
randomNum = random.randint(1,10)
#Use the "x in list" expression as it is the most readable and pythonic way
if randomNum in [2,4,6,8,10]:
print("shoo, those 2 people are walking away. now it's the time to get out of here")
#Just use else for the second case, as this has the same behaviour without boiler plate
else:
print("they are coming at your direction. you have to do something and fast")
choice2 = 0
#Defensive programming: As long as neither 1 nor 2 was pressed, the input request will loop forever
while choice2 not in [1, 2]:
print("What do you want to do?")
choice2 = int(input("""
1)hit them with the crowbar
2)pretent that you are still unconscious\n"""))
#I had no error here to be honest as you said. It just worked.
if choice2 == 1:
print("ok")
elif choice2 == 2:
print("not ok")
if __name__ == "__main__":
crowbar()
Alright, a few things to note:
To check if a value is inside of a data structure, use is in instead of ==.
Variable names should be descriptive and follow the lower_case_with_underscores style.
Your random number generation can be simplified, since currently are you need is a 50/50 chance.
You should probably be using a solid IDE, which would alert you to the naming issues and the incorrect membership testing.
Here is your program, I refactored it quite a bit.
import random as rand
def crowbar():
print('You pick up the crowbar. Now you have to get out of the car.')
random_num = rand.random()
if random_num > 0.5:
print('Fortunately, those 2 people are walking away. You can get away safely.')
else:
print('They are moving towards you, you have to react quickly.\nYou decide to...\n(1) Hit them with the '
'crowbar.\n(2) Pretend that you are still unconscious.')
choice = input('Your choice (1 | 2): ')
if choice == '1':
print('Ok.')
elif choice == '2':
print('Not ok.')
else:
print(f'Error! Incorrect input: {choice}')
crowbar()
Explanations:
As mentioned above, I simplified the random number generation.
Variable names have been improved.
I fixed as much of the grammar, syntax and overall writing as I could.
The program now verifies the input. I also got rid of the unnecessary conversion to int.
Your indentation is incorrect and you are missing parenthesis in choice2 = ...
Also you need if randomNum in [2,4,6,8,10]: and if randomNum in [1,3,5,7,9]:
This works fine
def crowbar():
print("nice, so you picked the crowbar, and now you have to get out of the car")
randomNum = random.randint(1,10)
if randomNum in [2,4,6,8,10]: # list here
print(" shoo, those 2 people are walking away. now it's the time to get out of here")
elif randomNum in [1,3,5,7,9]: # list here
print("they are coming at your direction. you have to do something and fast")
choise2 = int(input("""
1)hit them with the crowbar
2)pretent that you are still unconcious""")) # parethesis here
if choise2 == 1: # corrected indentation here
print("ok") # corrected indentation here
elif choise2 == 2: # corrected indentation here
print("not ok") # corrected indentation here

My display menu repeats multiple times when an input is entered

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

How would I write a program that can call a variable from another def?

So I set out to make a simple game of hangman and everything worked fine, the whole code worked but it lacked the ability to allow the user to replay when the game is over. Thus I set out to put all the code I have written in various functions. So that I can call the functions when they are required (I thought it was the most logical way to allow replay-ability). Various problems followed but one stood out.
The main culprit (I think) is that I could not successfully get a value to update globally. I've read similar questions on the site but could not successfully adapt it to my case. I have a sample code to show what exactly I mean:
def GameMode():
choice = input('Play alone or play with friends? A F : ')
choice = choice.upper()
if choice == 'A':
wordslotmachine = ['stand','emerald','splash']
word = random.choice(wordslotmachine)
word = word.upper()
Rules()
elif choice == 'F':
word = input('Enter your word for your friends to guess: ')
word = word.upper()
Rules()
else:
choice = input('Please enter A or F: ')
choice = choice.upper()
I would need the program to remember what the value of "word" is and use this word in another method (this method is ran by another method showed below "Rules()"):
def MainGame():
guesses = ''
turns = 10
underscore = 0
seconds = 1
checker = 0
cheaterchance = 5
while turns > 0: #check if the turns are more than zero
for char in word: # for every character in secret_word
if char in guesses: # see if the character is in the players guess
print(char+' ', end='')
else:
print('_ ', end='')# if not found, print a dash
underscore += 1
if underscore == 0:
print(': You got it!')
Wait()
NewGame()
break
#A block of if's to check for cheating
if guess not in word:
print('Your guesses so far: '+guesses)
turns -= 1
if turns == 0:
break
else:
print('')
print('Try again. You have',turns,'more guesses')
print('Delayed chance to answer by',seconds,'seconds')
counter = 1
print(0,'.. ', end='')
while counter < seconds:
time.sleep(1)
print(counter,'.. ', end='')
counter += 1
if counter == seconds:
time.sleep(1)
print(counter,'.. done!', end='')
print('')
print('')
seconds += 1
underscore = 0
else:
print('Your guesses so far: '+guesses)
underscore = 0
#The else portion of the code to check for cheating
I have tried defining "word" outside of the function. Doing this doesn't fix the problem, GameMode() will not successfully update the value of "word". And whatever the value of "word" defined outside of the function will be called and used by MainGame(). However doing this shows another problem.
That being, the code that previously worked (it successfully read the input and correctly updated the game status) now does not work. Even if the correct letter is entered by the user, the program reads the input as incorrect.
These are the two problems I have faced so far and have yet to find a way to overcome them.
Note: I have successfully created a way to make the game replay-able by putting the entire original code (without the functions) inside a while loop. However I would still very much like to know how I can get the code to work using functions.
Edit: This is the function for Rules():
def Rules():
#Bunch of prints to explain the rules
MainGame()
print('Start guessing...')
Wait() is just a delay function with a countdown.
Global vs. Local variables.
You can reference and use a global variable from within a function, but you cannot change it.
It's bad practice, but you CAN declare a variable within your function to be global and then changes to it inside your function will apply to the variable of the same name globally.
HOWEVER, what I suggest is to return the word at the end of your function.
def whatever_function(thisword):
do some stuff
return word
new_word = whatever_function(thisword)
Functions can, and usually should, return values. Make GameMode() return the word to the caller;
def GameMode():
choice = input('Play alone or play with friends? A F : ')
choice = choice.upper()
if choice == 'A':
wordslotmachine = ['stand','emerald','splash']
word = random.choice(wordslotmachine)
word = word.upper()
Rules() #ignore this
elif choice == 'F':
word = input('Enter your word for your friends to guess: ')
word = word.upper()
Rules() #ignore this
else:
choice = input('Please enter A or F: ')
choice = choice.upper()
return word
From the main call GameMode and save the word;
def MainGame():
guesses = ''
turns = 10
underscore = 0
seconds = 1
checker = 0
cheaterchance = 5
word = GameMode() # add this here
You almost certainly want to use a class with instance variables
Contrived example:
class Hangman:
def __init__(self):
print("Starting hangman")
def mode(self):
# ...
self.word = 'whatever'
def play(self):
print("Look i have access to word", self.word)
if __name__ == '__main__':
hm = Hangman()
hm.mode()
hm.play() # may be what you want to put in a while loop
To access a global variable from inside a function, you have to tell python that it is global:
my_global_var = 1
def some_func():
global my_global_var
You have to use global keyword in every method that the global variable is being used or python will think you are defining/ using a local variable
Having said that, you should avoid globals as a coding practise.
global word #probably define this after imports.
def GameMode():
global word #add this
choice = input('Play alone or play with friends? A F : ')
choice = choice.upper()
if choice == 'A':
wordslotmachine = ['stand','emerald','splash']
word = random.choice(wordslotmachine)
word = word.upper()
Rules() #ignore this
elif choice == 'F':
word = input('Enter your word for your friends to guess: ')
word = word.upper()
Rules() #ignore this
else:
choice = input('Please enter A or F: ')
choice = choice.upper()
def MainGame():
guesses = ''
turns = 10
underscore = 0
seconds = 1
checker = 0
cheaterchance = 5
global word # add this here
use the code
global word
above the def or let the def return the value of word so it is stored in a variable outside the def

Categories

Resources