I am currently making a game in Python. Whenever you want help in the game, you just type help and you can read the help section.
The only problem is, I need to add a function block for each level.
def level_01():
choice = raw_input('>>>: ')
if choice=='help':
level_01_help()
def level_012():
choice = raw_input('>>>: ')
if choice=='help':
level_02_help()
So I was wondering if is possible to make a global function block for all the levels?
When you enter help, you get to help(), and then it automatically goes back to the function block you just came from.
I really hope you understand what I mean, and I would really appreciate all the help I could get.
You can actually pass the help function as a paramater, meaning your code can become:
def get_choice(help_func):
choice = raw_input('>>>: ')
if choice == 'help':
help_func()
else:
return choice
def level_01():
choice = get_choice(level_01_help)
def level_02():
choice = get_choice(level_02_help)
Ideally you should have a separate Module for all interface related tasks, so that the game and interface will be two seperate entities. This should make those 2911 lines a bit more legible, and if you decide to change Interfaces (from Command Line to Tkinter or Pygame for example) you will have a much much easier time of it. Just my 2¢
A really nice way to handle this kind of problem is with the built in python help. If you add docstrings to your function, they are stored in a special attribute of the function object called doc. You can get to them in code like this:
def example():
'''This is an example'''
print example.__doc__
>> This is an example
you can get to them in code the same way:
def levelOne():
'''It is a dark and stormy night. You can look for shelter or call for help'''
choice = raw_input('>>>: ')
if choice=='help':
return levelOne.__doc__
Doing it this way is a nice way of keeping the relationship between your code and content cleaner (although purists might object that it means you can't use pythons built-in help function for programmer-to-programmer documentation)
I think in the long run you will probably find that levels want to be classes, rather than functions - that way you can store state (did somebody find the key in level 1? is the monster in level 2 alive) and do maximum code reuse. A rough outline would be like this:
class Level(object):
HELP = 'I am a generic level'
def __init__(self, name, **exits):
self.Name = name
self.Exits = exits # this is a dictionary (the two stars)
# so you can have named objects pointing to other levels
def prompt(self):
choice = raw_input(self.Name + ": ")
if choice == 'help':
self.help()
# do other stuff here, returning to self.prompt() as long as you're in this level
return None # maybe return the name or class of the next level when Level is over
def help(self):
print self.HELP
# you can create levels that have custom content by overriding the HELP and prompt() methods:
class LevelOne (Level):
HELP = '''You are in a dark room, with one door to the north.
You can go north or search'''
def prompt(self):
choice = raw_input(self.Name + ": ")
if choice == 'help':
self.help() # this is free - it's defined in Level
if choice == 'go north':
return self.Exits['north']
A better solution would be to store the level you are on as a variable and have the help function handle all of the help stuff.
Example:
def help(level):
# do whatever helpful stuff goes here
print "here is the help for level", level
def level(currentLevel):
choice = raw_input('>>>: ')
if choice=='help':
help(currentLevel)
if ...: # level was beaten
level(currentLevel + 1) # move on to the next one
Sure, it's always possible to generalize. But with the little information you provide (and assuming "help" is the only common functionality), the original code is extremely straightforward. I wouldn't sacrifice this property only to save 1 line of code per level.
Related
So, I'm programming a game in python that has a lottery system. To this I have a function called lottery which is played whenever the lottery is used. This lottery system get a random number from 1 - 50 and then calls 1 of 50 lottery event functions which are every so slightly different.
The problem is that this function is basically unreadable, as it defines 51 separated functions, and the lottery function uses a list of 50 different if states to check which outcome it should use.
I've tried rewriting this code I don't know how many times now. First, I tried to refactor it using the built in typing module's overload descriptor, but overload requires the parameters to be different types for every different overload. This just led to me adding 50 different type classes which was even less efficient.
I then tried calling the function from the builtin globals dictionary, but my IDE would flag an error when running, even if the code worked. here's some pseudocode, since I no longer have the original code:
def lottery()
lotnumb = random number(range(50))
if lotnumb == 1:
lotevent1()
elif lotnumb == 2:
lotevent2()
elif lotnumb == 3:
lotevent3()
elif lotnumb == 4:
lotevent4()
elif lotnumb == 5:
lotevent5()
elif lotnumb == 6:
lotevent6()
elif lotnumb == 7:
lotevent7()
// this continues all the way to if statement number 50
You could easily put all the functions into a list and index it but if you want something more complex or just dont want to use them in a list here is a solution.
This seems to me like a situation where you could use a the __getattribute__ method in python classes. The way you could do this is by creating a lottery_event class and within it define each of your functions.
It would look like this.
class LotteryEvents:
#staticmethod
def lotevent1():
pass
#staticmethod
def lotevent2():
pass
#staticmethod
def lotevent3():
pass
# and so on...
Then what you can do is use python's dunder method __getattribute__ to be able to access each function. Calling __getattribute__ with the name of the function you want to use will return that function. You can add this as a method in the class like so.
class LotteryEvents:
#classmethod
def getLotEvent(cls, num):
# gets the function and stores it to the variable loteventfunc
loteventfunc = cls.__getattribute__(f'lotevent{num}')
return loteventfunc
# you could just call it from here
#staticmethod
def lotevent1():
pass
#staticmethod
def lotevent2():
pass
#staticmethod
def lotevent3():
pass
# and so on...
# then to call it you do this
random_num = random.randint(1, 50)
func = LotteryEvents.getLotEvent(random_num)
func()
There are many other ways to solve this problem, but this is how I would most likely tackle something like this. It does not remove having to define each function but it does remove that awful mess of else ifs and looks cleaner. If it still takes up too much space in your file I would recommend putting this class into a separate file and importing it into your project. Even without this solution putting it all in a different file could help organize your code.
Without knwoing more details about what is actually inside your loteventX functions, what I can suggest you is to add them into a list and randomly choose one and call, from inside you lottery function. Like this:
lottery_events = [
lotevent1,
lotevent2,
lotevent3,
lotevent1,
...
]
def lottery():
lotevent = random.choice(lottery_events)
lotevent()
However, I think there might be a simpler solution where you can parameterize your loteventX functions and not need to implement each of them independently. Please share more details if you want to explore that option.
import random
def f_1():
print("1")
def f_2():
print("2")
def f_3():
print("3")
def f_4():
print("4")
def applyFunction(func):
func()
if __name__=="__main__":
f_list = [f_1,f_2,f_3,f_4]
applyFunction(random.choice(f_list))
I apologize in advance for my confusing question.
I am attempting to write a simple text-based rpg for school, and I went into this with no prior knowledge of python or how to code in it. so after many youtube videos and tutorials and such, I got a pretty good base understanding. So one thing I wanted is for the character to have a name that you set just after the start. so to do this I did this
def character_namer ():
character_name = raw_input("please enter your name: ")
the problem is that now my variable 'character_name' can only be used within the def character_namer loop. How do I create a variable outside of that loop that represents 'character_name' so that any time I want to say the characters name I just type 'character_name'. Thanks.
Just return the value
def character_namer():
return raw_input("please enter your name: ")
character_name = character_namer()
print character_name
Either return the value within the function like what Paul Rooney suggested, or if you're inside idle or python shell simply do:
character_name = raw_input('please enter your name: ')
I'd say it's good practice to keep track of the game state in a class. Sure this might be a little over the top for "just" the character name, but it will be much more easy to extend and maintain.
class Game(object):
def __init__(self):
self.character_name = None
# eventually other important values
def ask_name(self):
self.character_name = raw_input('please enter your name: ')
def play(self):
self.ask_name()
print('Hello,' self.character_name)
# etc ...
game = Game()
game.play()
For my examine command, because I don't want to do this:
def examine(Decision):
if Decision == "examine sword":
print sword.text
elif Decision == "examine gold":
print gold.text
elif Decision == "examine cake":
print cake.text
...
for every item in my game.
So I wanted to convert the second word of the Decision string into a variable so that I could use something like secondwordvar.text.
I tried to use eval(), but I always get an errors when I make a spelling mistake in a single word command.
The error
IndexError: list index out of range
It's be working otherwise though.
Right now my code is this:
def exam(Decision):
try:
examlist = shlex.split(Decision)
useditem = eval(examlist[1])
print useditem.text
except NameError:
print "This doesn't exist"
Does anyone have an idea, for an other option, how I could write that function in a easy way?
I should probably also include the full game. You can find it here:
http://pastebin.com/VVDSxQ0g
Somewhere in your program, create a dictionary mapping the name of the object to a variable that it represents. For example:
objects = {'sword': sword, 'gold': gold, 'cake': cake}
Then you can change your examine() function to something like the following:
def examine(Decision):
tokens = shlex.split(Decision)
if len(tokens) != 2 or tokens[0] != 'examine' or tokens[1] not in objects:
print "This doesn't exist"
else:
print objects[tokens[1]].text
What you could do (because with my knowledge in programming, somewhat limited, this is the most advanced way I could see it) is to utilize dictionaries. I'll try to explain in english, because my knowledge of code in this field is suspect and I don't want to mislead you.
Dictionaries are very array-like, allowing you to associate a decision with a value.
You would be able to associate Examine sword with an action code 4
This would (in a hack-y way) allow you to convert your string to a variable, more by direct and consistent referencing of key/value pairs.
Good luck with this approach; Read up some on Dictionaries and you may very well find them easier to handle than it sounds!
Finally, as a form of good coding practice, never use eval() unless you are sure of what you are doing. eval() executes the code inside the (), so if, god forbid, some malicious process manages to run that code with a malicious line injected inside it:
eval(###DELETE EVERYTHING RAWR###)
You'll have a bad time. Sincerely.
Also, for the sake of evaluating code, I've heard that it is a very slow command, and that there are better alternatives, performance-wise.
Happy coding!
These two print the same text:
Using a dictionary:
texts = dict(sword = "wathever",
gold = "eachever",
cake = "whomever")
def examine_dict(decision):
decision = decision.split()[1]
print texts[decision]
examine_dict("examine sword")
Using object attributes (a class):
class Texts():
sword = "wathever"
gold = "eachever"
cake = "whomever"
def examine_attribute(decision):
decision = decision.split()[1]
text = getattr(Texts, decision)
print text
examine_attribute("examine sword")
Depending on what you want, one method can be more appropriate than the other. The dictionary-based method, however, is in general, the easier and the faster one.
Your variables are stored in a dictionary somewhere. If they are global variables, globals() returns this dictionary. You can use this to look up the variable by name:
globals()['sword'].text
If the variables are stored in a class as attributes, you can use getattr:
getattr(object, 'sword').text
You'll want to catch possible exceptions for bad names.
I think I have all the terms right in my question...but I'm not sure. Basically what I am trying to do is get the little details of my inventory and equipment set in a text game.
What I want to do is have a weapon, lets go with a sword, and i want it to change certain aspects of the character itself. Lets go with the hp of the character increases by like...5, what i would have in this case is, (don't mind the sloppy work!)
global basehp
basehp = 10
global armorhp
armorhp = 0
global skillhp
skillhp = 0
global hpmod
hpmod = (skillhp + armorhp)
global righthand
righthand = 0
so setting the example with hp this is what i actually have in my program, now what i want to do is change armorhp to match what the armor would change it to, say i have my sword that adds 5 to my hp right? what i am working with now is just not working, at first i thought to just have it equip and add 5 to armorhp, then i started thinking about UNequiping it...the effect would be the same...i shall show you what i mean
def hpsword:
armorhp = (armorhp + 5)
def prompt():
x = raw_input("Type command>>")
if x == 'equip':
print "equip what?"
print inventory
y = raw_input(">>>>")
if y == 'sword':
if 'hpsword' in inventory:
global righthand
righthand = 1
inventory.remove ('hpsword')
equipmentlist.append ('hpsword')
Now from right here you'll notice i left some things out, like my lists, i'm getting to that but this is where i can really explain more of what i mean, i want to use each item like that, but i can't figure out how...i'm confusing i know, i have a lot on my mind, but i want to just do something like....
def this sword():
this sword does this thing!
then when it gets equiped add the effect it does, such as +5 health, then remove it and take the effect with it, without having to do 100 lines of code with a bunch of statements that are oh add this effect but then when you don't want to use it anymore reset righthand to 0 and then if it was this number there take this many away from that before you change it to number 2 and add 10 more.
i'm also trying to avoid the equip sword, gain 5 health, unequip, still at 5? good, equip, YAY 10!!!
if i make any sense at all i'm shocked...but if anyone can figure out what i mean and give me a few pointers that would be great!
EDIT:
Reading up on what was left in a comment, (Thanks for that Eevee!) I think I am starting to figure out classes a little...very little.
New question then, would i then make a class for the main character, like,
class character(object):
then add in the init
def __init__(self)
then would i define inventory like,
def __init__(self)
def inventory
or would i do this,
def __init__(self, inventory)
sorry if i ask too many questions, just trying to get it figured out and i've noticed people here are nice enough to at least look at questions from wannabe programmers like myself :D
ANOTHER EDIT!!!!
Again, thanks a ton Eevee! That makes some sense, though reading through it i'm still a little confused as to how i would add it to my code and then what i would need to change for some different items.
To try and explain some, when i say different items, you gave me
class Sword(object):
def visit_health_bonus(self, character):
return 5
and if i wanted a sword that did something different i would just create a new class with a different name, but then basically the same thing?
class Sword2(object):
def visit_strength_bonus(self, character):
return 2
and that would give me a sword that would add 2 to my strength right?
now onto my other question, when i attempt to use this, what would i really need to get it to work right, i'm seeing a lot of things that i don't think would run without having some other bits
class Sword(object):
def visit_health_bonus(self, character):
return 5
# Then, every turn, recalculate health:
health = player.base_health
for equipment in player.equipment: //would i need another class here? (player)
# Pretend this lists everything the player has equipped; you might
# have to check each hand manually
health += equipment.visit_health_bonus(player) //also going to think a class (equipment)
It sounds like you are missing out on some of the awesome utility of object oriented programming, similar to what Marcin said you might what to make a series of classes like so:
class Weapon:
def __init__(self, name, armor_hp_bonus = 0, skill_hp_bonus = 0):
self.skill_hp_bonus = skill_hp_bonus
self.armor_hp_bonus = armor_hp_bonus
self.__name__ = name
class Hero:
def __init__(self):
self.basehp = 10
self.armorhp = 0
self.skillhp = 0
self.righthand = None
self.inventory = []
def choose_weapon(self):
while True:
for idx, item in enumerate(self.inventory):
print '{i}: {weapon}'.format(i=idx, weapon=item.__name__)
choice = raw_input("Pick your weapon: ")
try:
self.equip(self.inventory.pop(int(choice)))
return
except IndexError:
print "Invalid choice"
continue
def equip(self, weapon):
if self.righthand:
print "Weapon already equipped to right hand!"
return
else:
self.righthand = weapon
self.armorhp += weapon.armor_hp_bonus
self.skillhp += weapon.skill_hp_bonus
def unequip(self):
if not self.righthand:
print "No weapon to unequip!"
return
self.skillhp -= self.righthand.skill_hp_bonus
self.armorhp -= self.righthand.armor_hp_bonus
self.inventory.append(self.righthand)
self.righthand = None
def acquire_weapon(self, weapon):
self.inventory.append(weapon)
def calc_effective_hp(self):
return self.basehp + self.skillhp + self.armorhp
A class lets you keep all the variables you are having trouble keeping track of in one place. The skill bonus of one weapong will never get confused with the skill bonus of another weapon this way, because the variable storing that information is contained within the object. One you set up your classes correctly, the rest of your code is a lot shorter and cleaner because you can just call a method and rest assured that everything is being done correctly:
sword_of_1000_truths = Weapon('sword_of_1000_truths', skill_hp_bonus = 1337)
gandalf = Hero()
gandalf.acquire_weapon(sword_of_1000_truths)
gandalf.choose_weapon()
>> 0 sword_of_1000_truths
>> Pick your weapon: 0
print gandalf.calc_effective_hp()
>> 1347
gandalf.unequip()
print gandalf.calc_effective_hp()
>> 10
The methods such as equip and unequip make sure to increment and decrement the Hero's hp correctly, while keeping track of the logic of the game, i.e. you cant have two weapons in one hand, and you cant unequip a weapon if you don't have one equipped etc. This helps eliminate those ugly 100 lines of code you mention.
Good luck! Hope this helps
I'm assuming you're familiar with objects. If not, you should really read up on them (LPTHW is really good) before continuing, as they're pretty central to Python. If so, you should really be using a Player object instead of half a dozen globals.
The really easy approach is to have the sword be an object that does stuff when equipped and unequipped:
class Sword(object):
health_bonus = 5
def on_equip(self, character):
character.health += self.health_bonus
def on_unequip(self, character):
character.health -= self.health_bonus
Then you call on_equip when the player equips the sword, and on_unequip when the player unequips it. Other weapons can have similar event hooks, maybe for different stats.
That's all well and good, but something about it bugs me: you have to remember to call on_unequip whenever the player loses an item, and there might be more than one way that happens. You also have to track the player's "base" health separately, and if it changes, updating the "calculated" health requires unequipping and reequipping the weapon, which doesn't make a lot of sense.
So let's try something else. The "visitor pattern" Marcin referred to means (roughly) to loop through all the parts of a larger whole and call the same method on them. In this case, you want to ask all of the player's equipment how much extra health it grants.
class Sword(object):
def visit_health_bonus(self, character):
return 5
# Then, every turn, recalculate health:
health = player.base_health
for equipment in player.equipment:
# Pretend this lists everything the player has equipped; you might
# have to check each hand manually
health += equipment.visit_health_bonus(player)
Now you calculate the total health every time it's needed, so you don't have to worry about updating it when, say, the player levels up. You also don't have to worry about doing any extra bookkeeping when something gets unequipped: as soon as it disappears from the list of equipment, it stops contributing.
This is still a little inflexible, as it doesn't easily support e.g. a sword that doubles your attack power when fighting dragons. The most general way to solve this involves a lot of tangling and extra objects and figuring out what effects need to happen first, and I've yet to make it work to my satisfaction. Hopefully the above approaches are a good enough starting point. :)
The most flexible way to solve this is to have the action of "attack with equipped weapon" itself be an object and have equipment, the player, the target, etc. all react to the attack by increasing stats. But then you have questions of priority that are really fiddly to resolve so let's not do that.
A couple of things:
You have 5 global variables. These should be moved into a class, and your function become a method on that class.
The usual way to work something like this is for the sword to support a particular interface defining what modifications it makes, which the character class can read. Alternatively, use the visitor pattern, and have the character pass itself to a method of the sword on equip/unequip.
I want a function that can return the variable/object name as str like this :
def get_variable_name (input_variable):
## some codes
>>get_variable_name(a)
'a'
>>get_variable_name(mylist)
'mylist'
it looks like silly but i need the function to construct expression regarding to the variable for later on 'exec()'. Can someone help on how to write the 'get_variable_name' ?
I've seen a few variants on this kind of question several times on SO now. The answer is don't. Learn to use a dict anytime you need association between names and objects. You will thank yourself for this later.
In answer to the question "How can my code discover the name of an object?", here's a quote from Fredrik Lundh (on comp.lang.python):
The same way as you get the name of that cat you found on your porch:
the cat (object) itself cannot tell you its name, and it doesn’t
really care — so the only way to find out what it’s called is to ask
all your neighbours (namespaces) if it’s their cat (object)…
….and don’t be surprised if you’ll find that it’s known by many names,
or no name at all!
Note: It is technically possible to get a list of the names which are bound to an object, at least in CPython implementation. If you're interested to see that demonstrated, see the usage of the inspect module shown in my answer here:
Can an object inspect the name of the variable it's been assigned to?
This technique should only be used in some crazy debugging session, don't use anything like this in your design.
In general it is not possible. When you pass something to a function, you are passing the object, not the name. The same object can have many names or no names. What is the function supposed to do if you call get_variable_name(37)? You should think about why you want to do this, and try to find another way to accomplish your real task.
Edit: If you want get_variable_name(37) to return 37, then if you do a=37 and then do get_variable_name(a), that will also return 37. Once inside the function, it has no way of knowing what the object's "name" was outside.
def getvariablename(vara):
for k in globals():
if globals()[k] == vara:
return k
return str(vara)
may work in some instance ...but very subject to breakage... and I would basically never use it in any kind of production code...
basically I cant think of any good reason to do this ... and about a million not to
Here's a good start, depending on the Python version and runtime you might have to tweak a little. Put a break point and spend sometime to understand the structure of inspect.currentframe()
import inspect
def vprint(v):
v_name = inspect.currentframe().f_back.f_code.co_names[3]
print(f"{v_name} ==> {v}")
if __name__ == '__main__':
x = 15
vprint(x)
will produce
x ==> 15
if you just want to return the name of a variable selected based on user input... so they can keep track of their input, add a variable name in the code as they make selections in addition to the values generated from their selections. for example:
temp = raw_input('Do you want a hot drink? Type yes or no. ')
size = raw_input('Do you want a large drink? Type yes or no. ')
if temp and size == 'yes':
drink = HL
name = 'Large cafe au lait'
if temp and size != 'yes':
drink = CS
name = 'Small ice coffee'
print 'You ordered a ', name, '.'
MJ
If your statement to be used in exec() is something like this
a = ["ddd","dfd","444"]
then do something like this
exec('b = a = ["ddd","dfd","444"]')
now you can use 'b' in your code to get a handle on 'a'.
Perhaps you can use traceback.extract_stack() to get the call stack, then extract the variable name(s) from the entry?
def getVarName(a):
stack = extract_stack()
print(stack.pop(-2)[3])
bob = 5
getVarName(bob);
Output:
getVarName(bob)