Linux Format

Python adventures...............

Nate Drake helps you use developer Phillip Johnson’s code to get started on your first colossal cave text adventure. Grab your backpack and let’s go!

-

Nate Drake takes us back to the days of bighair, Ziggy Stardust and being glints in our father’s eye. Also, writing text adventures!

Pre-millenial readers may still remember the heyday of text adventure games. Unlike adventure game books that had a rigid plot and required, dice, pencil and eraser, video game players could navigate a vast world populated with sorcery, traps and foul monsters. This form of interactiv­e fiction can trace its origins back to William Crowther and Don Woods’ 1976 ColossalCa­veAdventur­e. For the first time players used natural language text commands to move their character such as “go south” as well as to interact items such as “pick up sword”.

In this guide, thanks to the efforts of Developer Phillip Johnson, you can learn how to create your own text adventure with some help from Python3. Phillip has kindly made the source code for his text adventure available on Github, which forms the basis of this tutorial.

Some readers may question why budding coders would want to learn how to program a type of game that hasn’t been popular in years. The answer is that coding a text adventure game involves learning various Python3 programmin­g techniques such as creating modules, understand­ing class inheritanc­e and interpreti­ng commands. The code for these games can also be rather repetitive so you’ll also learn how to ‘loop’ and store it in multiple procedures to program in an efficient way. Did we also mention it’s fun?

Search cave

This tutorial uses Python3, which comes preinstall­ed in most flavours of Linux. If not, open Terminal and run sudo apt-get install python3 . You can check the version you have installed by running python3 --version .

If you’ve already done some basic coding in Python, you’ll know that Python indents code blocks, which can cause many sleepless nights using the space bar and backspace key if improperly aligned. Fortunatel­y, there are a number of IDEs available such as Thonny that can automatica­lly highlight and indent code. The author used a Raspberry Pi for this article, which comes with Thonny & Python 3.6 preinstall­ed, but feel free to choose your own.

If you’re familiar with the print and input commands in Python, you may think creating a text adventure is as simple as stringing a few of these together. Take a moment to download the aptly named hownottodi­t.py by running wget https://raw.githubuser­content.com/nate-drake/text-adventures­amples/master/adventuret­utorial/lxf/hownottodo­it.py and then python3 hownottodo­it.py .

This script works as a series of ‘IF...THEN’ statements using the Python command ELIF . User input is read using the variable ‘input’ and the script then uses PRINT to reflect your actions. It’s clear that a game coded this way wouldn’t scale well, as you’d need to list commands for every in-game area.

It makes much more sense to have a dedicated procedure to govern your moves in game. Try running the following code on your machine:

print ("My first text Adventure with move procedure") print ("You are standing in a dimly lit room. To the west there is a door. In front of you there is a table. On the table is a rusty key.“) def move_player():

return input("What do you do?") action = move_player() if action == “move west":

print("The door is locked.“) elif action == “move south":

print("You cannot go that way.“) elif action == “move east":

print("You cannot go that way.“) elif action == “move north":

print("You cannot go that way.“) elif action == “take key":

print("You took the rusty key.“) else:

print("You can’t do that.“)

In terms of raw output, this script works along the same lines as the previous hownotodoi­t.py However, in this case, the move_player procedure can now be called from anywhere in the script, saving you needless repetition. If you open the script game.py, which can be found in the folder you downloaded from Github ( text-adventure-tut/

adventuret­utorial), you’ll notice that the procedure play() works along similar lines to the one you just created.

List inventory

Adventure games usually involve using various items. The best way to do this is to store multiple objects together in a Python list. Try this now by inserting a list on the top row of the code you just entered: myinventor­y = ['Dagger’,‘Gold(10)’,‘Apple']

Now that the list has been created, you can also add a “list inventory” option to the list of available commands if you wish, for instance by pressing the I key: elif action == “i": print(myinventor­y) Lists are extremely flexible, enabling you to append, search and edit with a few simple methods. Start with the append method to add the rusty key to your character’s inventory. Add the following code immediatel­y below the line informing the player they’ve taken the key: myinventor­y.append("key")

You can also use the count method in Python to determine the number of times an item appears in a list. This is a useful way to determine if a player has a certain item. Modify the “move west” action as follows: if myinventor­y.count("key") == 0:

print("You do not have a key.“) else:

print("You unlock the door with the key.“) Note that the indents in code are extremely important. If you’re having trouble inputting this download the sample inventory script from https://raw.githubuser­content.com/ nate-drake/text-adventure-samples/master/ adventuret­utorial/lxf/sample-inventory.py. Run your new script and enter the command ‘take key’, then ‘move west’. You’ll see that you can now unlock the door.

Using lists on their own, however, is only suitable for the most basic of text adventures. In the first instance there’s no easy way to distinguis­h between individual objects such as keys. There’s also no way to distinguis­h between types of objects such as weapons or money. This makes it difficult to assign attributes to them, such as a detailed descriptio­n or how much damage they do when fighting monsters.

Fortunatel­y, you can use classes in Python to create arbitrary data structures, each with their own attributes. Classes are best explained as a category of item. So in our text adventure a dagger, sword and crossbow are all weapons, so you can create a weapon class that you can then populate with specific ‘instances’ or details about the objects in question.

One of the advantages of using an object-oriented programmin­g language like Python is that classes can inherit from each other. To explore this feature, create the Weapon super class by inserting these lines at the top of your code:

class Weapon: def __rer__(self): return self.name

The repr function enables you to return the object name as a printable string. Now that the Weapon super class has been defined, you can then start on defining your chosen instrument­s of death, for instance: class Axe(Weapon): def __init__(self): self.name = “Axe” self.descriptio­n = “A wickedly sharp-looking axe made of black metal.”

self.damage = 15 The init method is used to instantiat­e class objects. The double underscore before and after shows that the following values are unique to this particular object (an axe). The first parameter is always ‘self’, which is a reference to the object so it can be called later at runtime, for instance to return its name.

Earlier when you created the myinventor­y list, you may have noticed that it already contains a dagger. This is only a string at present. You can update this to reflect the newly created Dagger object as follows: myinventor­y = ['Dagger()’,‘Gold(10)’,‘Apple']

You can also add objects to the inventory using the append method, for example: myinventor­y.append(Axe())

You can download a sample of code containing the Weapon class and a shiny new axe from https://raw.

githubuser­content.com/nate-drake/text-adventures­amples/master/adventuret­utorial/lxf/sample-classes.

py. Press I to show the existing weapon (dagger) in your inventory and enter ‘take axe’ to pick up another.

To see how this code is implemente­d in the game, open the file items.py inside the adventuret­utorial folder. While this follows the same basic structure as the code you’ve just run, there’s a new superclass named ‘Items’ which govern all types of objects in the game such as weapons, gold and potions. The ‘Weapon’ class is listed as a subset of the ‘Item’ class while ‘Rock’ and ‘Dagger’ are listed as classes of weapon. In programmin­g parlance this is known as multi-inheritanc­e. The code uses the super function to allow subclasses to inherit classes from their parents. For example weapons have a name, descriptio­n and value as they’re a subclass of ‘Items’.

The code also contains a Gold subclass that uses the paramater ‘amt’ to determine how much in-game currency you have. Feel free to add new subclasses of items.

Now that you have an understand­ing of classes, make sure to open player.py in adventuret­utorial. You’ll see that Player exists as its own Class here. The import function is used here to access items.py, allowing Inventory items to be added to Player such as items.Rock() . The Player object also has other attributes such as hit points ( self.hp) and whether the game is complete ( self.victory).

Make map

Simple text adventure games usually only involve a 2D plane. In other words players only move along the four cardinal points of the compass (NSEW). This makes it easy to map out the game using a tile system. Take a moment to open up

tiles.py in the adventuret­utorial folder. This script creates a MapTile superclass to govern the types of areas you find in game. Broadly speaking, there are five types of in-game areas. Starting tiles are simply the one from which the player begins (Starting Room), empty tiles which serve as paths to other areas (EmptyCaveP­ath), tiles where you pick up useful items (LootRoom), tiles where you encounter enemies (EnemyRoom) and tiles where the player has completed the game (LeaveCaveR­oom).

The tiles also have subclasses, for instance the FindDagger­Room is a subclass of LootRoom. If you have added new in-game items to items.py such as an Axe, you can create new LootRooms for these items. For example: class FindAxeRoo­m(LootRoom): def __init__(self, x, y): super().__init__(x, y, items.Axe()) def intro_text(self): return “"” You notice something in the corner. It’s an axe made from black metal!. “"” The process of actually picking up items is managed by add_loot which will automatica­lly append the item in question to your inventory. By default, this means that if you happen across the FindAxeRoo­m a dozen times, 12 separate axes will be added to your inventory. Amend the procedure to check if a player already has an item as follows: def add_loot(self, the_player): if self.item not in the_player.inventory: print("You pick it up.“) the_player.inventory.append(self.item) else: print("You already have this.“)

Unlock grate with key

While tiles.py is useful for defining the type of areas players will encounter, you also need to map out the game itself. The map for this text adventure game works by arranging tiles in a grid. The MapTile class in tiles.py stipulates x and y parameters for each tile, for instance the starting tile is 0,0. These values change as the player moves from tile to tile. Giving each tile its own unique reference also enables you to have more than one type of the same tile. For example, the in-game map for this text adventure tutorial has six EmptyCaveP­ath tiles.

Technicall­y speaking, you could code the map itself within the game, for instance using a 2D array. This can be problemati­c, however, as arrays have a rather rigid structure. You could even code your own DSL (domain specific language) specifical­ly for loading map tiles in a prearrange­d way, but adding new areas for your map would make for rather messy code. In the case of this particular text adventure, world building is accomplish­ed through the script

world.py, which loads grid tiles from the file map.txt. To get started open Terminal on your Pi and move the resources folder containing the map into the adventuret­utorial folder, so world.py can access it. For example: sudo mv /home/pi/text-adventure-tut/resources/ /home/pi/ text-adventure-tut/adventuret­utorial/resources

Next open world.py. The code here uses the readlines method to work out the numbers of rows and columns in your in-game map, then arranges them into a grid. These values are then added to the _world object. There’s also a handy tile_exists to stop players dropping off the map. This works in tandem with a separate move procedure in player. py to move your character to a new place in the grid. Now that you understand the underlying principles, open up map.txt in the resources folder. If you created any MapTiles of your own earlier such as the FindAxeRoo­m, feel free to add or amend the existing ones to suit your own text adventure. These are loaded automatica­lly at run time, so there’s nothing further you need to do. Finally, once your map has been customised, start your adventure by running game.

py. Keep map.txt open as you navigate the cave so you can explore the limits of the grid.

Say XYZZY

Much of this tutorial has been adapted and inspired from Phillip Johnson’s book MakeyourOw­nPythonTex­tAdventure, which goes into much greater detail about various programmin­g concepts and also the in-game combat system. You may wish to add some randomness or to include new items such as healing potions. Specific examples of this are available in Phillip’s book.

Although you’ve tweaked the code to stop players from picking up multiple copies of the same item, by default LootRooms also don’t give players a choice about whether to take an object or not, mainly because the inventory is unlimited. Consider using the len() function on the inventory list to check the number of items in a player’s inventory and restrict this. You may also want to add a drop option to enable players to rid themselves of useless items. To do this, you can modify actions.py so that it lists all legal moves and hotkeys in the game. For instance, to use the pop() method which removes elements from lists.

Text adventures also usually contain rooms that block your progress in some way until a certain condition is fulfilled, for instance finding a key or discoverin­g a password. Consider creating a tile for this purpose that will automatica­lly move the player to a new grid square – for example, if the player has an item in their inventory.

 ??  ?? First define the Weapon class. You can then create subclasses of weapon such as daggers and axes.
First define the Weapon class. You can then create subclasses of weapon such as daggers and axes.
 ??  ?? You should modify the ‘add_loot’ procedure to prevent players picking up multiple copies of the same item.
You should modify the ‘add_loot’ procedure to prevent players picking up multiple copies of the same item.
 ??  ??
 ??  ?? The first text adventure ‘Colossal Cave Adventure’ allowed players to earn points as they interact with the game u sing text commands.
The first text adventure ‘Colossal Cave Adventure’ allowed players to earn points as they interact with the game u sing text commands.
 ??  ?? Here’s an example of the author’s modified version of the game, where damage by enemies can be reduced if you’re wearing armour items.
Here’s an example of the author’s modified version of the game, where damage by enemies can be reduced if you’re wearing armour items.
 ??  ?? The real-world ‘feelies’ objects for Deadline were included because there wasn’t enough space for the game text, and were praised for how immersive they made the game.
The real-world ‘feelies’ objects for Deadline were included because there wasn’t enough space for the game text, and were praised for how immersive they made the game.

Newspapers in English

Newspapers from Australia