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
Related
I'm currently making a scoring system for an event and I'm having trouble using while loops as way to validate user input. For example I want to force the user enter their name so it can be added to a spot for a sport like tennis which I used a dictionary for. Everytime I am done asking them for their user input the while loop keeps apearing leading to a never ending while loop. Here's what I've done.
```solo_player = {
"Contestant 1":{"Player 1":[],"Score":[],"Event":[]},
"Contestant 2":{"Player 2 ":[],"Score":[],"Event":[]},
"Contestant 3":{"Player 3":[],"Score":[],"Event":[]},
"Contestant 4":{"Player 4":[],"Score":[],"Event":[]}}
def individual():
solo_name = False
while not solo_name:
solo_name = input("What is your name:")
print(""" \nIndividual Menu and Available Spots\n
1) Player 1
2) Player 2
3) Player 3
\n4) Go back to Main Menu\n""")
solo_menu = ["1","2","3","4"] #valid choices for this menu
solo_menu = False
while not solo_menu:
solo_menu = input("Please enter your choice here:")
if solo_menu == "1":
solo_player["Contestant 1"]["Player 1"].append(solo_name)
print(solo_player["Contestant 1"])
print("Thank you for taking the spot of Player 1")
solo_menu = True
elif solo_menu == "2":
solo_player["Contestant 2"]["Player 2"].append(solo_name)
print(solo_player["Contestant 2"])
print("Thank you for taking the spot of Player 2")
solo_menu = True
else:
print("Enter a value between 1-4)
solo_menu = False```
My ideal output would be that the while loop will stop after I picked one spot from the available spots. Sorry for the long if/else statements I'm quite new to it so I don't know how to make it shorter
EDIT: I fixed the issue. It was caused by calling the individual func outside my menu def.
Here's what I believe you are after.
A couple of changes:
Your while loops can contain the validation that you are wanting to perform as their condition. If your validation becomes more complex then you can consider moving back to a boolean to break the while loop, but it feels overly complex for your requirements.
Removal of the if statements. The whole idea of a dictionary is that you can specific a key and get a value, so having an elif for every potential key is overly cumbersome. Just concatenate the validated user input to derive the key.
solo_player = {
"Contestant 1":{"Player 1":[],"Score":[],"Event":[]},
"Contestant 2":{"Player 2":[],"Score":[],"Event":[]},
"Contestant 3":{"Player 3":[],"Score":[],"Event":[]},
"Contestant 4":{"Player 4":[],"Score":[],"Event":[]},
"Contestant 5":{"Player 5":[],"Score":[],"Event":[]},
"Contestant 6":{"Player 6":[],"Score":[],"Event":[]},
"Contestant 7":{"Player 7":[],"Score":[],"Event":[]}}
solo_name=""
while solo_name == "":
solo_name = input("What is your name:")
print(""" \nIndividual Menu and Available Spots\n
1) Player 1
2) Player 2
3) Player 3
4) Player 4
5) Player 5
6) Player 6
7) Player 7
\8) Go back to Main Menu\n""")
#prompt for player slot
solo_menu_options = ["1","2","3","4","5","6","7","8"] #valid choices for this menu
solo_menu=""
while solo_menu not in solo_menu_options:
solo_menu = input("Please enter your choice here:")
solo_player["Contestant "+solo_menu]["Player "+solo_menu].append(solo_name)
print(solo_player["Contestant "+solo_menu])
print("Thank you for taking the spot of Player "+solo_menu)
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.
I'm currently writing up a simple program to manage a car yard for a rental company, i've gotten to the end with no real issues until now. To illustrate what I mean i'll posted my code and then my issue.
# class for Car_Yard
class CarYard():
def __init__(self, listOfCars):
self.availableCars = listOfCars
def carYardCarsAvailable(self):
print("Available Cars: ")
for car in self.availableCars:
print(car)
def carYardRentCar(self, rentCar):
if rentCar in self.availableCars:
print("Congratulations on renting your new car, here\'s the keys")
self.availableCars.remove(rentCar)
else:
print("We currently don't have that car in our yard")
def carYardReturnCar(self, rentCarReturn):
self.availableCars.append(rentCarReturn)
print("You have returned the car. Thank You!")
# class for Buyer and his/hers actions
class Buyer():
def buyerRentCar(self):
print("Which car would you like to rent out?" )
self.car = input()
return self.car
def buyerReturnCar(self):
print("Which car would you like to return? ")
self.car = input()
return self.car
# create objects from class and pass a list of cars to the car yard
carYard = CarYard (['Mazda','Holden','Ford','Porsche','Honda','VW','Toyota','Kia'])
buyer = Buyer
# infinite loop
while True:
print()
print("Enter 1 to see our wide range of cars")
print("Enter 2 to rent a car")
print("Enter 3 to return a car")
print("Enter 4 to leave the rental yard")
print()
userInput = int(input())
if userInput is 1:
carYard.carYardCarsAvailable()
elif userInput is 2:
rentCar = buyer.buyerReturnCar
carYard.carYardRentCar(rentCar)
elif userInput is 3:
rentCarReturn = buyer.buyerReturnCar
carYard.carYardReturnCar(rentCarReturn)
elif userInput is 4:
quit()
The issue that i'm having is when I run my code and enter 2 it automatically skips to the line "we currently don't have that car in our yard" and when I enter 3 is says "You have returned the car. Thank you!".
I'm trying to figure out why my code is not called the Buyer class to request for the input. Any suggestions on what I might be missing?
You shouldn't use is like that. The is operator tests whether 2 objects are the same, which is not the same as testing whether their values are equal. What you actually want is an equality test (e.g. userInput == 1).
Anyway, the source of your problem is that you're passing around methods instead of the values that are returned by those methods. For instance this may work better:
buyer = Buyer()
...
elif userInput == 2:
rentCar = buyer.buyerReturnCar()
carYard.carYardRentCar(rentCar)
By passing buyer.buyerRentCar you're passing a method to carYardRentCar, and naturally it's not able to match that method against anything in the list of cars. What you want is to pass a string that is returned by carYardRentCar(). This will cause that method to get invoked, asking the user for input, and then the result of that will get passed along, which is what you want
I am trying to write a menu in which option 1 would add a key:value pair to a dictionary and option 2 would run a threading module using the items from the dictionary. Key is a message from the user input and Value is a number of seconds, from the user input, that a thread should pause for, before displaying the message.
Threading itself is irrelevant right now though. What I'm struggling with is the fact that when I use the function for option 1, it adds the key:value pairs to the dictionary (called messages_and_times) successfully. But as soon as the function is finished the dictionary becomes empty again, as can be seen from the function for option 2, which accesses it.
At the bottom you can see my code. I've added the following lines in order to check what's in the dictionary at each step:
print(dict(messages_and_times))
if not messages_and_times:
print("This dictionary is empty.")
else:
print("This dictionary contains items.")
It doesn't seem to work correctly either however. First of all it prints "This dictionary contains items." whether the printed dictionary looks empty or not. Second of all the following part of my code (clear() is used for clearing the terminal display):
def create_dictionary():
clear()
answer = input(
"Would you like to add another message? (yes/no)").lower()
if answer == "yes":
option_1()
elif answer == "no":
clear()
print("You will now be returned to the main menu.")
print(dict(messages_and_times))
does print a dictionary containing items if I chose to add them. But if I add the line
print(dict(messages_and_times))
to the main_menu() itself, the above mentioned create_dictionary() function prints an empty dictionary instead. Just this one print() statement in the main_menu() affects whether create_dictionary() shows a dictionary with items in it or not.
Could someone please help me understand how to design a code in which the dictionary retains the items created by one function, so that they can be accessed by other functions?
Thank you in advance for your time and assistance,
import os
clear = lambda: os.system('cls')
def main_menu():
list_of_messages = []
list_of_times = []
messages_and_times = zip(list_of_messages, list_of_times)
def option_1():
clear()
list_of_messages.append(
input("Please type in a message you would like to add to the list:"))
clear()
list_of_times.append(
input("Please type in the time of delay for this message:"))
def create_dictionary():
clear()
answer = input(
"Would you like to add another message? (yes/no)").lower()
if answer == "yes":
option_1()
elif answer == "no":
clear()
print("You will now be returned to the main menu.")
print(dict(messages_and_times))
if not messages_and_times:
print("This dictionary is empty.")
else:
print("This dictionary contains items.")
time.sleep(1.5)
main_menu()
else:
clear()
print("Please answer yes or no.")
time.sleep(1.5)
create_dictionary()
create_dictionary()
def option_2():
clear()
print(dict(messages_and_times))
if not messages_and_times:
print("This dictionary is empty.")
else:
print("This dictionary contains items.")
time.sleep(5)
main_menu()
clear()
selection = 0
while selection == 0:
print(("-" * 15) + "MAIN MENU" + ("-" * 15) + "\n")
print("1: Input a message and a corresponding time of delay before its display.")
print("2: Print your customized list of messages.")
print("3: Generate a list of random messages with random delays.\n")
selection = int(input(
"Please select one of the options, by typing in the corresponding number:"))
if selection == 1:
option_1()
elif selection == 2:
option_2()
elif selection == 3:
clear()
print("You've selected the third option.")
else:
clear()
print("Please select from options 1 - 3.\n")
time.sleep(1.5)
main_menu()
I am making a simple text-based RPG in Python. Currently I have two methods for most rooms, one for when they first enter and one if they return. Is there a way that I can make sure that they haven't been in that room before without another method?
For example, if I had a method named tomb() i create another method called tombAlready() that contains the same code except for the introduction text to the room.
So if I had
slow_type("\n\nTomb\n\n")
slow_type("There is an altar in the middle of the room, with passages leading down and west.")
choice = None
while choice == None:
userInput = input("\n>")
if checkDirection(userInput) == False:
while checkDirection == False:
userInput = input("\n>")
checkDirection(userInput)
userInput = userInput.lower().strip()
if userInput == "d":
catacombs()
elif userInput == "n":
altar()
elif userInput == "w":
throneroom()
else:
slow_type("You cannot perform this action.")
Then tombAlready() would have the same code except for slow_type("There is an altar in the middle of the room, with passages leading down and west.")
What you want is state associated with a function. Use an object with a method:
class Room:
def __init__(self, description):
self._description = description
self._visited = False
def visit(self):
if not self._visited:
print(self._description)
self._visited = True
Then you can have a Room object for each room:
catacombs = Room('There is a low, arched passageway. You have to stoop.')
tomb = Room('There is an altar in the middle of the room, with passages leading down and west.')
throneroom = Room('There is a large chair. It looks inviting.')
You can visit a room twice but it only prints its description once:
>>> catacombs.visit()
There is a low, arched passageway. You have to stoop.
>>> catacombs.visit()