So we want to generate a maze automatically instead of having to manually create and connect all of the nodes together. This could save a lot of time since in a normal Pacman maze there could be up to 100 nodes to create and connect together. What I would like to do is have a text file that defines the maze. I would then like to use that text file as an input to some method and then have that method output the required nodeList. The file should be something that is easy for us to make, and also something that the computer can easily parse. So we need a system of symbols that lets the computer know what should be interpreted as a node and what should be interpreted as empty space and so on. I'll use the following four symbols:
Remember that nodes are the red circles and each node is connected to some other node via a horizontal and/or vertical path.
Using only these symbols I should be able to generate a text file of any size that defines a maze. If we were to convert the previous nodes we've been using in the last sections into a text file, then it would look like the following image on the right. Create a new file called mazetest.txt and copy the values below into that file. Please note that there is a space between each character.
There are a few things we need to understand about this text file, in case you want to make your own.
Open up the nodes.py file. The first line we'll add is importing numpy. Numpy makes a few things that we want to do a bit easier. For example, we'll use it to read out text file and place it into an array. Then we can do things to that array like transpose it. That's all we really need numpy for. Also go ahead and delete the entire setupTestNodes method. There's no going back now.
We want to read in our text file when we create an object from this class. The 'level' input is just the text file. I named it 'level', because you'll eventually have a different text file for different levels.
We'll get rid of our nodeList and create a nodesLUT instead. This is a dictionary which will contain our nodes, it just makes it easier to look up our nodes based on some value. LUT stands for "Look Up Table".
There are 4 new methods that we'll need to create. Let's go over them next.
So here we'll read in the text file using numpy's loadtxt function. We need to set the dtype to '<U1' or else it will try to read in the data as floats and create an error when it encounters the '.' character.
At this point we're entering in a Numpy 2d numpy array. So we're just going to go through it and anytime we find a '+' character, we'll create an entry in the lookup table with the row and column that node was found and create a Node object.
The constructKey method simply converts a row and column in a text file to actual pixel values on the screen.
So we're going to first connect the nodes horizontally. We do that by going one row at a time and looking for a '+' character. When we find one we need to know if the key value is None or not. The key variable just contains the row and column of a node. It allows us to know if two nodes need to be connected horizontally or not. As we go through the values in the row when we come across a '+' character and the key is None, then we set the key to be the row and column of that node we found. But if we encounter any characters that are not '-' which is the symbol we're using to connect nodes horizontally, then set the key to None again. However, if we come across another node while the key is not None, then that means those nodes should be connected horizontally. Then the new nodes row and column become the key value and we continue like this for all of the rows.
This is similar to how we connected the nodes horizontally. However, this time we're going to transpose the data array. What that basically means is that the columns become rows and the rows become columns. That way we can basically follow the same logic when connecting up the nodes vertically.
The main differences here are that we're looking for the '|' symbol instead of the '-' symbol and when we connect the nodes we need to reference UP and DOWN instead of LEFT and RIGHT. Also, the key needs to be flipped to read (j, i) instead of (i, j). That's because of the transpose. If we have an array that has shape (m, n), then the shape of the transposed array is (n, m).
Other than that it's basically the same as the previous code.
Also included are 2 methods that will allow us to get a node if we give either the (x, y) pixel location or the (column, row) tile location.
We're also going to add a method here that tells us the node we want Pacman to start on. For now it's just going to be the first node in the node lookup table. We'll change it later on.
We finally need to change how we reference the nodes for drawing to the screen since we're using the nodesLUT instead of the nodesList to keep track of our nodes.
We'll need to modify the startGame method in the GameController class by passing in the name of the text file when we create the NodeGroup object. We need to remove the line where we were testing the nodes. Then for the Pacman object we'll call the method we just created that will return the node Pacman should start on.
When running the code now you should see something similar to the image on the right. That's to be expected, however, you should still be able to move around the maze normally. In the next section we'll change the maze file to a Pacman maze.