Starting on a Text Adventure

For the past few months, I’ve been learning to program in Python. It’s a pretty cool language that makes learning to code pretty easy—at least from where I stand—and within a really short amount of time, with the help of my dad, I’ve been able to get into this programming thing, and I’m beginning to really dig it.

As it turns out, programming is not so much about the language that you use, but much more about the logical breakdown of processes into smaller parts that are then solved one at a time. The smaller the part, the easier it is usually to implement—or so legend has it.

After doing a good bunch of small coding exercises that ranged from calculating square roots, Fibonacci sequences, manipulating strings, lists and other kinds of data, I’ve decided to sink my teeth into a larger project.

My dad recently introduced me to a Text Adventure, an old-school kind of computer game that is based solely on text output and does not require fancy graphics. I think I got interested in it when I played infocom’s The Hitchhiker’s Guide to the Galaxy, and the bulldozer came right through my bedroom wall in the game. The moment was so cool that something clicked inside me and I decided right then and there that I want to do something like it.

So, here we are. I want to use this blog as a kind of journal to document the process of the creation of my own text adventure game and my own growth as a new programmer. I know that I understand the basic concepts of programming at this point and I am familiar enough with Python to solve these problems but to put all of it together in a larger project that requires many components to work together… I’m a little intimidated but here goes nothing!

The first thing I need for a text adventure game is a Parser—a component that takes the player’s text input and parses it into pieces of information that the game can use to analyze and respond to.

At the heart of the parser is the detection of words, and then giving them a grammatical context. It means, determining if the word is a verb or a noun, and giving it a meaning so that the program can later determine what the user actually tried to achieve when entering his command.

I decided to use a dictionary to look up the vocabulary, with a secondary dictionary serving as the value to hold the word type and meaning.

This is what it looks like.

Vocab = {

	"look" : { "type" : WordType.Verb, "meaning" : Tokens.Look },
	"l" : { "type" : WordType.Verb, "meaning" : Tokens.Look },
	"examine" : { "type" : WordType.Verb, "meaning" : Tokens.Look },
	"inspect": {"type": WordType.Verb, "meaning": Tokens.Look},


	"exit" : { "type" : WordType.Verb, "meaning" : Tokens.Exit },
	"x" : { "type" : WordType.Verb, "meaning" : Tokens.Exit },
	"quit" : { "type" : WordType.Verb, "meaning" : Tokens.Exit },
	"leave": {"type": WordType.Preposition, "meaning": Tokens.Exit },
	}

As you can see, this allows me to have different words that nonetheless have the same meaning, and using a dictionary makes the look-up a breeze. Since a text adventure centers around the vocabulary, I think this is a critical step.

But before we get to that, I need to capture what the player is typing.

class Parser ( object ):
	def Prompt ( self ):
		""" Let the user enter a command """

		_rawInput = input ( "> " )
		_inputWords = _rawInput.lower ().split ( " " )
		return self.Parse ( _inputWords )

Here I’m grabbing the input from the player and I split it into separate words. The result is a list that contains each word the player entered—I’ve also made sure each is in lower case—so I can directly use each word to see if it is in my vocabulary. Nice and simple.

The next part of the program does just that, tokenizing each word, which is a fancy way of saying that, instead of dealing with these text strings in the future, from now on I can work with specific word and grammar tokens. It also makes sure that similar words in the vocabulary have the same meaning like I defined in the Vocab data above.

As you may have noticed in the vocabulary code snippets above, I implemented the word types and the actual tokens that define the meaning in Enum classes. Enum is part of the Python language and creates a class that can only be read and not written to. It is a way to get around the fact that Python does not have true constants and this seemed to be the next best thing to it that I could find.

class WordType ( Enum ):
    Verb = 0
    Noun = 1

class Tokens ( Enum ):
    Look = 0
    Exit = 1

As a really nice side effect, now, whenever I print the variables where I store my results in to check their values, Python is actually printing Token.Look instead of the number 0, which is totally cool and something I had not expected

So, let’s be off to see the wizard and tokenize the user input…

import string

def Parse ( self, _inputWords ):
	""" Compare input words against dictionary and create respective tokens """

	theVerb = None
	theNoun = None

	for _word in _inputWords:
		_cleanWord = _word.rstrip ( string.punctuation )
		if _cleanWord in Vocab:

			if Vocab [ _cleanWord ] [ "type" ] == WordType.Verb:
				theVerb = Vocab [ _cleanWord ] [ "meaning" ]
				

			elif Vocab [ _cleanWord ] [ "type" ] == WordType.Noun:
				if None == theNoun:
					theNoun = Vocab [ _cleanWord ] [ "meaning" ]

		else:
			print ( " I'm sorry, what do you mean by '" + _cleanWord + "'?" )
			return False

	return True

What we have here is a loop that goes through each word in the player’s input, looks it up in the vocabulary and then assigns the meaning token to either theNoun or theVerb, two variables I will be using throughout the game to check for user commands.

The way this loop is set up is that whenever the parser finds a word that is not part of the vocabulary, it will terminate the entire loop and spit out an error. No use trying to work out the entire sentence when you don’t even understand half of what the player entered, right?

To see if it all works, I put it in a loop in which I repeat getting a user input, tokenize it and then print the result so I can check if it works correctly. It’s essentially what will serve me as the main game loop for my text adventure later on.

while not ( Tokens.Exit == theVerb and None == theNoun ):

	theParser.Prompt ()

	print ( "\n——————————————————————————————————————————\n"
		"Verb: " + str ( theVerb ) +
		"  Noun: " + str (theNoun ) )

Now it’s time to expand the vocabulary and start thinking about the next step… and I will tell you all about it next time.

Leave a Reply

Your email address will not be published. Required fields are marked *