logo

UTK Notes


Lab 9 - Multi-User Dungeons

Table of contents

Overview

This lab will be a text-based maze-ish game that works by generating “dungeons” based off of a file containing a variable amount of room information. The funky part of this lab is that it’ll most likely be your first time using pointers! So strap in because this stuff is a little weird at first.

RECOMMENDED

I highly recommend you do this lab with a class. It will make it a lot easier than if you’re just trying to do functional programming. I’ll give some vague descriptions about what I think your class would look like, but I think this is a good time to learn how to start structuring them yourselves because you’ll definitely have to later.

General Structure

In general, my code was comprised of these things:

  • Class for the whole dungeon. This would encompass rooms and methods to interact with those rooms, as well as keeping track of the room that the player is currently in.
  • Struct for rooms. This would hold information pertaining to a single room, including all of its exits.
  • Struct for exits. This would simply bundle what comprises an exit (direction e.g. w for west, and room# e.g. 2) for ease of storage/access.
  • Methods that are part of your dungeon class. These should let allow you to execute some kind of command.
  • Loop in main that allows the user to call your class’s methods to play the game.

So to summarize, the Dungeon class holds all the rooms and has methods to interact with them, each room has its own set of exits and a description, and the exits are just a struct that holds the direction and room index.

(structs and classes are basically the same thing in C++, but by convention we use structs to hold data and classes to hold both methods and data)

The hard part about the the rooms and exits in particular is that they will all need to be dynamically allocated.

Tools You Should Use

As part of my recommendation, these are the tools I think you should use:

  • getline() - You can use this in many ways, so get creative. There are overloaded versions of it as well that give you more control over how it behaves (e.g. changing the delimiter). You might also want to pair this with ignore() to get rid of the newline character.
  • stringstream - Makes things a bit easier when parsing exits, in my code at least.
  • string - This might seem obvious, you’ll have to use it in the lab anyway of course. But remember that a string is BASICALLY just a vector but all of its elements are chars. It provides the same methods your familiar with from vector like push_back() and pop_back() - two things I used in my code. (if you’re using a special delimiter in getline(), then the \n character will get read and appended to your string. Hence pop_back())
  • seekg()/tellg() - these two methods belong to fstream. You’ll have to use seekg() at least once (well technically there is a workaround – reset the file handle).
  • Google - Not even joking. If you don’t understand how something works like getline, ignore, clear, Google is your friend. Google until your fingers are bleeding, because I swear it will solve most of your problems. I would bet 99% of the stuff I know in terms of programming is thanks to Google. Don’t know what seekg() does? GOOGLE IT. You’ll see exactly what it does when you do that. Once you’ve learned what it does, you might be a bit confused on how to use it for your particular case. Let’s say you learn that seekg() allows you to move the file pointer for a currently open file to a certain position. (So in english, it lets you move around in your file.) You know that now, but how do I move to the beginning of the file? GOOGLE IT. (unless you learned it already in which case ❤️)

Restrictions

Not to beat a dead horse here, but the restrictions are very important for this lab so consider the horse abused – Any container or object (instantiation of a class/struct) will be dynamically allocated. You’re not allowed to use objects allocated on the stack if they’re associated with the rooms (excluding strings and your greater class assuming you wrote yours the way I wrote mine). You must use pointers in this lab, which will be difficult because you’ve never even touched them before most likely. There’s a lot of rules to pointers, so don’t be afraid to ask questions because we’ll answer all of them!

example

1
2
int arr[20]; // nope
int* arr = new int[20]; // yep

Pointers, new, delete, oh my

Okay now that you’re working with pointers you are a bona fide big boy/girl, so put on your big boy/girl pants because you’re gonna need them. Some of this/all of this I’m sure you’ve already learned, but I’m gonna explain it here in my own words so it’s 1. accessible and 2. potentially more understandable for some. You can never have too many resources.

Pointers

Pointers are really pretty simple at the core. They are pointers (really?) to an address in memory. Every thing you declare in your program has a memory address associated with it, which is where it really lives in your computer. When you do something like int a = 10, your computer goes to what’s called the stack, which is just a large space of memory that’s reserved specifically for your program, and inserts your variable a at an address on the stack. Since it’s an int that address will take up 4 bytes in C++. To create a pointer you use the following syntax:

1
int* b; // you can also do int *b

This creates a pointer, denoted by the *, to an int. So that means that our tag b will point to an address in memory that holds an int. Currently, it points to nothing, so its value is NULL or nullptr in C++. If you wanted to point it to something, you would do something like

1
2
int a = 10;
int* b = a;

This creates a variable on the stack a. It holds the value 10. Then we create a pointer to an int, int* with the name b. b now points to a, which means that the value of b is the address of a. Now we can dereference b, which will give us the value that lies at the address it is pointing to.

1
2
3
int a = 10;
int* b = a;
cout << *b; // prints 10

As you can see, you dereference a pointer by adding an * in front of its name. The effect here is that any change made to the value of a will also be reflected in the dereferenced value of b because they are pointing to the same address in memory. This is a very important concept to understand, so make sure you do.

Seems kinda useless in this context right? Well yeah, but hopefully you get the point here. There’s a lot more depth to it, but this will suffice for now. A more common use-case for pointers you’ll see is in conjunction with the new operator.

new/new[]

If you’re coming from 101, then you kinda now about the new operator. It’s quite a bit different in C++, but they kinda signal the same thing. When you use the new operator, you’re dynamically allocating memory for something. This means that your program did not have the memory for this before, but now you’ve given it the memory to do so. It’s used like so:

1
2
3
int* a = new int; // creates a new block of memory that can hold an int
int* b = new int[10] // creates a new block of memory that can hold 10 ints
string* s = new string[2] // creates a new block of memory that can hold 2 strings

Note that new and new[] are a bit different. new creates a single object new[n] creates an array of n objects. You’re probably wondering why we would want to do this. Well there’s a couple reasons:

  1. Objects created with new are not confined to a single scope. An object created without new will be limited to its scope. Once that scope is left, the object is destroyed. Objects created with new are destroyed either when we destroy them ourselves, or when our program terminates.

  2. Now we can dynamically create space for something. If you wanted to create an array for some variable amount of ints for example, you couldn’t do it without new. Creating an array is a compile-time task, meaning the size of the array has to be known before it even gets run. But with new, you can create an array on the spot that will have a size we determined during runtime. (Like how you’ll use new to dynamically allocate an array with enough size to hold the number of rooms)

I won’t try to bog you down with more details, so just know that stuff for now.

delete/delete[]

Just like it sounds, where new allocates memory, delete de-allocates memory. It’s pretty self explanatory

1
2
3
4
5
int* a = new int;
int* b = new int[10];

delete a;
delete[] b;

First we allocated memory for a (4 bytes) and b (40 bytes), then we deallocated memory for a and b.

As a rule of thumb…

Never delete/delete[] something that’s not been created with new/new[]. Consequently, every new/new[] should eventually have a corresponding delete/delete[].

Steps

Here I’m gonna write a general outline for how you should approach writing the program. I recommend following them in the order they are written. Good luck!

Reading The Rooms

The first thing you should check off is reading in the rooms. There’s no requirement for you to use a read_input() function or anything, so approach it the way you feel best. I did it by reading in the filename from the command line args, and then I passed that name to my read function which opened the file and did all the reading. You could open it in main like in lab 8 and pass an ifstream to your read function, or you could just do all your reading in main. It’s up to you!

This is the beef of the lab, and it’s where all your dynamic allocations will go. We’re allocating memory dynamically because we don’t know how many rooms there are going to be. We could have 1 room, or we could have 1000. We don’t know, so we have to allocate memory dynamically. You’ll need to allocate memory for the array of rooms, and then for each room you’ll need to allocate memory for the array of exits.

Another approach…

Alternatively, you could have every room have a fixed array of exits (there can only be 4 per room), and then set the exit indices for each room accordingly. This would be slightly easier to implement than the dynamic method, but less fun. Choose your own adventure!

The reading process is up to you, but I’ll say you should most likely use a combination of ifstream, stringstream, and getline(). The process is

  1. Open the file
  2. Read the title of the room
  3. Read the description of the room
  4. Read exits
  5. Repeat 2-4 until you’ve read all the rooms

Each section of the file is delimited by a ~, so you’ll know where to stop. Also hint: Each room consists of 3 ~’s, which should be a good indicator of how to count the number of rooms.

The goal of this section is to be able to print a formatted output of the rooms. (This is not part of the lab, just a checklist) Once you can do that, you’re in good shape to move onto the next step. Here’s what my formatted output looks like after reading the room1 file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
Title: Room #0 - @ index 0
Desc: You are at the start. Your journey begins..
Exits: 
s 5

Title: Room #1 - @ index 1
Desc: You see a portrait of Dr. Marz hanging on the wall. What could it mean?
Exits: 
s 6

Title: Room #2 - @ index 2
Desc: There seem to be some animal droppings in the corner. Gross. 
Exits: 
e 3

Title: Room #3 - @ index 3
Desc: A dirty hall with cockroaches crawling around. Is this a student apartment complex?
Exits: 
w 2
e 4

Title: Room #4 - @ index 4
Desc: this is room 4s description
Exits: 
w 3
s 9

Title: Room #5 - @ index 5
Desc: You're near the start.
Exits: 
e 6
n 0

Title: Room #6 - @ index 6
Desc: There are many passageways here.
It would be easy to get lost...
Exits: 
s 11
w 5
e 7
n 1

Title: Room #7 - @ index 7
Desc: You hear a faint buzzing noise
Exits: 
e 8
w 6

Title: Room #8 - @ index 8
Desc: Have I been here before?
Exits: 
e 9
w 7

Title: Room #9 - @ index 9
Desc: Nothing remarkable.
Exits: 
w 8
n 4

Title: Room #10 - @ index 10
Desc: There's a mural of WWII propaganda on the wall. 
Exits: 
s 15

Title: Room #11 - @ index 11
Desc: Scrawled in blood on the wall is a message:
"post on piazza instead of emailing your ta"
what could it mean?
Exits: 
n 6
e 12

Title: Room #12 - @ index 12
Desc: A three way junction.
Exits: 
s 17
w 11
e 13

Title: Room #13 - @ index 13
Desc: There's bread crumbs on the floor. 
Exits: 
e 14
s 18
w 12

Title: Room #14 - @ index 14
Desc: There's a panel on the ceiling but you 
can't quite reach it. 
Exits: 
w 13
s 19

Title: Room #15 - @ index 15
Desc: Sitting on a desk is a bowl of soup and some tea
Exits: 
e 16
n 10

Title: Room #16 - @ index 16
Desc: There's a collection of rusty old road signs
Exits: 
w 15
e 17

Title: Room #17 - @ index 17
Desc: Someone has been here before you. There are footprints and signs of a struggle. 
Exits: 
n 12
w 16
s 22

Title: Room #18 - @ index 18
Desc: There's a trail of crumbs heading south..
Exits: 
s 23
n 13

Title: Room #19 - @ index 19
Desc: It's empty
Exits: 
n 14

Title: Room #20 - @ index 20
Desc: There's a pile of old newspapers in the corner. 
Exits: 
e 21

Title: Room #21 - @ index 21
Desc: You get the feeling you're closer to the exit now. 
Exits: 
e 22
w 20

Title: Room #22 - @ index 22
Desc: There's a wooden chair leaned against the wall. 
Exits: 
w 21
n 17

Title: Room #23 - @ index 23
Desc: There's a draft in here. 
Exits: 
n 18
e 24

Title: Room #24 - @ index 24
Desc: Freedom!!! A door opens to the outside. 
Exits: 
w 23

It prints the title of every room, the description, and all of the exits on separate lines. If you can print all of this from what you’ve read from the file (after you’ve read everything in), then you’ve done it correctly (probably)

Player Options

Now that you’ve completed the reading portion, you should have an object that contains all of the information we got from it. From there, you should provide methods that allow the player to interact with the rooms such as

  • Look() - prints the title, description and exit directions.
  • Move() - moves the player to a different room based on the direction key entered
  • Quit() - quits your program. This method should de-allocate any memory that was allocated.

Or just make some functions that do this if you didn’t chose to make a class. OR you could just do it all in main without functions. It’s up to you.