I am new to python and want to write a simple text adventure game. The player enters a tavern and interacts with the guests. The game takes place in a fantasy setting, where there are multiple races. I want to randomly generate each guest and then interact with them in the tavern. Here is my simplified code:
import random
class guest:
def __init__(self,race,name,mood):
self.race = race
self.name = name
self.mood = mood
def humannames():
human_names_choice = ('Mark','Joe','Bill')
return random.choice(human_names_choice)
def orcnames():
orc_names_choice = ('Ug','Orok','Ushnar')
return random.choice(orc_names_choice)
def humanmoods():
human_moods_choice = ('entertained','disgusted','pleased')
return random.choice(human_moods_choice)
def orcmoods():
orc_moods_choice = ('drunk','pissed off','angry')
return random.choice(orc_moods_choice)
guest_human = guest('human',humannames(),humanmoods())
guest_orc = guest('orc',orcnames(),orcmoods())
allguests = [guest_human,guest_orc]
guest1 = random.choice(allguests)
print('You meet a ' + guest1.race + ' named ' + guest1.name + '. He seems ' + guest1.mood)
So far so good. With this system i can add many more races,names etc. later.
My problem is the following:
It is logical, that guest1.name and guest.race never change. If i print the last statement 5 times in a row, i will get the same results, because the values are set at the start. This is good for me, because those things should never change. With guest1.mood however it is more complicated. A tavern guest doesnt always have the same mood, it changes over time.
Question: How can i keep guest1.name and guest.race static but get a new random mood everytime the player meets the same guest again later?
Thanks in advance!
You can add a change_mood method to your guest class. This means that you can change the mood whenever you want.
class guest:
def __init__(self,race,name,mood):
self.race = race
self.name = name
self.mood = mood
def change_mood(self):
self.mood = random.choice(('entertained','disgusted','pleased'))
Then you can change the mood like this.
print('You meet a ' + guest1.race + ' named ' + guest1.name + '. He seems ' + guest1.mood)
guest1.change_mood()
print('You meet a ' + guest1.race + ' named ' + guest1.name + '. He seems ' + guest1.mood)
This will always change the mood to a human mood, which is not ideal for orcs. To make this easier for different races, you could make two subclasses of guest: one for humans, and one for orcs. I would organise it like this:
class Guest:
def __init__(self):
self.name = random.choice(self.names)
self.mood = random.choice(self.moods)
def change_mood(self):
self.mood = random.choice(self.moods)
class Human(Guest):
race = 'human'
names = ('Mark', 'Joe', 'Bill')
moods = ('entertained', 'disgusted', 'pleased')
class Orc(Guest):
race = 'orc'
names = ('Ug', 'Orok', 'Ushnar')
moods = ('drunk',' pissed off', 'angry')
You can then use the classes like this:
guest1 = Orc()
print('You meet a ' + guest1.race + ' named ' + guest1.name + '. He seems ' + guest1.mood)
Edit: another way would be to store all of your race data in one big data structure, and then specify the race as an argument to the Guest class.
class Guest:
races = {
'human': {
'names': ('Mark', 'Joe', 'Bill'),
'moods': ('entertained', 'disgusted', 'pleased'),
},
'orc': {
'names': ('Ug', 'Orok', 'Ushnar'),
'moods': ('drunk',' pissed off', 'angry'),
},
}
def __init__(self, race):
self.race = race
self.name = random.choice(self.races[race]['names'])
self.mood = random.choice(self.races[race]['moods'])
def change_mood(self):
self.mood = random.choice(self.races[self.race]['moods'])
Also, instead of storing the race data in the class, you could store it in a separate JSON file or something, and then load the data from the file in the __init__ method.
Related
I develop bottom up, starting with small simple methods to go to the big full fledged implementation
class Pop(object):
def welcome(self, name, new_member = False):
response = ""
if new_member:
response = " NOT"
return str("hello there "+name+", you seem"+response+" to be a member\n")
def ageVerification(self, name, age, new_member = False):
the_welcome_string = self.welcome(name, new_member)
minimum = ""
excuse = ""
if age < 16:
minimum = " NOT"
excuse = ", sorry"
return str(the_welcome_string+str(age)+" is"+minimum+" the minimum required age to buy beer in Belgium"+excuse+"\n")
def theWholething(self, name, age, address, new_member = False):
if age < 16:
appology = str("you cannot order any beer\n")
else:
appology = str("your beer will be shipped to "+address+"\n")
return str(self.ageVerification(name, age, new_member)+appology)
# EOF
My question is if it is normal that when i reach theWholeThingMethod, I carry along all the parameters of the previously defined methods? Is this pythonic?
My population class has almost 20 "helper" methods called in theWholeThing, and it seems I am just fiddling with parameters to get them in the right order ...
theWholeThing(self,\
name,\
age,\
address,\
registered = True,\
first_date_entered,\
last_date_entered,\
purchased_amount,\
favorite_beer,\
promotional_code,\
and_so_on0,\
and_so_on1,\
and_so_on2,\
and_so_on3,\
and_so_on4,\
and_so_on5,\
and_so_on6,\
and_so_on7,\
and_so_on8,\
and_so_on9,\
and_so_on10):
My question is if it is normal that when i reach theWholeThingMethod, I carry along all the parameters of the previously defined methods? Is this pythonic?
Neither.
There is really no point in having a class if all the methods take all the arguments anyway. These might as well just be functions.
There are many ways this could be done, depending on whether the various parameters are mandatory, or what happens when one is not provided, but here is one possibility:
from dataclasses import dataclass
#dataclass
class Pop(object):
name: str
age: int
address: str
new_member : bool = False
def welcome(self):
response = ""
if self.new_member:
response = " NOT"
return str("hello there "+self.name+", you seem"+response+" to be a member\n")
def ageVerification(self):
the_welcome_string = self.welcome()
minimum = ""
excuse = ""
if self.age < 16:
minimum = " NOT"
excuse = ", sorry"
return str(the_welcome_string+str(self.age)+" is"+minimum+" the minimum required age to buy beer in Belgium"+excuse+"\n")
def theWholething(self):
if self.age < 16:
appology = str("you cannot order any beer\n")
else:
appology = str("your beer will be shipped to "+self.address+"\n")
return str(self.ageVerification()+appology)
# EOF
Note: #nneonneo had a great suggestion of using a dataclasses, so answer tweaked to incorporate that
I am making a text based adventure game in python. Once the game begins, I would like to create an instance of a class called "Character" which is the player's character object. I would like the user to be able to choose the race of the character they want to play. So far I have:
class Race:
def __init__(self, name, passive, hp):
self.name = name
self.passive = passive
self.hp = hp
and
class Lizard(Race):
def __init__(self, name, passive, hp):
super().__init__(name, passive, hp)
self.name = 'Lizardman'
self.passive = 'Regrowth'
self.hp = 20
def regrowth(self):
if 0 < self.hp <= 18:
self.hp += 2
and
def race_select():
races = ['Lizard']
while True:
for i, j in enumerate(races):
print(f"[{i + 1}]", j)
choice = int(input('Pick a race:'))
if choice <= len(races):
print('You are a ', races[choice - 1])
return races[choice - 1]
else:
continue
If I understand correctly, if I wanted the race to be a Lizard, I would still have to do
character = Lizard('Lizardman', 'Regrowth', 20)
Is there an easy way to let the user choose the race and the object to be created accordingly? Thanks
A simple solution would be to map a name to a class using a dictionary. As a simple example:
race_map = {"lizard": Lizard,
"human": Human} # I'm adding a theoretical other class as an example
choice = input('Pick a race:')
race_initializer = race_map.get(choice, None) # Get the chosen class, or None if input is bad
if race_initializer is None:
# They entered bad input that doesn't correspond to a race
else:
new_creature = race_initializer(their_name, their_passive, their_hp)
new_creature is now the new object of the chosen class.
You may want to standardize the input using choice.lower() to ensure that capitalization doesn't matter when they enter their choice.
I changed it to allow for specifying a race by a string name instead of a number. If you wanted a number, you could keep your list, but apply the same idea. Something like:
race_list = races = [('Lizard', Lizard), ('human', Human)]
choice = int(input('Pick a race:'))
try:
race_initializer = race_list[choice][1] # 1 because the class object is the second in the tuple
new_creature = race_initializer(their_name, their_passive, their_hp)
except IndexError:
# Bad input
I included the name in the race_list so that you can loop over the list and print out index->name associations for the user to pick from.
You may also want to use a more robust structure than a plain tuple to store name->initializer mappings, but it works well in simple cases.
I try to create a tree of objects / not classes. I mean i do not want to create relations between the objects in prototype level, but in instance level so the relations between the objects will be solved with the code.
Here i wrote something
class human :
def __init__ (self,Name,Surname,Gender,Age,Father,Mother) :
self.Name = Name
self.Surname = Surname
self.Gender = Gender
self.Age = Age
self.Father = Father
self.Mother = Mother
class family :
def __init__ (self,Surname,Members) :
self.Surname = Surname
self.Members = Members
def ListMembers (self) :
for member in self.Members :
print (self.Surname +' family member ' +member.Name + ' '+ member.Surname + ' is ' + member.Age + " years old ")
human1 = human( 'Mary','Walter','Female','42','Jason White','Sally White')
human2 = human('Charles', 'Walter', 'Male', '45', 'Samuel Ribbon', 'Angie Ribbon')
human3 = human('Claire', 'White', 'Female', '16', 'Charles Walter', 'Mary Walter')
WalterFamily = family('Walter',(human1,human2,human3))
WalterFamily.ListMembers
When i run this code WalterFamily.ListMembers prints nothing.
What is exact way of nesting objects in another object ?
Like i tried to do here : Nesting 3 humans inside a family and also reach to the attributes of these objects..
The last line of code is not what you want. All functions that belong to a class (like your ListMembers function) is a function stored in a variable name.
When you have what you currently have:
WalterFamily.ListMembers
You only print the function object stored at the ListMembers variable. To actually call it, you need to add parenthesis.
WalterFamily.ListMembers()
In Python everything is an object, including functions. So WalterFamily.ListMembers is just a reference to the object. To call the function you need WalterFamily.ListMembers()
I'm working on a small fighting game as a learning experience and right now I'm working on implementing a store where you can buy weapons.
I decided to use a class for the store and have everything that you can do in it as a class method. But I'm unsure how to get all the data from my Weapon class and use it in the Store class. It's not pretty but here's what I have so far:
Sorry for misspelling tier.
class Item(object):
'''Anything that can be used or equiped.'''
def __init__(self, _id, desc, cost):
self._id = _id
self.desc = desc
self.cost = cost
class Weapon(Item):
def __init__(self, _id, desc, dam):
self._id = _id
self.desc = desc
self.dam = dam
def __str__(self):
return self._id
class Store(object):
dagger = Weapon('Dagger', 'A small knife. Weak but quick.', 'd4')
s_sword = Weapon('Short Sword', 'A small sword. Weak but quick.', 'd6')
l_sword = Weapon('Long Sword', 'A normal sword. Very versatile.', 'd8')
g_sword = Weapon('Great Sword', 'A powerful sword. Really heavy.', 'd10')
w_teir_1 = [dagger, s_sword, l_sword]
w_teir_2 = [w_teir_1, g_sword]
def intro(self):
print 'Welcome, what would you like to browse?'
print '(Items, weapons, armor)'
choice = raw_input(':> ')
if choice == 'weapons':
self.show_weapons(self.w_teir_1)
def show_weapons(self, teir):
for weapon in teir:
i = 1
print str(i), '.', teir._id
i += 1
raw_input()
I can't get the show_weapon function to print the _id for the weapon. All I can do is get it to print the raw object data.
Edit: I've figured out how to display the _id of the weapon when I'm passing the list w_teir_1 through the show_weapons method. But when I attempt to push w_teir_2 through, I get this error: AttributeError: 'list' object has no attribute '_id'
You need to change the last print stmt like below, since you're iterating over a list. _id attribute exists only for the elements which exists inside that list.
print str(i), '.', weapon._id
or
print str(i) + '.' + weapon._id
Update:
def show_weapons(self, teir):
for weapon in teir:
if isinstance(weapon, list):
for w in weapon:
i = 1
print str(i), '.', w._id
i += 1
raw_input()
else:
i = 1
print str(i), '.', weapon._id
i += 1
raw_input()
This is concatenated to the question I asked earlier today ("List" Object Not Callable, Syntax Error for Text-Based RPG). Now my dilemma resides in adding the herb to the player's herb list.
self.herb = []
is the starting herb list. The function collectPlants:
def collectPlants(self):
if self.state == 'normal':
print"%s spends an hour looking for medicinal plants." % self.name
if random.choice([0,1]):
foundHerb = random.choice(herb_dict)
print "You find some %s." % foundHerb[0]
self.herb.append(foundHerb)
print foundHerb
else: print"%s doesn't find anything useful." % self.name
with foundHerb being the random choice. How do I add this item to the list in a neat way (currently it prints the herb name, then "None") and allow for having several of the same herb?
Here's the herb class:
class herb:
def __init__(self, name, effect):
self.name = name
self.effect = effect
Sample list of herbs (warning: immaturity):
herb_dict = [
("Aloe Vera", Player().health = Player().health + 2),
("Cannabis", Player().state = 'high'),
("Ergot", Player().state = 'tripping')
]
Use a list.
self.herb = []
foundHerb = 'something'
self.herb.append(foundHerb)
self.herb.append('another thing')
self.herb.append('more stuff')
print 'You have: ' + ', '.join(self.herb)
# You have: something, another thing, more stuff
EDIT: I found the code from which you get foundHerb in one of your other questions (please post it in this question too!), which is:
foundHerb = random.choice(herb_dict)
When I look at herb_dict:
herb_dict = [
("Aloe Vera", Player().health == Player().health + 2),
("Cannabis", Player().state == 'high'),
("Ergot", Player().state == 'tripping')
]
This is wrong, use = for assignment. == is for testing equality.
You need to use a function in the second item in these tuples.
Don't add the second item into the list. Like this:
self.herb.append(foundHerb[0])
In your function, think what would happen if random.choice([0,1]) was 0. it would not run the if block, so no herb would ever be chosen. Perhaps in your function, you can return False to say that no herb was found. Then you can do this:
self.herb = []
myherb = collectPlants() # This will either contain a herb or False
if myherb: # If myherb is a plant (and it isn't False)
self.herb.append(myherb)