Time to Move

Now that we have different rooms in place, the next logical step is to connect them and allow the player to walk from one to the other. What we need to get started is something to represent an Exit. But what is an exit in game terms?

Since we’ll be making this a class, the first thing we have to ask ourselves, what do all exits have in common? The answer is surprisingly simple.

↳ Direction
↳ Target

For the time being, that is really it. We need to know, where in the room the exit is (Direction) and which room it leads to (Target). It could not be simpler. So let’s implement it.

class Exit ( object ):
	""" Define an exit and where it leads to """
	def __init__ ( self, _direction, _target ):
		self.Direction = _direction
		self.Target = _target

This will allow us to create instances of exits right in the room initialization like I do here.

theRooms = [
    Room ( 
		"This is room 0",
 		[ Exit ( Tokens.South, 1 ) ] ),
    Room ( 
		"This is room 1",
		[ Exit ( Tokens.North, 0 )] )

As you can see, the first room now has an exit leading south into the second room, and the second room has an exit leading north back into the first room. At some point, I may use named indices for the rooms to make it more readable and less error-prone, but for now, using indices like 0 and 1 will do to identify the rooms.

It’s no use having these exits in the room, however, if the player is not aware of them. What we need now is a method for a room object that will list all of its exits.

def ListExits ( self ):
	""" Create and print a list of exits for the room. """
	_counter = 0
	_outString = ""

	for _xit in self.Exits:
		if 0 != _counter:
			_outString += " and "

		_outString += _xit.Describe ()
		_counter += 1

	if 0 == _counter:
		_outString = "Strangely, you can see no exits here."
	elif 1 == _counter:
		_outString = "An exit is leading " + _outString + "."
		_outString = _outString.replace ( " and ", ", ", _counter - 2 )
		_outString = "Exits are leading " + _outString + "."

	print ( _outString )

This may look a little confusing at first, but it’s really quite simple. I am running through all the exits in a room and count them. I am also creating a string variable that contains each exit listed and separated by the word “and.” If there are multiple exits, it would say something like “North and East and South,” for example. Because this sort of listing is ugly, in the end, I then replace all but the last of the ands with a comma. That’s what this line does.

		_outString = _outString.replace ( " and ", ", ", _counter - 2 )

When the loop is done, I am simply checking how many exits there were and create grammatically correct responses that I then print to the screen. The result is then something like

Exits are leading North, East and South.

But where do the words North, East and South actually come from? You will notice that I am calling a Decribe() method on each one of the exits, which I expect to return that word.

However, since the name is not part of the exit itself, where does it come from? I realized I needed a way to look up a token and find its actually command word… a reverse lookup of what the parser does, in essence.

This may not be elegant, I know, but it’s the best I could come up with so far. Perhaps in the future, as I learn more about some of Python’s more obscure language features I’ll be able to find a more efficient implementation. Since it’s in a separate function it’ll be an easy improvement to make.

def TokenLookup ( self, _token ):
	""" Look up the text string of a vocabulary word based on its token """

	for _key, _word in Vocab.items ():
		if (len ( _word ) >= 2):  # Make sure there is a 'meaning' entry
			if _word [ "meaning" ] == _token:
				return _key

	return None

So now, my exit’s Describe() function can simply do this…

def Describe ( self ):
	return theParser.TokenLookup ( self.Direction ).title ()

With exit listings in place, now it is time to turn my attention towards moving between rooms. The first thing that is needed is an expanded vocabulary, so I am adding these word definitions to it.

"north" : { "type" : WordType.Direction, "meaning" : Tokens.North },
"east" : { "type" : WordType.Direction, "meaning" : Tokens.East },
"south" : { "type" : WordType.Direction, "meaning" : Tokens.South },
"west" : { "type" : WordType.Direction, "meaning" : Tokens.West },
"n" : { "type" : WordType.Direction, "meaning" : Tokens.North },
"e" : { "type" : WordType.Direction, "meaning" : Tokens.East },
"s" : { "type" : WordType.Direction, "meaning" : Tokens.South },
"w" : { "type" : WordType.Direction, "meaning" : Tokens.West },

I’ve introduced a new word type also, called WordType.Direction which the parser will feed into a variable called theDirection so that it won’t interfere with any verbs or nouns. After all, I want the player to be able to also say something like Run north or Go East or much later perhaps even Close the door to the North. In order to do that I need to preserve the verbs and nouns and treat directions as their own command entity.

Once that is all done, it is time for the room’s Evaluate() function to respond to my directional commands.

if None != theDirection:
	""" 	Check if there is an exit leading a direction; 
			If there is, change to the new room; 
			Print the new room's description 
	_exit = theRoom.HasExit ( theDirection )

	if _exit:
		theRoom = rooms.theRooms [ _exit.Target ]
		theRoom.Describe ()
		return True

Believe it or now, but that’s it. This will allow the player to simply walk from one room to the next. No special checks, not a lot of code, just a few lines that re-assign a few variables. I am starting to get a sense that this is the true power of object-oriented programming because now that we have “activated” a new object as the current room, all logic will run through that room object. Mighty impressive stuff… especially for so little code.

Leave a Reply

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