Maze Game
Text based adventure games have a player move through a number of virtual rooms within the program. This code requires us to set up rooms and paths that move between them.
If you think about the game you will see that we need pointers to implement this. Each path needs to know the room it goes to. This cannot be a copy of the room - as circular paths would become impossible.
Instead, what we need is to have one place where the rooms exist, and have the paths point to their destination room.
The player’s location can then also be a pointer to a room. You can think of the value as saying “this is the room I am in” and it points to the room.
Understanding the program
To get started, we can build a part of the code to explore how this can work. We need some tools from the next chapter to wrap this up, but for now we can explore how paths could be created to move to a destination.
We are going to create a virtual world that the user can explore through text. The world will have rooms and paths between these rooms.
Each room will have a title and a description, and in the next chapter we will add a number of paths to other locations.
Each path will have a description and a destination. This will make it simpler to code, as paths go only one way and therefore have only one destination. This would require two paths if you want to join two rooms so that the user can travel in either direction, but alternatives involve more complexity with little savings or other benefits.
For now, the program can create three rooms. The user will be in the first room, and presented with details of two paths - one leading to each of the other rooms. The user will choose which path to take, and then be shown the details of their destination room. Then the program will terminate.
With this we have the main mechanics for a maze game, and when we learn to deal with multiples we can easily start to build multiple rooms each with multiple paths.
Choosing Building Blocks
Structs and Pointers
I would start with the main entities, and the data they will contain. For this, there are two clear entities which we can code as structs: room_data and path_data.
The room will have fields for title and description, while the path will have a description and a destination.
Going forward we will want to have paths within our rooms, so we can start by coding the path struct ahead of the room struct. There is a challenge here, we need to code the room pointer within the path, but the room will be declared after the path. To achieve this we can use a forward declaration.
To do this, we use a typedef
and set up an alias for a pointer to a struct room_struct
. We can name this type room_ptr
. The code for this is shown below.
Then in the path, we can use this room_ptr
type when we declare the destination field.
We also need to fulfil the forward declaration, and provide details of what the struct room_struct
is. This can be coded as shown below:
These are all the types we need, so now we can focus on the functions and procedures that work with these.
Functions and Procedures
There isn’t much logic in this small prototype, as the main objectives are to create rooms and paths. The logic for moving can be coded into main until such time as we can expand the structs with some additional details.
For this we will need to create:
read_integer
function - using either the SplashKitread_line
code orscanf
.new_path
function - to populate a path between rooms.new_room
function - to populate a room with a title and description.print_path
procedure - to output details of a path as part of a list the user can choose fromprint_room
procedure - to output details of a room to the user.move_player
procedure - to move a player to the destination of a path.- The
main
function - to create the rooms, and paths, position the player in the first room, print the details of the room they are in, print the details of the paths, get which path the user wants to take, move the player through the path
Read Integer
Check out the code in the printing and scanning section, or copy in your code that uses read_line
from SplashKit. This function will ensure that the user enters a number so that we can determine which path they want to take.
The pseudocode for this is the same as we covered previously. So there should be nothing new here.
New Path
This function will populate a path struct value and return it to the caller. While this can be achieved with a single line at the moment, we will want to be able to expand the struct going forward and this will mean changes to the way things are initialised. Having a function that is responsible for this means we only have one place we need to update when we want to change how initialisation works.
Below is the header comment for the function. This captures the requirements for what this will need to do. In the code, you will need to declare a path_data
variable, set its title and descriptions, and return it.
New Room
Like the new path function, this function will populate a room struct with the required data. Below if the header comment for the function.
Print Path
Print path will output a paths details to the terminal. This will always be printed in a list, so we can accept a parameter for the index of the path to include. This can then be used by the user to determine which path they want to take.
Print Room
This will also be a simple procedure at this stage. We can print the title, and room description. Going forward we will need to expand this to also display the paths from the room.
Move Player
Move player will perform the actions needed to move a player through a path to a new room. The player is represented by a pointer to room_data
. As we need to update this, we can pass the pointer by reference. In effect, this is a pointer to the pointer that needs to be updated. When you dereference the pointer-pointer, you get to the pointer you want to update.
Let’s take a closer look at how we might create our paths and rooms:
Main
For now, main is going to be a test ground for this program. We can use this to get these building blocks working together.
We will need this to do the following:
- Set up the rooms
- Create some paths
- Get current room to point to room 1
- Show the user the room they are in (current room)
- Show them the paths we set up
- Ask which path they want to take
- Move them through that path
- Print the details of the room they are in (using the current room)
I would suggest you set up 3 rooms and 2 paths. You can picture these paths being in room 1. One path can go to room 2 and the other to room 3. Test it out by taking the different paths.
Try changing one of the paths to go to room 1. What happens to the current room when you take this path?
When you get this to work spend some time thinking about how this is all coming together. You want to focus on understanding how the pointer in current room is working. When this clicks, you will start to see the potential that pointers (and reference in other languages) offer you.
Handling nullptr
In C/C++ you can use NULL
or nullptr
to represent a pointer to nothing. We could use this to create a path that leads… nowhere.
Try updating one of your paths to have the nullptr
as its destination. What happens when you run your program? Why does this happen?
You can guard against this in the print_room
code. See if you can get this working so that it runs even when paths go nowhere.