Text-based RPG: How to apply cool downs to user input - python

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()

Related

Python printing an Else statement when not supposed to [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 months ago.
Improve this question
I'm having trouble trying to get a game creation exercise to stop printing the else statement (at the bottom of the code block). the idea is, you can navigate from room to room, but if you go in a direction you're not supposed it should tell you. However, it seems to be doing that even when you CAN go somewhere. I'd greatly appreciate any advice.
Code is below:
class Room:
number_of_rooms = 0
def __init__(self, room_name):
self.name = room_name
self.description = None
self.linked_rooms = {}
self.character = None
Room.number_of_rooms = Room.number_of_rooms + 1
def set_description(self, room_description):
self.description = room_description
def get_description(self):
return self.description
def set_name(self, room_name):
self.name = room_name
def get_name(self):
return self.name
def describe(self):
print(self.description)
def set_character(self, new_character):
self.character = new_character
def get_character(self):
return self.character
def describe(self):
print( self.description )
def link_room(self, room_to_link, direction):
self.linked_rooms[direction] = room_to_link
def get_details(self):
print(self.name)
print("--------------------")
print(self.description)
for direction in self.linked_rooms:
room = self.linked_rooms[direction]
print( "The " + room.get_name() + " is " + direction)
def move(self, direction):
if direction in self.linked_rooms:
return self.linked_rooms[direction]
else:
print("You can't go that way")
return self
I would greatly appreciate any advice on this, it's maddening. I just need it to stop printing "You can't go that way" when you can. It actually does work, it just insist on printing it every time you go into a new room as well as when you can't.
This is the code it links to
foyer = Room("foyer")
ballroom = Room("ballroom")
dining_hall = Room("dining hall")
kitchen = Room("kitchen")
foyer.link_room(ballroom, "south")
ballroom.link_room(foyer, "north")
ballroom.link_room(dining_hall, "east")
dining_hall.link_room(ballroom, "west")
dining_hall.link_room(kitchen, "north")
kitchen.link_room(dining_hall, "south")
If you add the following test code to the end of your class (assuming that it is inside a module Room.py):
if __name__ == "__main__":
print("Testing")
# rooms
room1 = Room("Floor")
room2 = Room("Kitchen")
room3 = Room("Living Room")
# link the rooms
room2.link_room(room1, "left")
room3.link_room(room1, "right")
# move
room2.move("up") # not allowed
room3.move("right") # should be possible
Then you can run the test code directly if you execute the module.
Now if you are in room 3 (living room) it is possible to get out on the right.
But if you are in room 2 (kitchen) you can only move to the left.
The test prints "You can't go that way" only if you do a move like room2.move("up") which is correct, because only "left" is allowed. Comment that line and you won't see that message any more.
According to this test, the class is behaving as it should. Note that you could (and should!) also write a unit test from the example I gave, asserting the expected output.
Update:
In your example, allowed moves are:
# allowed
kitchen.move("south")
dining_hall.move("north")
ballroom.move("east")
foyer.move("south")
And examples for not allowed moves are:
# not allowed
kitchen.move("west")
dining_hall.move("east")
ballroom.move("south")
foyer.move("north")
For these you will get "You can't go that way".
Maybe the issue you had was that you were using objects rather than strings as parameter to describe the direction.
For example, this will always fail (printing "You can't go that way"):
foyer.move(ballroom) # param should be a string, not an object
To prevent this kind of error, you could add a check to the move method:
def move(self, direction):
if not isinstance(direction, str):
raise ValueError("Direction needs to be a string!")
if direction in self.linked_rooms:
return self.linked_rooms[direction]
else:
print("You can't go that way")
return self
With that addition, the application will throw an exception, if you pass an object and not a string:
ValueError: Direction needs to be a string!
For interactive testing, you could use this code:
new_dir = "nop"
current_room = foyer
while new_dir != "":
print("current room: " + current_room.name)
print("please enter direction:")
new_dir = input()
if new_dir == "":
print("leaving")
break
new_room = current_room.move(str(new_dir))
if new_room != current_room:
print("moving to: " + new_room.name)
current_room = new_room
For debugging, it might be also helpful if you add the following code to the top of the module:
def Log(func):
def inner(*args, **kwargs):
print(f"Logging {str(func)}: {args[1:]}")
result = func(*args, **kwargs)
print(f"Result: {str(result)}")
return result
return inner
Now you can decorate any of the functions with the #Log attribute like so:
#Log
def __init__(self, room_name):
...
#Log
def move(self, direction):
...
Whenever such a decorated function is called, it will print it to the console, e.g.:
please enter direction:
south
Logging <function Room.move at 0x000001994B97C720>: ('south',)
Result: <main.Room object at 0x000001994B97A150>
moving to: ballroom
current room: ballroom
please enter direction:
If you're done with debugging, you can comment out the attributes (# #Log) to remove the extra print outs.

How do I make a class instance using user input?

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.

Python dictionary nested within method auto-executes all values (methods) when outer method is called

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

How can I turn a user input into a global function? (Python)

I'm trying to create my own text game, as instructed in Ex 45 of Learning Python the Hard Way, but I'm a bit stuck. I have my own inventory system set up and I need to figure out how to display a list of the inventory any time the user types "inventory" (I also want to do similar things for "help")... I know that I could add an if-statement after every raw_input for it, but I wanted to see if there was some global function or code that I could use to make this a lot easier on me.
I've searched and searched but I have no idea how to phrase the question or what I could possibly do to fix this. Here's my inventory set-up, with how I'm using to call it:
class Item(object):
def __init__(self, name, quantity=1):
self.name = name
self.raw = name.strip().lower()
self.quantity = quantity
def recalc(self):
self.netValue = self.quantity * self.value
class Container(object):
def __init__(self, name):
self.name = name
self.inside = {}
def __iter__(self):
return iter(self.inside.items())
def __len__(self):
return len(self.inside)
def __contains__(self, item):
return item.raw in self.inside
def __getitem__(self, item):
return self.inside[item.raw]
def __setitem__(self, item, value):
self.inside[item.raw] = value
return self[item]
def add(self, item, quantity=1):
if quantity < 0:
print "ERROR."
if item in self:
self[item].quantity += quantity
self[item].recalc()
else:
self[item] = item
inventory = Container("Inventory")
And here's what I'm using to call it when inventory is typed:
print "Inventory: [" + ",".join((item[1].name for item in inventory)) + "]"
You could create a function for each valid user input, such as inventory
Then, you could do:
input = raw_input('...')
self.method = getattr(<your class>, input)
self.method()
which would call
def inventory(self, ...):
pass
you could use a dictionary combined with lambdas, this would be compact and fast
from __future__ import print_function
#store commands
inputLookup = {"Inventory" : lambda: print ("Inventory: [" + ",".join((item[1].name for item in inventory)) + "]"), "other text" : lambda: 3} #and so on
text = input() #replace with hover you get input
inputLookup[text]() # call the function from dictionary at that command
the import will change all your print statements to a print() however.
For lambdas you can also do.
def f():
print "Inventory: [" + ",".join((item[1].name for item in inventory)) + "]"
inputLookup = {"Inventory": lambda: f()}
text = input()
try:
inputLookup[text]
except:
print "that is not a valid input" #or something
this will allow you to use the old print statment
yes, you should never use recursion to the same function in this manner, you'll generate a large stack. Also, those print statements are in a weird place.
def func1():
print "Uh oh."
print "You're outside the house and it's quiet."
print "You EXAMINE and find a ROCK."
While True:
userinput1 = raw_input(">>> ")
if userinput1 == "inventory":
print "Inventory: [" + ",".join((item[1].name for item in inventory)) + "]"
elif userinput1 == "pick up":
if rock not in inventory: # so they can't pick up infinite rocks
inventory.add(rock)
print "You picked up a ROCK."
else:
print "you've already picked up the rock"
elif userinput1 == "examine":
print "Man's best friend."
else:
print "Let's go on about our way then."
break
I failed to see you were just starting out. I would stay away from lambda's for the moment, as they're a bit more advanced, sorry for the confusion. Before, if you look at your if/elif/else tree, there was no way for the else statement to be called, so i've edited it a bit, i hope it functions the way you intended. Also, are inventory and rock globals? if not you would need to declare or reference them in the function. (although as a future note, staying a way from globals is generally good)
hmm...i assume you mean items like the rock and such.
Items = {'rock': Rock(), 'item2': Item2(), 'item3' : item3()} #where Item() is creating the object
Inventory.add(Items[itemname]) # adds the item
You could make Items as a global so you can access it everywhere. This is ok for now, but as you learn, you should generally stay away from globals.

How can I use a returned value to open up another instance of a class in Python?

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()

Categories

Resources