Have you ever played a game that feels infinite? As if the map could go on forever, generating new level after new level no matter how much you play? Turns out infinite games aren’t so impossible, or even uncommon! The secret is writing code that creates maps on the spot.
As we dive into the infinite world of endless maps, let’s start with a simple labyrinth written in Python.
PART 1: THE FRAME
Picture your labyrinth as a checkerboard of squares. Each square is referenced by the unique combination of its row number and column number, and each square contains a wall or a hallway. Characters can move into hallways, but can’t walk into, over, or through walls. (Unless they’re ghosts?)
To get started, navigate over to https://repl.it/languages/python3 and add the following code to the left-hand editor:
from random import random numRows = 20 numCols = 40
So far we’ve imported the “random” module, which we need to keep our maps unpredictable. We’ve also create two variables that represent the length and width of our rectangular labyrinth.
To display each square in our map we can use a nested for loop. At any given moment, “i” represents the row number, and “j” the column number. Each variable starts at 0 and increases by a value of 1 each round. Eventually, they’ll max out at the sizes defined by numRows and numCols.
(Remember, in computer science, all lists start at position “0”!)
Next, our labyrinth needs walls around its edges so that it’s not too easy for our heroes to escape. We’ll place these walls in the first row (i == 0), the last row (i == numRows – 1), the first column (j == 0), and the last column (j == numCols – 1). Walls are represented with the character “X”, and hallways with a space:
for i in range(numRows): for j in range(numCols): if i == 0 or j == 0 or i == numRows - 1 or j == numCols -1: print("X", end="") else: print(" ", end="") print()
You need to end your “print” statements with an empty string, otherwise Python starts a new line after each one. With that in mind, don’t forget the print statement at the end of the first for loop! Otherwise you’ll never start a new row. When you’re ready, hit the “run” button and try the code out.
PART 2: ENTRANCES AND EXITS
Next, our characters need to get in and out of this cunning trap. To place entrances and exits in the outer walls, change your if statement to look like this:
if i == 0 and (j == 1 or j==2): print(" ", end="") elif i == numRows-1 and (j == numCols-2 or j==numCols-3): print(" ", end="") elif i == 0 or j == 0 or i == numRows - 1 or j == numCols -1: print("X", end="") else: print(" ", end="")
Why is it important for the first check to be the position of the entrances & exits? What happens if we switch the order in this “if statement”?
PART 3: RANDOM WALLS
Finally — the fun part! To keep our mazes fresh and unexpected, we’ll add walls at random. This makes it unlikely that we’ll generate the same maze twice.
Python’s “random” module gives us access to the “random()” function, which returns a decimal number between 0 and 1. If you want your event to happen only 50% of the time, use the condition “if random() < 0.5”. If you want a 30% chance of success? Then type “if random() < 0.3”. Add this to our previous code, and the maze generator now looks like this: [code language="python"] elif random() < 0.3: print("X", end="") [/code] We use an “elif” instead of an “if” because we only want to print a single character for each square. The moment one of the branches of the “if statement” is executed, we want to ignore all other possibilities. Play around with different probabilities and see how that affects the results! Remember, don’t choose a number smaller than 0 or greater than 1.
PART 4: MORE STRUCTURE
You’ve probably noticed that making everything completely random doesn’t create a map with a “labyrinth” feel. It looks more like a jumbled chaotic soup!
The solution is to add some structure to our randomness. Instead of treating all squares equally, what if we only add walls to every second row and every fifth column? We’re still calling the “random()” function, so there’s a chance no wall will be added. However, certain squares are guaranteed to be hallways. We’ll also add our walls to the chosen locations with a much higher probability.
To check if a row is a multiple of 2 or a column is a multiple of 5, you can use the modulus (mod) operator. The final code looks like this:
from random import random numRows = 20 numCols = 40 for i in range(numRows): for j in range(numCols): if i == 0 and (j == 1 or j==2): print(" ", end="") elif i == numRows-1 and (j == numCols-2 or j==numCols-3): print(" ", end="") elif i == 0 or j == 0 or i == numRows - 1 or j == numCols -1: print("X", end="") elif i % 2 == 0 and random() < 0.6: print("X", end="") elif j % 5 == 0 and random() < 0.5: print("X", end="") else: print(" ", end="") print()
Definitely more maze-like! Again, you can tweak the values of your probabilities depending on how dense you want the walls to be.
PART 5: POLISHING IT OFF
While our code does a decent job, it’s still a pretty simple maze generator. Sometimes it creates labyrinths with no solution.
If you were creating a full game, you’d want to write some code that double-checks that there’s at least one pathway between the entrance and exit. You might even create a system that ranks how difficult the maze is: easy, medium, or hard?
Another solution is to write code that transforms your “broken” map into something functional. This might mean removing a few keys walls, or adding a few dead ends to make the map more challenging.
Figuring out what rules you’re using to guide the randomness is important to creating effective, playable maps. And that depends on your game’s objectives, its gameplay style, and most of all, on your imagination.
PART 6: TAKING IT FURTHER
Try adding some objects to your maze, like hidden treasures and secret keys! To do this, you’ll need to add a new “elif” statement. Choose what letter or symbol represents your cool new object (“!”, “?”, “*”, “O”) and spruce up your labyrinth into some truly worth exploring!
HINT: use a very low percentage, like “0.01” or even “0.005”.
Learn More
Raspberry Pi forum
https://www.raspberrypi.org/forums/viewtopic.php?p=288704
maze generator
http://code.activestate.com/recipes/252127-maze-generator/
mazes
http://reeborg.ca/docs/en/reference/mazes.html
make your own maze
https://worksheets.theteacherscorner.net/make-your-own/maze/