I have been dealing with this question for quite a time, so I decided to ask directly here for explanations/ what and where to read from.
From university we are doing a project where we use python and the cmd class to call the methods as arguments to the command line.
The lecturer has provided us with some example on how it could work and so on. However he didn't fully explained some details that I still miss about the arguments and how in general you can call the function as an argument in the command line prompt.
Here I will provide a shortened version of the code which he has written, as the general methods are the only thing included here in order to explain the situation in simpler manner.
import cmd
class Multiplayer(cmd.Cmd):
def __init__(self):
"""Init the object."""
super().__init__()
self.dice = dice.Dice() # dice object to call the roll() later
self.player1 = player.Player(1) # score = 0, index = 1
self.player2 = player.Player(2) # score = 0, index = 2
self.playerTurn = player.Player(1) # score = 0, index = 1
def do_start(self, _): #here is the part i dont fully understand
"""start a new game"""
print("You have started a new game.\n")
print("""
Welcome to the game. Type help or ? to list commands.\n
To set both players names, type: 'set_names' \n""")
self.__init__() #calling constructor so the new game starts
def do_exit(self, _):
"""Leave the game."""
print("press 'l' to leave game, press 's' to start a new one")
comand = input()
if comand == 'l':
return True
elif comand == 's':
self.do_start(self)
else:
print("unknow syntax")
self.do_exit(self)
def do_EOF(self, arg):
"""Leave the game."""
return self.do_exit(arg)
When I type "start" this function is called byt the cmd and executed. In general does the "do_" part infront of the function name has to do something with the command line itself? Why do we need this second argument (the underscore dash)? And what is it for?
(I have tried to remove it but then i typed in "-help", couldn't see the command.) I have checked other's classmates codes and they have used other words instead of underscore dash.
If you provide me with some sources/explanations so I can get a better grasp of this concept, it would be really appreciated.
Thanks in advance for the replies. Cheers!
Related
I was trying to make a "game" in Python where the user inputs a command. However, I do not know whether you can take that input to be a function name. This is my current effort:
def move():
print("Test.")
if __name__ == "__main__":
input("Press enter to begin.")
currentEnvironment = getNewEnvironment(environments)
currentTimeOfDay = getTime(timeTicks, timeOfDay)
print("You are standing in the {0}. It is {1}.".format(currentEnvironment, currentTimeOfDay))
command = input("> ")
command()
Here, the input was move, as I wanted to try and call that function (as a potential end user might). However, I get the following error:
Traceback (most recent call last):
File "D:\Text Adventure.py", line 64, in <module>
command()
TypeError: 'str' object is not callable
I was wondering if there was any way that I could allow a user to 'move' in the game, which the program achieves by calling the "move" function.
It looks like you're using python3.x where input returns a string. To recover the python2.x behavior, you need eval(input()). However, you shouldn't do this. It's likely to lead to a bad day.
A better idea is to put the functions into a dictionary --
def move():
#...
def jump():
#...
function_dict = {'move':move, 'jump':jump }
and then:
func = input('>') #raw_input on python2.x
function_dict[func]()
The following code works for me on python3.2.
def move():
print("Test.")
func_dict = {'move':move}
if __name__ == "__main__":
input("Press enter to begin.")
currentEnvironment = "room" #getNewEnvironment(environments)
currentTimeOfDay = "1 A.M." #getTime(timeTicks, timeOfDay)
print("You are standing in the {0}. It is {1}.".format(currentEnvironment, currentTimeOfDay))
command = input("> ")
func_dict[command]()
You can access functions by name using:
function = globals()[function_name]
if the function is in the current module, or
function = getattr(other_module, function_name)
You should also take measures to disallow calling arbitrary functions, for example, prefixing:
def cmd_move() # ok to call this
def cmd_jump() # ok to call this
def internal_func....
cmd = raw_input('>') # e.g. "move"
fun = globals()['cmd_' + cmd]
fun()
Have a look at the cmd module. See this.
It is normally used for shell style comman dlanguages, but it can also be used to create simple text style adventure games.
You can create commands by creating a new method on the Cmd subclass.
E.g.
def do_move(self, args):
if self.next_room.has_snake():
print "The next room contains a poisonous snake. It bites you and you die."
else:
print "The room is empty"
It's usually better to re-use code as Hans suggests, but if you wanted to input commands and run them manually, it would be MUCH safer to have a dictionary of valid commands than to directly execute user-provided input.
cmd = { 'move': move, 'jump': jump, 'look': look }
I am working on a text adventure in Python. I'm very new to the concept of object oriented programming (and programming in general), so I'm not entirely sure what went wrong. Well, what I've done so far is made two methods; one that handles what the user types in, and one that dictates what will happen in the game, using other methods defining rooms that I will create in the future. One problem I have, though, is that I can't test the program by running it! When I run it, there is no prompt for user input - the program just ends without returning any error. There's not much code so maybe you could help me in determining the problem! I'm sure there's something really obvious in there I've forgotten...
class Main(object):
def handle_events(self, userinput, cmd):
self.userinput = userinput
self.cmd = cmd
userinput = raw_input('> ')
cmd = {'use' : use,'quit' : quit, 'help' : help}
if userinput[0] not in cmd:
print "Invalid command. Check [help] for assistance."
def main(self, handle_events):
print '''You are in a dark room filled with
strange and ominous objects.
After some feeling around, you think
you can make out a light switch.'''
self.userinput
if userinput[1] == 'switch':
print "You flicked the switch!"
Main().main
You are not calling your method. Main().main is just a reference to the method. To call it you need another set of parentheses: Main().main().
So, this may, or may not get complicated. Hopefully not.
In any case, I've been writing an fairly ambitious Python text game in my spare time, just to see if I can get it done. I realize there's tons of interactive fiction engines, parsers, etc out there, but Im doing it from scratch. Its the way I learn - the hard way, I guess.
So here's how it breaks down:
You have the main() function in the engine.py module which pretty much does grabs a room-object & displays the room description. Then it waits for user input and sends it to the parser.
The parser in the parser.py module runs through the user input and constructs a Sentence object (made up of a verb & the object - which can be a noun or direction). The Sentence object also has a output function which calls the xxxCommand class from inside the command.py module.
Example: You input "go north" & the parser accepts it as a suitable sentence. So the parser output function will search for the GoCommand class.
Now here's where I'm having trouble. Before I continue and for the sake of clarity, I'll paste my Engine & Sentence class:
class Engine(object):
def __init__(self, start_room):
self.L = lexi.Lexicon() #Imported earlier
self.P = parser.Parser() #Imported earlier
self.room = start_room
def main(self):
while True:
self.room.describe() #Describes the current room.
# Ask for user prompt
input = raw_input(self.room.prompt)
cmd = input.lower()
# Scans user input & creates tokens from Lexicon table
# For example: [('verb', 'go'), ('direction', 'north')]
tokenizer = self.L.scan(cmd)
# Runs through tokens and creates a sentence object
# With noun & object attributes
parsed_sentence = self.P.parse_sentence(tokenizer)
# See below
parsed_sentence.output(self.room)
class Sentence(object):
def __init__(self, verb, noun):
self.verb = verb[1]
self.obj = noun[1]
# There's a command module with different xxxCommand classes with an execute method
# as seen on last line. So pretend the command module has already been imported.
def output(self, current_room):
verb = self.verb.capitalize()
obj = self.obj
command = getattr(commands, '%sCommand' % verb)(obj)
command.execute(current_room)
Ok, after that long winded setup, I have my GoCommand class like so:
# Subclassed from Command parent class. Has one method called execute. Does nothing else.
class GoCommand(Command):
def __init__(self, direction):
self.direction = direction
def execute(self, current_room):
# 'E' is the instantiation of the Engine class, declared within engine.py
from engine import E
# self.direction is 'north' in our example
# current_room.map is a dict within any Room class named 'map'.
# For example: Intro.map = {'north': char_intro }
if self.direction in current_room.map:
print "You head %s\n" % self.direction # Pretty explanatory
# HERE'S WHERE I HAVE TROUBLE
E.room = current_room.map[self.direction]
else:
print "You can't go that way."
So what I was hoping to achieve was that when the loop finishes, E.room would equal a room class called char_intro and as the loop runs through again, it displays char_intro's description, essentially starting over again.
This is not whats happening. It just stays in the first room. Although the GoCommand.execute() is being run, E.room doesnt change. Anyone know why?
Oh dear god, I realize this is long but I hope someone knows what im talking about & can help me out. How should I fix this so that when the user says go north & there is a path set for North, it changes the room class??
To answer my own question, the loop in Engine.main() works just as its supposed to, but I took self.room.describe() out of the while statement:
def main(self):
self.room.describe() #Describes the current room.
while True:
# Ask for user prompt
input = raw_input(self.room.prompt)
cmd = input.lower()
...etc, etc, etc...
Then I changed the GoCommand to:
class GoCommand(Command):
def __init__(self, direction):
self.direction = direction
def execute(self):
# 'E' is the instantiation of the Engine class, declared within engine.py
from engine import E
# self.direction is 'north' in our example
if self.direction in E.room.map.keys():
print "You head %s\n" % self.direction # Pretty explanatory
E.room = E.room.map[self.direction]
print E.room.describe()
else:
print "You can't go that way."
And everything works as its supposed to.
So it looks like the game state (current room, inventory) is being kept on the Engine. Just for comparison's sake, look over the text adventure game I wrote as a command-parsing exercise at http://www.ptmcg.com/geo/python/confs/adventureEngine.py.txt. In that code, I kept the current room and inventory on a Player instance. The main game loop then looks like:
parser = Parser()
p = Player("Joe")
p.moveTo( startRoom )
while not p.gameOver:
cmdstr = raw_input(">> ")
cmd = parser.parseCmd(cmdstr)
if cmd is not None:
cmd.command( p )
parseCmd parses the input string, and if valid, returns a Command object that implements command(p) where p is the Player. Through the player, the command has access to the current room, current inventory, and any special player state or skills (for instance, a player with heightened vision might have better odds at detecting a trap when entering a particular room). This also makes it easier to test your commands with a mock Player, instead of having to mock any globals or attributes in the Engine itself.
I was trying to make a "game" in Python where the user inputs a command. However, I do not know whether you can take that input to be a function name. This is my current effort:
def move():
print("Test.")
if __name__ == "__main__":
input("Press enter to begin.")
currentEnvironment = getNewEnvironment(environments)
currentTimeOfDay = getTime(timeTicks, timeOfDay)
print("You are standing in the {0}. It is {1}.".format(currentEnvironment, currentTimeOfDay))
command = input("> ")
command()
Here, the input was move, as I wanted to try and call that function (as a potential end user might). However, I get the following error:
Traceback (most recent call last):
File "D:\Text Adventure.py", line 64, in <module>
command()
TypeError: 'str' object is not callable
I was wondering if there was any way that I could allow a user to 'move' in the game, which the program achieves by calling the "move" function.
It looks like you're using python3.x where input returns a string. To recover the python2.x behavior, you need eval(input()). However, you shouldn't do this. It's likely to lead to a bad day.
A better idea is to put the functions into a dictionary --
def move():
#...
def jump():
#...
function_dict = {'move':move, 'jump':jump }
and then:
func = input('>') #raw_input on python2.x
function_dict[func]()
The following code works for me on python3.2.
def move():
print("Test.")
func_dict = {'move':move}
if __name__ == "__main__":
input("Press enter to begin.")
currentEnvironment = "room" #getNewEnvironment(environments)
currentTimeOfDay = "1 A.M." #getTime(timeTicks, timeOfDay)
print("You are standing in the {0}. It is {1}.".format(currentEnvironment, currentTimeOfDay))
command = input("> ")
func_dict[command]()
You can access functions by name using:
function = globals()[function_name]
if the function is in the current module, or
function = getattr(other_module, function_name)
You should also take measures to disallow calling arbitrary functions, for example, prefixing:
def cmd_move() # ok to call this
def cmd_jump() # ok to call this
def internal_func....
cmd = raw_input('>') # e.g. "move"
fun = globals()['cmd_' + cmd]
fun()
Have a look at the cmd module. See this.
It is normally used for shell style comman dlanguages, but it can also be used to create simple text style adventure games.
You can create commands by creating a new method on the Cmd subclass.
E.g.
def do_move(self, args):
if self.next_room.has_snake():
print "The next room contains a poisonous snake. It bites you and you die."
else:
print "The room is empty"
It's usually better to re-use code as Hans suggests, but if you wanted to input commands and run them manually, it would be MUCH safer to have a dictionary of valid commands than to directly execute user-provided input.
cmd = { 'move': move, 'jump': jump, 'look': look }
I'm making a text-based game and need an application-wide command line that's able to be pulled up at any text entry. My plan was to have the commands contained within a module, and execute command(), which would then allow the user to enter a command (or get a list) that is then run (contained within the same module). This didn't work because I need a way to return to where the user was. Is there anyway to return to where the user was before they entered command mode, or is there a better way to do this?
This was my idea:
import commands
def something():
print "a question"
action = raw_input("> ")
if action == "command":
commands.commands()
elif "something else" in action:
do something
else:
error.error(1)
something()
Which then went to commands.py:
def commands():
print "Enter a command, help for a list, blah blah blah."
command = raw_input("$ ")
if command == "bag":
bag()
elif command == "other":
other()
def bag():
print "Action listing things in bag"
The problem is returning where the user left off.
What you need is a main game loop:
while game.is_running:
command = get_user_input()
user_do(command)
update_world()
This will repeat the three lines of code inside the while loop for as long as game.is_running is True. First, you get the user input. Next, you act on it. Finally, you perform any other updates your game needs, like moving or spawning monsters. At this point, it loops back and asks the user for another command.
Update: here is a working example:
# In commands.py:
def bag():
print 'bag'
def other():
print 'other'
def unrecognized():
print 'unknown command'
# In main.py:
import commands
def user_input():
print 'a question'
return raw_input('>')
def user_do(command):
# get the matching command out of commands, or pass back
# the unrecognized function if it's not found
action = getattr(commands, command, commands.unrecognized)
action()
is_running = True
while is_running:
command = user_input()
if command == 'quit':
is_running = False
else:
user_do(command)
In this example, I've cheated and am relying on the user input commands being identical to the name of the functions to be called. In user_do, the getattr call compares the string the user has input with the contents of the command module, returning the function of the same name if one exists, or the fallback function unrecognized if it doesn't. action will now either hold the command function or unrecognized.
If you don't want to have your user commands so tightly bound to the actual functions themselves, you can use a dict as a branching construct (or dispatch) instead of having a lot of if / elif / else statements:
# Modified main.py
import commands
COMMAND_DISPATCH = {
'bag': commands.bag,
'sack': commands.bag,
'other': commands.other,
# ...
}
# ...
def user_do(command):
action = COMMAND_DISPATCH.get(command, commands.unrecognized)
action()
In this example, rather than look up the functions in the commands module, we look them up in COMMAND_DISPATCH instead.
One more bit of advice: pretty soon you'll want to look at parsing the user input into more than just a single command. For this example, let's assume you want to be able to accept input of the form "command ... ". You can extend the user_input function to take care of this:
def user_input():
print 'a question'
user_input = raw_input('>').split(' ')
command = user_input[0]
arguments = user_input[1:]
return command, arguments
So if you enter 'foo bar baz', this would return the tuple ('foo', ['bar', 'baz']). Next we update the main loop to deal with the arguments.
while is_running:
# use tuple unpacking to split into command, argument pairs
command, arguments = user_input()
if command == 'quit':
is_running = False
else:
user_do(command, arguments)
Then make sure we pass them to the command:
def user_do(command, arguments):
action = COMMAND_DISPATCH.get(command, commands.unrecognized)
action(arguments)
And finally, we modify the commands to accept and handle the arguments:
def bag(arguments):
for argument in arguments:
print 'bagged ', argument
For a text adventure, you'll want a more substantial parser, something that deals with command object, command object preposition subject, and possibly even command adjective object ....
You should research "python finite state machine". It is pretty much exactly what you want.
Charming Python: Using state machines
Finite-state machine
wiki.python.org/moin/FiniteStateMachine
What is a state machine?
An overly accurate description of a state machine is that it is a
directed graph, consisting of a set of nodes and a corresponding set
of transition functions. The machine "runs" by responding to a series
of events. Each event is in the domain of the transition function
belonging to the "current" node, where the function's range is a
subset of the nodes. The function returns the "next" (perhaps the
same) node. At least one of these nodes must be an end-state. When an
end-state is reached, the machine stops.
When to use a state machine ...
Start in an initial state.
Read a line of input.
Depending on the input and the current state, either transition to a new state or process the line as appropriate for the current state.
Similar to what #MatthewTrevor suggested in his answer, you would have a main loop, and pass a "state" context to the first entry point call (start or intro or whatever). That call can change the state context to point at something else. When control reaches back to the main loop again and it checks the state, it will run the new state.