TOWARDS AN ADVENTURE SYSTEM IN PYTHON, PART I

I've been tinkering with interactive fiction (read: text adventures) lately. There's a plethora of toolkits and even languages out there to write these games. A toolkit written in Python seems to be missing, though.

Would such a kit be useful? Obviously, if the player (and to a lesser extent, the developer) are required to download Python plus the toolkit, they are in for a hefty download. This is not the case when they need, for example, an AGT generated executable, or a Z-machine. Therefore, to make it worthwile for someone to use our kit, it should provide substantial benefits over (most) existing systems.

I will think about, and deal with, such benefits later. Right now I just want to focus on the (rapid) implementation of an, initially rough and unpolished, adventure system.

The system itself should be as small as possible, for the sake of genericity. (I'm thinking about using this same system for e.g. MUDs and maybe other ideas, like RPG/adventure crossovers.) It should have a core that can be extended for adventures, MUDs etc. Maybe there will be a related library with some typical adventure classes. While I might (gradually) build a library with "default" code for basic adventure routines (for example, getting and dropping items, inventory, container, etc) and likewise for MUDs, such code should not be part of the standard package. The library (should) exist so the adventure writer shouldn't have to start entirely from scratch; however, s/he can, if necessary, ignore this library entirely.

...

The parsing and executing of commands is based on two principles:
  1. command templates
  2. command methods
:: Command templates ::

I envision these as strings laying out a "template" for the command. For example:

"get Object"
"talk to NPC"
"ask NPC about Entity"
"score"
Every word starting with uppercase can be the name of an existing Python class. By default, the system comes with a few base classes: Object for objects, NPC for non-player characters and creatures, etc. Entity is the superclass for all these. Normally your classes will derive from Object or NPC, although you can also introduce a whole new class of "things" and derive from Entity. Anything that does not derive from Entity is not taken into account by the template parser.

So, "take Object" tells the parser, that there is a command that starts with "take", followed by an object (instance of Object). The parser can find these instances in certain places, e.g. the player's inventory, or the current room. It will not scan all objects it knows, only those that are visible.

In a similar way, "talk to NPC" will be recognized as a string containing the word "talk", then "to", then an NPC. "score" on the other hand is just that-- one word, with no "variables".

Questions to ponder:
  • How to deal with synonyms? E.g. "take" is a synonym of "get"; do we need to define templates for all synonyms? Or will there be a better, easier way?
  • How to deal with commands that start with the same words? E.g. "look", "look at Entity", "look in Object", "look under Object", etc.
:: Command methods ::

Basically you write your adventure by creating classes for every object, creature, location, etc. in the game. For example, a small key may be coded like this:
    class SmallKey(Object):
        id = "small_key"
        name = "small key"  # yet to be defined...
        # etc...
Now, if we have a command "get Object", we can define its effects on this small key, simply by adding a method for it. For now, we'll say that such methods start with a prefix cmd_, followed by the name of the command. In this case, it will be cmd_get:
    class SmallKey(Object):
        ...
        def cmd_get(self):
            print "You take", self.the()
            engine.player.add_to_inventory(self)
When cmd_get() is called, a message is printed, stating that you take the small key. (The the() method obviously returns something like "the small key", or in general, the name of the object with "the" in front of it. In the final version, this may or may not be a separate method.) The object is then added to the player's inventory.

(Note that all code so far is indicative; not a single byte of code has been written so far, so things may, and most likely will, be different in the real version. These are just examples to indicate what the code can do.)

Questions to ponder:
  • Will the main adventure engine (which includes, or at least handles, the parser, etc) be passed to every object, or will it be a global variable (boo! hiss!)?
  • What is a good way to handle default answers/reactions? E.g. for objects that cannot be taken, it would be tiresome to add a cmd_get method that tells you so to every object. Rather, there should be a way to call a method elsewhere. Hmm...
  • When adding the object to the inventory, should we use the instance, or its id? (This is a question that pertains to something deeper, and it's related to being able to store instances to file and restore them... in other words, persistence. The relation between the two may not be obvious now, but will when I deal with it in a separate article.)
  • What to do with commands that take multiple "variables"? E.g. "ask NPC about Entity". Should cmd_ask() be a method of NPCs, or of every subclass of Entity? Maybe both are possible? If so, which has preference? (In this particular case, my hunch is that cmd_ask() should be added to NPCs, since they are the ones we ask a question to. It should not be added to the person, or possibly /thing/, we want to know something about. This may vary per situation though, and the system should be flexible enough to deal with any situation gracefully.)
    Also, in order for the code to know about the other "parameters", all these should be passed in to the method. For example, a NPC's cmd_ask method (or should it be ask_about?) should have two parameters: self, and the object we want to know something about.
  • What happens if a cmd_ method is absent? Will we resort to a "default reaction" somehow? Where in the code can this best be done?
  • In the case of multiple commands starting with the same words (e.g. "look", "look at Object", "look into Object", etc), what will the method names be like? Will they be, e.g. cmd_look_at, cmd_look_into, etc?
:: Lofty goals ::

Ideally, and eventually, I want this system:
  • to be language-independent; in other words, it should not matter whether I want to write an adventure in English, Dutch, or Esperanto;
  • to be integrated with Twisted Python, so adventures (and MUDs of course) can easily be played over a network;
  • to have acceptable GUI front-ends (clients) for wxPython, Tkinter and maybe other GUIs;
  • to have an acceptable parser;
  • to be elegant and the system of choice for Pythonic IF writers;
  • to have additional classes that enhance the adventure/MUD, e.g. a dialog system, etc;
  • to have a testing "framework", or at least the capability to run adventures "automatically" based on a list of commands, and to generate such a list from playing;
  • and lots of other things I can't think of right now. :-)
More on this later... This was quite enough to give me a sleepless night... ;-)

2002.06.22
0.01