Python Forum
Text Adventure Tutorial - if structure to dictionary
Thread Rating:
  • 2 Vote(s) - 4.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Text Adventure Tutorial - if structure to dictionary
#1
So, you're new to Python, and you want to make a game. You figure you'll make a text adventure game, that won't be too hard. To get started, you just program four rooms in a two by two grid to move around in.

"""
if_adventure.py

A text adventure using if/elif statements.
"""

# intro/setup
print("Welcome to Ichabod's Text Adventure")
current_room = 'empty'

# game loop
while True:
	# display room contents
	print()
	if current_room == 'empty':
		print('You are in an empty room.')
		print('The stone floors and walls and cold and damp.')
	elif current_room == 'temple':
		print('You are in a small temple.')
		print('There are three rows of benches facing a small statue.')
	elif current_room == 'torture':
		print('You are in a torture chamber.')
		print('There is a rack and an iron maiden against the wall')
		print('and some chains and thumbscrews on the floor.')
	elif current_room == 'bedroom':
		print('You are in a bedroom.')
		print('There is a large bed with black, silk sheets on it.')
	# get user input
	command = input('\nWhat do you do? ').strip()
	# movement
	if command.lower() in ('n', 'north'):
		if current_room == 'empty':
			current_room = 'temple'
		elif current_room == 'bedroom':
			current_room = 'torture'
		else:
			print("You can't go that way.")
	elif command.lower() in ('s', 'south'):
		if current_room == 'temple':
			current_room = 'empty'
		elif current_room == 'torture':
			current_room = 'bedroom'
		else:
			print("You can't go that way.")
	elif command.lower() in ('e', 'east'):
		if current_room == 'empty':
			current_room = 'bedroom'
		elif current_room == 'temple':
			current_room = 'torture'
		else:
			print("You can't go that way.")
	elif command.lower() in ('w', 'west'):
		if current_room == 'bedroom':
			current_room = 'empty'
		elif current_room == 'torture':
			current_room = 'temple'
		else:
			print("You can't go that way.")
	# quit game
	elif command.lower() in ('q', 'quit'):
		break
	# bad command
	else:
		print("I don't understand that command.")
It works, but it's not good. Why not? The main thing is that it's repetitive. You're doing the same programming over and over again. That's a bad idea in programming. The more you do the same thing over and over, the more you likely you are to create errors. It's also much harder to maintain. If you want to update the way it works, you have to update every repetition.

Think about it this way: the first version of Colossal Cave Adventure (the first text adventure game) had 66 rooms. Imagine what this code would look like with 66 rooms. It would be a mess.

Computers are much better at repeating things than humans are. When you find yourself programming the same thing repeatedly, you first thought should be "How can I program this once?" Functions are a good way to do this. Write the code once in a function, and then call the function repeatedly. certainly any sizable text adventure should use functions. But there's another technique that's useful here:

"""
dict_adventure.py

A text adventure using a dictionary.
"""

# data setup
rooms = {'empty': {'name': 'an empty room', 'east': 'bedroom', 'north': 'temple',
		'text': 'The stone floors and walls and cold and damp.'},
	'temple': {'name': 'a small temple', 'east': 'torture', 'south': 'empty',
		'text': 'There are three rows of benches facing a small statue.'},
	'torture': {'name': 'a torture chamber', 'west': 'temple', 'south': 'bedroom',
		'text': 'There is a rack and an iron maiden against the wall\nand some chains and thumbscrews on the floor.'},
	'bedroom': {'name': 'a bedroom', 'north': 'torture', 'west': 'empty',
		'text': 'There is a large bed with black, silk sheets on it.'}}
directions = ['north', 'south', 'east', 'west']
current_room = rooms['empty']

# game loop
while True:
	# display current location
	print()
	print('You are in {}.'.format(current_room['name']))
	print(current_room['text'])
	# get user input
	command = input('\nWhat do you do? ').strip()
	# movement
	if command in directions:
		if command in current_room:
			current_room = rooms[current_room[command]]
		else:
			# bad movement
			print("You can't go that way.")
	# quit game
	elif command.lower() in ('q', 'quit'):
		break
	# bad command
	else:
		print("I don't understand that command.")
Much more succinct. You have one set of code to show what room you're in, not one set for each room. You have one set of code for moving from room to room, not one set for each direction. That means it's a lot less code. The dictionary version has 39 lines of code, the if/elif version has 64. You can add a room to the dictionary version with two or three lines of code. Adding a room to the if/elif version takes at least eight lines of code. Back to Colossal Cave Adventure, that would be at least 592 lines of code for 66 rooms, 724 if they had an average of 3 exits each. Colossal Cave Adventure only had about 700 lines of code, and included things you could pick up, creatures you could fight, and points you could score.

How did I do this? I took the conditions and actions of the if/elif code and put them into a dictionary. In more general terms, I took the structure out of the code, and put it into the data. I looked at what the rooms had in common, which was what was repeated in the code. That became the structure (keys) of my dictionary object. Then the differences in the room became the values in the dictionary object. Then my code just needs to use the structure in the dictionary object to access the values, without either the structure or the values having to be part of the code.

You don't have to use a dictionary for this sort of thing, but dictionaries are vary handy for it. Even if you use a dictionary in your program, you don't have to hard code the dictionary in the program. You could store the values for the dictionary in a tab-delimited text file and load it into the dictionary at the start of the game. Once you get more experienced at programming and learn object-oriented programming, you'll see how well classes can be used for this sort of thing.

Note that I've mentioned several objects in the descriptions of the rooms. In a typical text-adventure game, you can move things around. Let's implement that with our dictionary version:

"""
object_adventure.py

A text adventure with objects you can pick up and put down.
"""

# data setup
rooms = {'empty': {'name': 'an empty room', 'east': 'bedroom', 'north': 'temple', 'contents': [],
		'text': 'The stone floors and walls are cold and damp.'},
	'temple': {'name': 'a small temple', 'east': 'torture', 'south': 'empty', 
		'text': 'This seems to be a place of worship and deep contemplation.', 
		'contents': ['bench', 'bench', 'bench', 'statue']},
	'torture': {'name': 'a torture chamber', 'west': 'temple', 'south': 'bedroom', 'contents': ['chains', 'thumbscrews'],
		'text': 'There is a rack and an iron maiden against the wall\naand some dark stains on the floor.'},
	'bedroom': {'name': 'a bedroom', 'north': 'torture', 'west': 'empty', 'contents': ['sheets', 'bed'],
		'text': 'This is clearly a bedroom, but no one has slept\nhere in a long time.'}}
directions = ['north', 'south', 'east', 'west']
current_room = rooms['empty']
carrying = []

# game loop
while True:
	# display current location
	print()
	print('You are in {}.'.format(current_room['name']))
	print(current_room['text'])
	# display movable objects
	if current_room['contents']:
		print('In the room are: {}'.format(', '.join(current_room['contents'])))
	# get user input
	command = input('\nWhat do you do? ').strip()
	# movement
	if command in directions:
		if command in current_room:
			current_room = rooms[current_room[command]]
		else:
			# bad movement
			print("You can't go that way.")
	# quit game
	elif command.lower() in ('q', 'quit'):
		break
	# gather objects
	elif command.lower().split()[0] == 'get':
		item = command.lower().split()[1]
		if item in current_room['contents']:
			current_room['contents'].remove(item)
			carrying.append(item)
		else:
			print("I don't see that here.")
	# get rid of objects
	elif command.lower().split()[0] == 'drop':
		item = command.lower().split()[1]
		if item in carrying:
			current_room['contents'].append(item)
			carrying.remove(item)
		else:
			print("You aren't carrying that.")
	# bad command
	else:
		print("I don't understand that command.")
With 21 lines of code, we've added contents for the rooms, a way to display the contents of the rooms, and get and drop commands for moving things around. This code is still shorter and simpler than the if/else version, and it does more.

Note that if you tried to add these features to the if/else version, you would have to add data on the rooms anyway. Otherwise you couldn't keep track of what objects where in which room.

Now you just need creatures to encounter and points to score, and you have a complete text adventure game. If you came up with a way to store the data in a text file, you could probably do it in less than 100 lines of code.
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
#2
-
Recommended Tutorials:
#3
+
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
#4
**
Recommended Tutorials:
#5
this kind of game, in an odd way led me to python.

i played an LPMUD based game and made it to wizard level which open up letting me create stuff via LPC coding.  i was also helping a good friend who was doing the same thing.  at this point i had lots of C coding experience, and loved LPC, which was a copy of Pike, which i picked up on next.  Pike and LPC were nearly identical and had a syntax very much like C.  when i was exposed to Python (via some networking tools i found), the sensed the environment of Pike and Python to be very much alike.  the syntax was different and i tried Python with only the need to learn a new syntax which i soon fell in love with, despite first thinking it was ugly.  i still do some stuff in Pike and/or C where performance is needed (Pike arithmetic is much faster than Python, at least for big ints). see the bigsqrt program i am about to post over in Scripts & Snippets
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
#6
I think this point can be simplified by using a calculator similar to this thread.
Recommended Tutorials:
#7
I would suggest to modify the dictionary whitespace to make it better to read and understand what is going on there based on this thread.
rooms = {
    'empty': {
        'name': 'an empty room', 
        'east': 'bedroom', 
        'north': 'temple',
        'text': 'The stone floors and walls and cold and damp.'},
    'temple': {
        'name': 'a small temple', 
        'east': 'torture', 
        'south': 'empty',
        'text': 'There are three rows of benches facing a small statue.'},
    'torture': {
        'name': 'a torture chamber', 
        'west': 'temple', 
        'south': 'bedroom',
        'text': 'There is a rack and an iron maiden against the wall\nand some chains and thumbscrews on the floor.'},
    'bedroom': {
        'name': 'a bedroom', 
        'north': 'torture', 
        'west': 'empty',
        'text': 'There is a large bed with black, silk sheets on it.'}
    }
Recommended Tutorials:


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020