Python: If-elses inside a class "init"; does this work?
I'm yet another programming newb going through "Learn Python the Hard Way" and have come up against something that I don't fully understand.
I'll preface this by saying that while I'm really enjoying his lesson plan, one unfortunate side-effect seems to be that I don't understand a lot of the technical talk involved in the programming world. It took me two days to figure out what 'instantiating' is, and even now I only think I get it, heh. So hopefully the answer to my query hasn't already been asked in a more technically descriptive way, if it has I sorry for being redundant.
Anyway, what I'm trying to do here is complete a text adventure, using classes to describe each room. What I'm actually doing is importing an old work of mine to this newer, more "OOP"ish way of doing things - converting a program which was essentially 980 lines of function calls.
So previously I had these 'rooms' inside each function that would check to see if you'd met certain game goals to describe the room to you. Example:
def room_one():
if "flashlight" in pack:
print "You see an exit to the NORTH and a bear to the EAST."
elif "flashlight" not in pack:
print "It's too dark to see."
Boring room, but apt description. So in trying to do this using classes, I seem to have hit a wall. I'll show you the code I'm using to define the game 'runner' and the room classes.
class Runner(object):
def __init___(self):
self.goals = [] #an array for game achievements (talked_to_king, etc)
self.pack = [] #an array for game items (flask, key, etc)
def play(self, currentroom, gamestate):
while True:
print "\n--------"
print currentroom.desc #this line prints the room description, could
cmd = raw_input("> ") #it also be part of the problem?
gamestate.state = currentroom.actions(cmd)
if gamestate.state != "SAME":
currentroom = gamestate.state
else:
pass
class Room(object):
def __init__(self):
self.desc = "null" #short room description
self.lookdesc = "super null" #longer room description for the LOOK command
def actions(self, cmd):
if 'help' in cmd:
print """
'look' -- see your surroundings, including exits.
'get' -- to pick up an item (if it can be picked up).
'inventory' -- displays your collected items
'throw' -- some objects can be thrown
'talk' -- speak to someone
Other commands, or objects to interact with, will tend to
show up IN UPPERCASE. (but still type them in lowercase!)
Cardinal directions (north, south, east, west) for movement.
"""
return "SAME"
elif 'inv' in cmd:
print "--Inventory--\n"
for items in game.pack:
print items
print "-------------\n"
return "SAME"
elif 'look' in cmd:
print self.lookdesc
return "SAME"
elif 'goals' in cmd:
for goal in game.goals:
print goal
return "SAME"
else:
print "That won't work here."
return "SAME"
class Office(Room):
def __init__(self):
super(Office, self).__init__()
# here's the part that doesn't work
if 'principal' in game.goals:
self.desc = "You're in the principal's office, and there's a zombie in here!"
else:
self.desc = "You're in the principal's office."
self.lookdesc = "The walls in here are dingy from decades of smoking.\nA door to the WEST leads back to the foyer."
def actions(self, cmd):
if "west" in cmd and not "principal" in game.goals:
print "Buck up and talk to the old prune."
return "SAME"
elif "talk" in cmd:
print "You call to the principal."
game.goals.append('principal')
next = raw_input("--Mr. Friiiiinkseseeees...--")
print "But OH MY DAMN he's actually a zombie now."
next = raw_input("--Aww weak--")
return "SAME"
return super(Office, self).actions(cmd)
To check to make sure the "goals" array was being appended I did put a simple goal checking function in the Room parent class, and sure enough the game.goals gets appended. But my if-else statement doesn't seem to actually do anything; no matter what's in the goals array, I always get the else' desc when I come back to the room.
In fairness putting the if-else in the room init like that was sort of a shot in the dark, in fact I was surprised that I didn't get an error message back telling me that's not where those go! But the fact that it doesn't seem to care about the contents of game.goals ar开发者_如何学编程ray tells me that this isn't the best way to do this. How should I be implementing this?
As a subquestion - with a program like this, if the gamestate changes enough that a room's contents are dramatically altered, is it considered better to just have a whole other class for the altered room? Is it more important to maintain the "map" and to say that RoomThree(Room) is always RoomThree no matter what gamestate changes have occurred, or if by the time the game comes back to RoomThree the place is trashed, looted, on fire, full of vampires and smells of lettuce, should that just be a different RoomThree altogether? I kind of hope not, because it just seems more reasonable to have each room just 'be that room' and let the contents of things like game.goals and game.pack change the instance of the class instead of just making a new class for when certain conditions are met.
Anyway, this was longwinded. tl;dr - Am I doing it weird??
EDIT AGAIN: Cleaned up the main question's code to meet the new code as suggested by people here, also added the parent class "Room" to see if that isn't part of the problem. Still no solution, but I do believe the quality of the code has improved :) whee
FINAL EDIT(i hope): Aha, Stone's answer was again most helpful. Since I was returning "same" to my runner, it was using the old instance of Office. I needed to create a whole new instance to get it to recognize the change; by returning "Office()" instead of "SAME" I accomplished this. Thanks everyone for helping to not only solve the issue at hand but to make the code easier to read, and to help me change my thinking regarding my wordy if-else statements. Mwah :D
What happens when you have 100 rooms? Will your play function have 100 if statements, just to set the room you are in? (set it again, basically)
It would probably be much better to just set the game state directly to whatever you want when it changes.
edit: I just re-read what you have an now the problem seems kind of obvious, unless you've left out vital code.
The only time that you appear to add principal to to goals is inside of a member function of class Office. Therefore, by the time that you get around to adding principal to goals, it's too late, Office has already been initialized and won't be re-initialized unless you create a new instance of Office. You'll need to update the room's description after you add that goal.
I suppose by this:
if not 'principal' in game.goals:
you meant this:
if 'principal' not in game.goals:
or this:
if not ('principal' in game.goals):
not 'somestring'
is False
, because non-empty string is always True
and you don't have False
in your goals, so it always evaluates to False
.
Where Office objects is created? Because you are only checking the condition on object creating and principal is added to the goal after that, so if you use same object again, description wouldn't change.
精彩评论