For an online course in python, I'm making a basic text-based adventure game in python.
Right now, I have a rudimentary inventory system that works through booleans for if the user has an object or not, and integers for limited items, such as ammo and whatnot.
Here is the code for the inventory system
def Inventory(self): #The inventory for the game. I don't know how to program it properly, so this is my testing ground.
#This will hold the boolean values to if the player has the items or not. Another will be used to show the user the items
street_clothes = False
pistol = False
ammo = 0
phone = False
And this is the code where I am trying to modify the inventory function above
#Eric's apartment
def Eric_Apartment(self):
print "type in grab your gun"
action = raw_input("> ")
if action == "grab":
self.Inventory(CR97) = True
# self.CR97_ammo += 15
# print CR97_ammo
# print self.CR97_ammo
exit(1)
Attempting to run this program gets me this error:
python ex43.py
File "ex43.py", line 78
self.Inventory(CR97) = True
SyntaxError: can't assign to function call
Is there something else I'm supposed to do? I'm very new to python, and this is my first project on my own.
Here is the entire code, for reference
from sys import exit #allows the program to use the exit(1) code
from random import randint #allows the program to use a random number
class Game(object):
#quotes that pop up if the person dies, and also defines the start and self variables
def __init__(self, start):
self.quips = [
"You lose!"
]
self.start = start
def Inventory(self): #The inventory for the game.
#This will hold the boolean values to if the player has the items or not.
street_clothes = False
pistol = False
ammo = 0
phone = False
#this function launches the game, and helps with the room transfer
def play(self):
next = self.start
while True:
print "\n---------"
room = getattr(self, next)
next = room( )
#if the user dies, or fails at the game, this is the function that is ran
def death(self):
print self.quips[randint(0, len(self.quips)-1)]
exit(1)
#Welcome screen to the game
def welcome_screen(self):
print " place holder"
return 'intro_screen'
#Intro screen to the game
def intro_screen(self):
print "place holder"
action = raw_input("> Press any key to continue ")
return 'Eric_Apartment'
#Eric's apartment
def Eric_Apartment(self):
print "type in grab your gun"
action = raw_input("> ")
if action == "grab":
self.Inventory(CR97) = True
# self.CR97_ammo += 15
# print CR97_ammo
# print self.CR97_ammo
exit(1)
a_game = Game("welcome_screen")
a_game.play()
That's an amazingly perverse way to go about it. Why are you using a function to store data?
Just have a player object, with an inventory array.
I'd recommend using objects to model items too. Good use for for a class hierarchy. COuld have a base Item, with Subclasses SingleItem and StackableItem, etc.
Instead of using a function, try using a class -
class Player:
def __init__(self):
self.street_clothes = False
self.pistol = False
self.ammo = 0
self.phone = False
def give_street_clothes(self):
self.street_clothes = True
# etc
But personally, instead of using each item as a boolean, I'd use a list of items:
class Player:
def __init__(self):
self.inventory = []
# add code for ammo/pistol
def has_item(self, item):
return item in self.inventory
def give_item(self, item):
self.inventory.add(item)
def remove_item(self, item):
self.inventory.remove(item)
# etc
Related
I'm working on a simple skeleton for a game, and in an effort to try and be more "pythonic", I'm using objects/classes/dictionaries to try and capture all my actions/behaviors (as methods over functions, etc).
For some reason, every time I execute the method 'act' within the class "Player", the dictionary embedded within act runs all of its values (which are, in turn, methods from within the same instance of the class "Player"). In other words, the player chooses "attack, heal, and flee" every time, all at once, before being prompted.
I'm sure there's a simple explanation, but I've been looking for hours and can't find another example of someone's dictionary auto-running all the methods embedded within. Can you help?
Thanks!
- Jake
from random import randint
### BEGIN ALL CLASSES HERE
# To be used for all game objects (living and non-living)
class gameObject(object):
def __init__(self, name):
self.name = name
# To be used for all characters who can act in some way/be killed/change
class livingThing(gameObject):
def __init__(self, name, HP=1):
self.name = name
self.HP = HP
# The playable character(s)
class Player(livingThing):
def __init__(self,name="The Stranger", HP=4, MP=5, strength=1, intellect=1, spirit=1, luck=5, gil=6):
self.name = name
self.HP = HP
self.MP = MP
self.gil = gil
self.strength = strength
self.intellect = intellect
self.spirit = spirit
self.luck = luck
def act(player, enemy):
actions = {
"attack" : player.attack(enemy),
"heal" : player.heal(enemy),
"flee" : player.flee()
}
#Takes input from the player
decision = input("What would you like to do? ")
if decision.lower() in actions:
actions[decision.lower()]
else:
print("That didn't work! Try again.")
# Prints both player and enemy HP
def printHP(player, enemy):
print("{0}'s' HP: {1} \n{2}'s HP: {3}".format(player.name, player.HP, enemy.name, enemy.HP))
# Allows the player to attack an enemy (currently functional)
def attack(player, enemy):
enemy.HP -= player.strength
print("You strike {0} for {1} damage!".format(enemy.name, player.strength))
player.printHP(enemy)
# Allows the player to heal a certain amount of health based on its "spirit" stat (currently functional)
def heal(player, enemy):
healed = randint(0, player.spirit)
player.HP += healed
print("You've healed for {0}!".format(healed))
player.printHP(enemy)
#Allows the player to attempt to run away
def flee(player):
randluck = randint(0, player.luck)
if randluck > 3:
print("You successfully escaped!")
return player.HP
else:
print("You weren't able to escape!")
# Anything that can act with/against the player
class Actor(livingThing):
def __init__(self, name="Unknown Entity", HP=10, MP=2, gil=3):
self. name = name
self.HP = HP
self.MP = MP
self.gil = gil
### END ALL CLASSES ###
### DICTIONARIES CONTAINING ACTIONS ###
### CHARACTERS ###
fighter = Player()
monster = Actor()
fighter.act(monster)
I see the problem. When you are executing Python code, and you have a dictionary as you do, Python evaluates the dictionary fully. If you wanted your values (in your key:value) pairs to be the results of those methods, this is surely one way to do it.
In your case, what you can do is reference the function itself, and not invoke it. You can do this by getting rid of the parentheses, like this:
player.attack
instead of
player.attack()
Then, to call the function you can do something like
actions[decision.lower()](enemy)
Since one of your functions, flee, doesn't accept any parameters, you could give flee a parameter that you simply don't use in the function. If you were designing many many methods that your player can act with, then one strategy would be to give them all only named parameters, like this:
def f1(enemy=None,something=None,foo=None):
if enemy is None:
raise Exception("enemy cannot be None")
#process_enemy
If however, you also have a very high amount of parameters, then you could do this:
def attack(**kwargs):
#kwargs is a dictionary of parameters provided to the function
enemy = kwargs.get('enemy',None)
if enemy is None:
raise Exception("enemy cannot be None")
def eat(**kwargs):
food = kwargs.get('food',None)
if enemy is None:
raise Exception("food cannot be None")
attack(enemy="someenemyobject")
eat(food="somefoodobject")
attack() # raises Exception
attack(food="somefoodobject") # raises Exception
food(enemy="someenemyobject") # raises Exception
food(food="somefoodobject",enemy="someenemyobject") # does not raise Exception
I am trying to figure out a way to change a global variable from False to True if function rope is called. With my existing code, what could I add to make this possible?
Also, the global variable that exists is called inventoryRope, and it starts off as False.
Here is my code:
def rope():
print("You pick up the rope.")
command = input("Type CONTINUE to carry on.")
if command == "CONTINUE":
nextScene()
Need to use global:
inventoryRope = False
def rope():
global inventoryRope
print("You pick up the rope.")
command = input("Type CONTINUE to carry on.")
if command == "CONTINUE":
inventoryRope = True
nextScene()
Your actual goal here appears to be tracking inventory and making its state accessible from multiple functions. In that context, your approach will work, but it doesn't scale well to arbitrary numbers of inventory items.
inventory_rope = False
def pick_up(item):
if item == "rope":
inventory_rope = True
def use(item):
if (item == "rope") and inventory_rope:
print("Used rope")
Note: Because it looks like you're keeping this very simple, I'm using strings for items here. There are certainly better ways to do this.
There are many potential ways to handle inventory; one would be to simply create a list to handle whatever the player picks up.
inventory = []
def pick_up(item):
print("You picked up", item)
inventory.append(item)
def use(item):
print("Used", item)
inventory.remove(item)
You could instead inherit from the built-in list type, if you want to build in additional/different behaviors.
class Inventory(list):
def append(self, item):
print("You picked up", item)
super().append(item)
inventory = Inventory()
inventory.append("rope")
Another possibility would be making the inventory an attribute of your player object, if there are other things the player can do that would make sense to build into a class.
class Player(object):
_inventory = None
def __init__(self):
# Note: Don't use mutable objects as default arguments
self._inventory = []
def pick_up(self, item):
print("You picked up", item)
self._inventory.append(item)
player = Player()
player.pick_up("rope")
I am trying to create a textgame in Python, and it is relying pretty heavily upon .json files. The most current problem is how to handle the picking up and dropping of items in the game. Conceptually, I think I can create a .json file containing player information (among them an inventory), update the inventory with the item's keyword, and delete that keyword from the room's .json file. I would do the opposite if the player were dropping an item.
My game.py:
import cmd
from room import get_room
from item import get_item
import textwrap
class Game(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
#Start the player out in Room #1 by calling the get_room() function
#passing the integer '1' with it, and reading 1.json.
self.loc = get_room(1)
# Displays the room to the player.
self.showRoom()
def move(self, dir):
#Grabs the direction the layer is moving, runs it through the
#_neighbor function within the room module. Determines if there
#is a room in that direction. If so, update to that new room an
#display it.
newroom = self.loc._neighbor(dir)
if newroom is None:
print("You can't go that way.")
else:
self.loc = get_room(newroom)
self.showRoom()
def showRoom(self):
#Displays the current room the player is in, as well as any other
#Objects within it. (Items, enemies, NPCs, etc)
print(self.loc.name)
print("")
#Wraps the text up to 70 words per line.
for line in textwrap.wrap(self.loc.description, 70):
print(line)
print("")
#Looks in the room's .json file to see if the room has a key to
#an item. If it does. Display it on the ground.
if self.loc.haveItem != "None":
print(self.loc.onGround)
print("")
#Looks to the room's .json file to see if the room has a key to
#'showNeighbors.' If it does, display the possible exits of that
#room.
print("Possible Exits:")
print(self.loc.showNeighbors)
def do_look(self, args):
#An exact copy of the showRoom function. There has to be a way to
#point do_look to showRoom() to keep from executing duplicate form.
#This is just bad form.
"""Reprints the rooms description and available exits."""
print(self.loc.name)
print("")
for line in textwrap.wrap(self.loc.description, 70):
print(line)
print("")
if self.loc.haveItem != "None":
print(self.loc.onGround)
print("")
print("Possible Exits:")
print(self.loc.showNeighbors)
def do_get(self, args):
#A function that handles getting an item off the room's ground.
#Currently not functioning as intended. Is meant to find the item
#In the room's .json file, use it to open the items .json file,
#and grab the 'keywords of said item. Then checks to see if
#get <keyword> was typed. If so, edit room's .json file to remove
#item from room, and add it to player inventory. Removing the
#.json file is neccessary to ensure item does not diplay in room
#once picked up.
"""type 'pick <item>' to pick up item in room."""
itemToTake = args.lower()
keywords = self.loc.keywords
if itemToTake == "":
print ("Take what? Type 'look' to see the items to take.")
return
if (itemToTake == keywords[0]) or (itemToTake == keywords[1]):
if self.loc.canTake == "True":
print("You have picked up a " + self.loc.itemName + ".")
#code should be placed here to wipe current room's .json
#file to ensure room is devoid of any items.
else:
print("That item is not here.")
def do_drop(self, args):
#A function that will handle dropping an item from a player's
#inventory. If an item is dropped in inventory, it will be removed
#from players .json inventory file, and added as a key:value
#to the room's .json file, so that should the player return, the
#item will load up as intended.
pass
def do_inv(self):
"""Opens up your inventory"""
#Hasen't been implimented yet. Would like to have it done through a
#.json file. The file would hold other players attributes such as:
#Hit Points, Strength, Dexterity, Armor Class, and the like.
#Self explainatory functions:
def do_quit(self, args):
"""Quit the game"""
print("Thank you for playing.")
return True
def do_n(self, args):
"""Goes North"""
self.move('n')
def do_s(self, args):
"""Goes South"""
self.move('s')
def do_e(self, args):
"""Goes East"""
self.move('e')
def do_w(self, args):
"""Goes West"""
self.move('w')
if __name__ == "__main__":
play = Game()
play.cmdloop()
my room.py:
import json
from item import get_item
"""
This module handles all of the rooms in the game. It searches for a .json file with the approriate id number, then opens it, reads it line by line, and stores it in a dictonary. The Room class then takes the information and sorts it out in to the corrisponding variables. If there is no string listed, it will list the default strings declared in the parameters.
"""
def get_room(id):
ret = None
with open(str(id) + ".json", "r") as file:
jsontext = file.read()
dictonary = json.loads(jsontext)
dictonary['id'] = id
ret = Room(**dictonary)
return ret
class Room():
def __init__(self, id = 0, name="A Room", description = "An Empty Room", neighbors = {}, showNeighbors = "None", haveItem = "None"):
#This is a mess. Has to be a better way to do all this rather than
#place it all in the initiate function. Function assigns .json file
# dictonaries to seperate variables. This is fine.
self.id = id
self.name = name
self.description = description
self.neighbors = neighbors
self.showNeighbors = showNeighbors
self.haveItem = haveItem
#This checks to see if room has an item. If so, grab it's .json file
#and assign it's values to variables. Fell that this SHOULD NOT be
#in __init__. Unsure how to do it any other way.
if haveItem != "None":
item = get_item(haveItem)
self.onGround = item.onGround
onGround = self.onGround
self.keywords = item.keywords
keywords = self.keywords
self.canTake = item.canTake
canTake = self.canTake
self.itemName = item.itemName
itemName = self.itemName
def modifiyRoom(self, id = 0, haveItem = "None"):
pass
#Function used to modify room .json files. uses include:
#Adding a dropped item.
#Removing an item dropped within room.
#Detect if enemy entered or left room.
#At least it would do all of that. If I can get it to work.
def _neighbor(self, direction):
if direction in self.neighbors:
return self.neighbors[direction]
else:
return None
def north(self):
return self._neighbor('n')
def south(self):
return self._neighbor('n')
def east(self):
return self._neighbor('n')
def west(self):
return self._neighbor('n')
and item.py:
import json
"""
This module handles all of the items in the game. It searches for a .json file with the approriate id, then opens it, reads it line by line, and stores it in a dictonary. The Item class then takes the information and sorts it out in to the corrisponding variables. If there is no string listed, it will list the default strings declared in the parameters.
"""
def get_item(itemName):
ret = None
with open(itemName + ".json", "r") as file:
jsontext = file.read()
item = json.loads(jsontext)
item['itemName'] = itemName
ret = Item(**item)
return ret
class Item():
def __init__(self, itemName = "", name = "An Item", onGround = "On ground", description = "A sweet Item", canTake = "False", keywords = "None", value = 0):
#Handles all of the variables found in an item's .json file.
#Feel this will have to act as a super class for more unique
#items, such as weapons, which will hold a damage value.
self.itemName = itemName
self.name = name
self.onGround = onGround
self.description = description
self.canTake = canTake
self.keywords = keywords
self.value = value
A sample room.json:
{"name" : "Disposal Room",
"description" : "A powerful burst of pain in your head wakes you up from your brief slumber, the intense throbbing causing you to see everything in a dull haze. It takes a few moments for the pain to die down, and with it your vision eventually returns to normal. After some examining you discover that you are in some sort of forgotten cell, the prison door to the west of you unlocked and slightly ajar.",
"neighbors" : {"s" : 2},
"showNeighbors" : "south",
"haveItem" : "sword"
}
Let's say I have a spell named heal. How can I prevent a user from spamming heal every time they are damaged. I have considered applying this to individual combat functions; however, I am not sure how to implement a global rule for this? This code may clear it up:
available_spells = ['fireball', 'heal']
equipped = {'Weapon': "Staff",
'Armor': "Robes",
'Spells': ['fireball', 'heal']}
print "Your available spell(s) is(are) '%s'. " % equipped["Spells"]
inp = raw_input("Type the name of a spell you want to use.: ").lower()
lst = [x for x in available_spells if x.startswith(inp)]
if len(lst) == 0:
print "No such spell"
print ' '
elif len(lst) == 1:
spell = lst[0]
print "You picked", spell
#COMBAT FUNCTIONS HERE
else:
print "Which spell of", equipped["Spells"], "do you mean?"
If I were to make a class that defines certain actions for spells to take, how could I implement that into the code I have? For example if I have a class of spells, with functions defining damage rules, cool down times, etc., how could I reference that function in the code I already have? i.e. the player types 'heal' and I want it to reference an above class that has those values defined to check if the player recently played the spell, and what it does when played.
Am I clear enough in this question? How should I write a spell cool-down mechanic? How can I implement this mechanic into the code above?
Instead of storing all available spells as a list, you could store them as a dictionary, which allows you to also store the desired cooldown duration:
available_spells = {
# spell name: cooldown duration in seconds
'fireball': 3.0,
'heal': 5.0,
}
Each player could have another dict that keeps track of the last time they cast each spell. When the game starts, it would be empty:
cast_spells = {}
When the player attempts to cast a spell, check if the spell name is in the cast_spells dict. If it's not, then they have not yet cast it this game, so they are allowed to cast it:
if spell_name not in cast_spells:
cast_spells[spell_name] = datetime.now()
Otherwise, if the spell name is in the cast_spells dict, check if the required cooldown has elapsed:
elif cast_spells[spell_name] + datetime.timedelta(seconds=spells[spell_name]) < datetime.now():
cast_spells[spell_name] = datetime.now()
Otherwise, the cooldown is still in effect.
else:
print 'Spell not ready.'
I would probably do it using with, an exception handler, and a simple timer. That way you can just repeat the cooldown pattern, have shared cooldowns (like shown below), or even global cooldowns, etc.
Here are the classes:
import time
class CooldownException(Exception):
pass
class Cooldown(object):
def __init__(self, seconds):
self.seconds = seconds
self.expire = None
def __enter__(self):
if not self.expire or time.time() > self.expire:
self.expire = time.time() + self.seconds
else:
raise CooldownException('Cooldown not expired!')
def __exit__(self, type, value, traceback):
pass
heal_cooldown = Cooldown(5)
def heal():
try:
with heal_cooldown:
print 'You heal yourself!'
except CooldownException as e:
print e
def apply_bandage():
try:
with heal_cooldown:
print 'You bandage yourself!'
except CooldownException as e:
print e
def drink_potion():
try:
with heal_cooldown:
print 'You quaff a potion!'
except CooldownException as e:
print e
And here's how they're used:
>>> heal()
You heal yourself!
>>> time.sleep(3)
>>> drink_potion()
Cooldown not expired!
>>> time.sleep(3)
>>> apply_bandage()
You bandage yourself!
If I were to make a class that defines certain actions for spells to take, how could I implement that into the code I have?
As you guessed, your problem is very well suited to classes.
Am I clear enough in this question?
Yes.
Your program, but with classes
Here is your program modified to use two custom classes, FireballSpell and HealSpell. Each one has a .name, which is a string, and a .cast(), which is a custom behaviour. It's nearly identical to your original code, so it should be easy for you to understand:
available_spells = [FireballSpell(), HealSpell()]
equipped = {'Weapon': "Staff",
'Armor': "Robes",
'Spells': [FireballSpell(), HealSpell()]}
while True:
print "Your available spell(s) is(are) '%s'. " % [spell.name for spell in equipped["Spells"]]
inp = raw_input("Type the name of a spell you want to use.: ").lower()
lst = [spell for spell in available_spells if spell.name.startswith(inp)]
if len(lst) == 0:
print "No such spell"
print ' '
elif len(lst) == 1:
spell = lst[0]
print "You picked", spell.name
spell.cast()
else:
print "Which spell of", [spell.name for spell in equipped["Spells"]], "do you mean?"
print ""
Run it and give it a try! Here is the complete script. I'm pretty sure it does exactly what you want.
Specific spells
Each specific class has a name, cooldown time, and specific behaviour. The parent Spell class (see bottom) handles the rest.
class FireballSpell(Spell):
def __init__(self):
self.name = "fireball"
self.cooldown_seconds = 5
def spell_specific_behaviour(self):
# do whatever you like with fireball
# this is only called if the spell has cooled down
print "casting fireball"
class HealSpell(Spell):
def __init__(self):
self.name = "heal"
self.cooldown_seconds = 10
def spell_specific_behaviour(self):
# same applies here as from FireballSpell
print "casting heal"
Spell class
This is a generic Spell class - the parent of all spells. It knows the name, cooldown time, and behaviour from the specific spells (child classes above). It also has generic cooldown mechanic that's shared by the spells:
class Spell:
# spell data - filled in by specific spells
name = "default"
cooldown_seconds = 0
last_cast_time = 0
def cast(self):
# only cast the spell if it has cooled down
if self.is_cooled_down():
# call the behaviour set by the specific spell
self.spell_specific_behaviour();
# set the last cast time to the current time
self.last_cast_time = time.time()
else:
print self.name + " has not cooled down yet!"
def spell_specific_behaviour(self):
# implement in specific spell subclasses
return
def is_cooled_down(self):
current_time_seconds = time.time()
cooldown_expire_time_seconds = self.last_cast_time + self.cooldown_seconds
return current_time_seconds > cooldown_expire_time_seconds
Again, here is the whole thing in one working script. Have fun!
META: decorators, exceptions, and with blocks? Whoa, guys. OP is just now learning about classes. Let's keep it simple here.
Here is another example using decorators...
from functools import wraps
class Cooldown(object):
def __init__(self, seconds, cooldown_message):
self.seconds = seconds
self.expire = None
self.cooldown_message = cooldown_message
def decorator(self, fail_message_callback):
def _wrap_decorator(foo):
def _decorator(*args, **kwargs):
if not self.expire or time.time() > self.expire:
self.expire = time.time() + self.seconds
result = foo(*args, **kwargs)
return result
else:
if fail_message_callback:
fail_message_callback(self.cooldown_message)
return None
return wraps(foo)(_decorator)
return _wrap_decorator
heal_cooldown = Cooldown(5, 'Cooldown not expired!')
def display(message):
print message
#heal_cooldown.decorator(display)
def heal():
display('You heal yourself!')
#heal_cooldown.decorator(display)
def apply_bandage():
display('You bandage yourself!')
#heal_cooldown.decorator(display)
def drink_potion():
display('You quaff a potion!')
heal()
time.sleep(3)
drink_potion()
time.sleep(3)
apply_bandage()
I am on exercise 43 doing some self-directed work in Learn Python The Hard Way. And I have designed the framework of a game spread out over two python files. The point of the exercise is that each "room" in the game has a different class. I have tried a number of things, but I cannot figure out how to use the returned value from their initial choice to advance the user to the proper "room", which is contained within a class. Any hints or help would be greatly appreciated.
Apologies for the poor code, I'm just starting out in python, but at my wit's end on this.
Here is the ex43_engine.py code which I run to start the game.
from ex43_map import *
import ex43_map
import inspect
#Not sure if this part is neccessary, generated list of all the classes (rooms) I imported from ex43_map.py, as I thought they might be needed to form a "map"
class_list = []
for name, obj in inspect.getmembers(ex43_map):
if inspect.isclass(obj):
class_list.append(name)
class Engine(object):
def __init__(self, room):
self.room = room
def play(self):
# starts the process, this might need to go inside the loop below
next = self.room
start.transportation_choice()
while True:
print "\n-------------"
# I have tried numerous things here to make it work...nothing has
start = StartRoom()
car = CarRoom()
bus = BusRoom()
train = TrainRoom()
airplane = AirplaneRoom()
terminal = TerminalRoom()
a_game = Engine("transportation_choice")
a_game.play()
And here is the ex43_map.py code
from sys import exit
from random import randint
class StartRoom(object):
def __init__(self):
pass
def transportation_choice(self):
print "\nIt's 6 pm and you have just found out that you need to get to Chicago by tomorrow morning for a meeting"
print "How will you choose to get there?\n"
print "Choices: car, bus, train, airplane"
choice = raw_input("> ")
if choice == "car":
return 'CarRoom'
elif choice == "bus":
return 'BusRoom'
elif choice == "train":
return 'TrainRoom'
elif choice == "airplane":
return 'AirplaneRoom'
else:
print "Sorry but '%s' wasn't a choice." % choice
return 'StartRoom'
class CarRoom(object):
def __init__(self):
print "Welcome to the CarRoom"
class BusRoom(object):
def __init__(self):
print "Welcome to the BusRoom"
class TrainRoom(object):
def __init__(self):
print "Welcome to the TrainRoom"
class AirplaneRoom(object):
def __init__(self):
print "Welcome to the AirplaneRoom"
class TerminalRoom(object):
def __init__(self):
self.quips = [
"Oh so sorry you died, you are pretty bad at this.",
"Too bad, you're dead buddy.",
"The end is here.",
"No more playing for you, you're dead."
]
def death(self):
print self.quips[randint(0, len(self.quips)-1)] # randomly selects one of the quips from 0 to # of items in the list and prints it
exit(1)
Instead of returning a string try returning an object, ie
if choice == "car":
return CarRoom()
It might be a good idea to make a Room class, and derive your other rooms from it.
The Room base class can then have a class variable which automatically keeps track of all instantiated rooms.
I haven't thoroughly tested the following, but hopefully it will give you some ideas:
# getters.py
try:
getStr = raw_input # Python 2.x
except NameError:
getStr = input # Python 3.x
getStr.type = str
def typeGetter(dataType):
def getter(msg):
while True:
try:
return dataType(getStr(msg))
except ValueError:
pass
getter.type = dataType
return getter
getInt = typeGetter(int)
getFloat = typeGetter(float)
getBool = typeGetter(bool)
def getOneOf(*args, **kwargs):
"""Get input until it matches an item in args, then return the item
#param *args: items to match against
#param getter: function, input-getter of desired type (defaults to getStr)
#param prompt: string, input prompt (defaults to '> ')
Type of items should match type of getter
"""
argSet = set(args)
getter = kwargs.get('getter', getStr)
prompt = kwargs.get('prompt', '> ')
print('[{0}]'.format(', '.join(args)))
while True:
res = getter(prompt)
if res in argset:
return res
.
# ex43_rooms.py
import textwrap
import random
import getters
class Room(object):
# list of instantiated rooms by name
ROOMS = {}
#classmethod
def getroom(cls, name):
"""Return room instance
If named room does not exist, throws KeyError
"""
return cls.ROOMS[name]
def __init__(self, name):
super(Room,self).__init__()
self.name = name
Room.ROOMS[name] = self
def run(self):
"""Enter the room - what happens?
Abstract base method (subclasses must override)
#retval Room instance to continue or None to quit
"""
raise NotImplementedError()
def __str__(self):
return self.name
def __repr__(self):
return '{0}({1})'.format(self.__class__.__name__, self.name)
class StartRoom(Room):
def __init__(self, name):
super(StartRoom,self).__init__(name)
def run(self):
print textwrap.dedent("""
It's 6 pm and you have just found out that you need to get to Chicago
by tomorrow morning for a meeting! How will you get there?
""")
inp = getters.getOneOf('car','bus','train','airplane')
return Room.getroom(inp)
class CarRoom(Room):
def __init__(self,name):
super(CarRoom,self).__init__(name)
class BusRoom(Room):
def __init__(self,name):
super(BusRoom,self).__init__(name)
class TrainRoom(Room):
def __init__(self,name):
super(TrainRoom,self).__init__(name)
class PlaneRoom(Room):
def __init__(self,name):
super(PlaneRoom,self).__init__(name)
class TerminalRoom(Room):
def __init__(self,name):
super(TerminalRoom,self).__init__(name)
def run(self):
print(random.choice((
"Oh so sorry you died, you are pretty bad at this.",
"Too bad, you're dead buddy.",
"The end is here.",
"No more playing for you, you're dead."
)))
return None
# create rooms (which registers them with Room)
StartRoom('start')
CarRoom('car')
BusRoom('bus')
TrainRoom('train')
PlaneRoom('airplane')
TerminalRoom('terminal')
.
# ex43.py
from ex43_rooms import Room
def main():
here = Room.getroom('start')
while here:
here = here.run()
if __name__=="__main__":
main()