#44
Maze Runner 2D

This two-dimensional maze runner shows the player a top-down, bird’s-eye view of a maze file you create in a text editor, such as the IDE you use to write your .py files. Using the WASD keys, the player can move up, left, down, and right, respectively, to navigate the @ symbol toward the exit marked by the X character.

To make a maze file, open a text editor and create the following pattern. Don’t type the numbers along the top and left side; they are only there for reference:

 123456789
1#########
2#S# # # #
3#########
4# # # # #
5#########
6# # # # #
7#########
8# # # #E#
9#########

The # characters represent walls, the S marks the start, and the E marks the exit. The # characters in bold represent walls that you can remove to form your maze. Don’t remove the walls at odd-numbered columns and odd-numbered rows, and don’t remove the borders of the maze. When you’re done, save the maze as a .txt (text) file. It could look something like this:

#########
#S    # #
# ### # #
# #   # #
# ##### #
#   #   #
### # # #
#     #E#
#########

Of course, this is a simple maze. You can make maze files of any size as long as they have an odd number of rows and columns. Be sure it’ll still fit on the screen, though! You can also download maze files from https://invpy.com/mazes/.

The Program in Action

When you run mazerunner2d.py, the output will look like this:

Maze Runner 2D, by Al Sweigart [email protected]

(Maze files are generated by mazemakerrec.py)
Enter the filename of the maze (or LIST or QUIT):
> maze65x11s1.txt
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░@░     ░       ░               ░                       ░   ░   ░
░ ░░░░░ ░ ░░░ ░ ░ ░░░░░░░ ░░░░░ ░░░░░░░░░░░░░░░░░░░░░ ░░░ ░ ░ ░ ░
░ ░   ░     ░ ░ ░   ░   ░     ░   ░           ░     ░   ░ ░   ░ ░
░ ░ ░ ░░░░░ ░ ░ ░░░░░ ░ ░░░░░ ░░░ ░ ░░░░░░░░░ ░ ░░░ ░░░ ░ ░░░░░ ░
░   ░     ░ ░ ░   ░   ░       ░ ░   ░       ░ ░ ░ ░   ░   ░     ░
░░░░░░░░░ ░░░ ░░░ ░ ░░░░░░░░░░░ ░░░░░ ░ ░░░░░ ░ ░ ░░░ ░░░░░ ░░░░░
░ ░     ░ ░   ░ ░ ░ ░           ░     ░       ░ ░     ░   ░     ░
░ ░ ░ ░░░ ░ ░░░ ░ ░ ░ ░░░░░░░░░░░ ░░░░░░░░░░░░░ ░ ░░░░░ ░ ░░░░░ ░
░   ░       ░       ░                           ░       ░      X░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
                           W
Enter direction, or QUIT: ASD
--snip--

How It Works

The program loads the data for the maze’s walls from a text file and into a dictionary stored in the maze variable. This dictionary has (x, y) tuples for keys and the string in the WALL, EMPTY, START, or EXIT constants for values. Project 45, “Maze Runner 3D,” uses a similar dictionary representation of the maze. The difference between the projects is in the code that renders that maze on the screen. Since Maze Runner 2D is simpler, I recommend becoming familiar with this program first before moving on to Maze Runner 3D.

  1. """Maze Runner 2D, by Al Sweigart [email protected]
  2. Move around a maze and try to escape. Maze files are generated by
  3. mazemakerrec.py.
  4. View this code at https://nostarch.com/big-book-small-python-projects
  5. Tags: large, game, maze"""
  6.
  7. import sys, os
  8.
  9. # Maze file constants:
 10. WALL = '#'
 11. EMPTY = ' '
 12. START = 'S'
 13. EXIT = 'E'
 14.
 15. PLAYER = '@'  # (!) Try changing this to '+' or 'o'.
 16. BLOCK = chr(9617)  # Character 9617 is '░'
 17.
 18.
 19. def displayMaze(maze):
 20.     # Display the maze:
 21.     for y in range(HEIGHT):
 22.         for x in range(WIDTH):
 23.             if (x, y) == (playerx, playery):
 24.                 print(PLAYER, end='')
 25.             elif (x, y) == (exitx, exity):
 26.                 print('X', end='')
 27.             elif maze[(x, y)] == WALL:
 28.                 print(BLOCK, end='')
 29.             else:
 30.                 print(maze[(x, y)], end='')
 31.         print()  # Print a newline after printing the row.
 32.
 33.
 34. print('''Maze Runner 2D, by Al Sweigart [email protected]
 35.
 36. (Maze files are generated by mazemakerrec.py)''')
 37.
 38. # Get the maze file's filename from the user:
 39. while True:
 40.     print('Enter the filename of the maze (or LIST or QUIT):')
 41.     filename = input('> ')
 42.
 43.     # List all the maze files in the current folder:
 44.     if filename.upper() == 'LIST':
 45.         print('Maze files found in', os.getcwd())
 46.         for fileInCurrentFolder in os.listdir():
 47.             if (fileInCurrentFolder.startswith('maze') and
 48.             fileInCurrentFolder.endswith('.txt')):
 49.                 print('  ', fileInCurrentFolder)
 50.         continue
 51.
 52.     if filename.upper() == 'QUIT':
 53.         sys.exit()
 54.
 55.     if os.path.exists(filename):
 56.         break
 57.     print('There is no file named', filename)
 58.
 59. # Load the maze from a file:
 60. mazeFile = open(filename)
 61. maze = {}
 62. lines = mazeFile.readlines()
 63. playerx = None
 64. playery = None
 65. exitx = None
 66. exity = None
 67. y = 0
 68. for line in lines:
 69.     WIDTH = len(line.rstrip())
 70.     for x, character in enumerate(line.rstrip()):
 71.         assert character in (WALL, EMPTY, START, EXIT), 'Invalid character
             at column {}, line {}'.format(x + 1, y + 1)
 72.         if character in (WALL, EMPTY):
 73.             maze[(x, y)] = character
 74.         elif character == START:
 75.             playerx, playery = x, y
 76.             maze[(x, y)] = EMPTY
 77.         elif character == EXIT:
 78.             exitx, exity = x, y
 79.             maze[(x, y)] = EMPTY
 80.     y += 1
 81. HEIGHT = y
 82.
 83. assert playerx != None and playery != None, 'No start in maze file.'
 84. assert exitx != None and exity != None, 'No exit in maze file.'
 85.
 86. while True:  # Main game loop.
 87.     displayMaze(maze)
 88.
 89.     while True:  # Get user move.
 90.         print('                           W')
 91.         print('Enter direction, or QUIT: ASD')
 92.         move = input('> ').upper()
 93.
 94.         if move == 'QUIT':
 95.             print('Thanks for playing!')
 96.             sys.exit()
 97.
 98.         if move not in ['W', 'A', 'S', 'D']:
 99.             print('Invalid direction. Enter one of W, A, S, or D.')
100.             continue
101.
102.         # Check if the player can move in that direction:
103.         if move == 'W' and maze[(playerx, playery - 1)] == EMPTY:
104.             break
105.         elif move == 'S' and maze[(playerx, playery + 1)] == EMPTY:
106.             break
107.         elif move == 'A' and maze[(playerx - 1, playery)] == EMPTY:
108.             break
109.         elif move == 'D' and maze[(playerx + 1, playery)] == EMPTY:
110.             break
111.
112.         print('You cannot move in that direction.')
113.
114.     # Keep moving in this direction until you encounter a branch point.
115.     if move == 'W':
116.         while True:
117.             playery -= 1
118.             if (playerx, playery) == (exitx, exity):
119.                 break
120.             if maze[(playerx, playery - 1)] == WALL:
121.                 break  # Break if we've hit a wall.
122.             if (maze[(playerx - 1, playery)] == EMPTY
123.                 or maze[(playerx + 1, playery)] == EMPTY):
124.                 break  # Break if we've reached a branch point.
125.     elif move == 'S':
126.         while True:
127.             playery += 1
128.             if (playerx, playery) == (exitx, exity):
129.                 break
130.             if maze[(playerx, playery + 1)] == WALL:
131.                 break  # Break if we've hit a wall.
132.             if (maze[(playerx - 1, playery)] == EMPTY
133.                 or maze[(playerx + 1, playery)] == EMPTY):
134.                 break  # Break if we've reached a branch point.
135.     elif move == 'A':
136.         while True:
137.             playerx -= 1
138.             if (playerx, playery) == (exitx, exity):
139.                 break
140.             if maze[(playerx - 1, playery)] == WALL:
141.                 break  # Break if we've hit a wall.
142.             if (maze[(playerx, playery - 1)] == EMPTY
143.                 or maze[(playerx, playery + 1)] == EMPTY):
144.                 break  # Break if we've reached a branch point.
145.     elif move == 'D':
146.         while True:
147.             playerx += 1
148.             if (playerx, playery) == (exitx, exity):
149.                 break
150.             if maze[(playerx + 1, playery)] == WALL:
151.                 break  # Break if we've hit a wall.
152.             if (maze[(playerx, playery - 1)] == EMPTY
153.                 or maze[(playerx, playery + 1)] == EMPTY):
154.                 break  # Break if we've reached a branch point.
155.
156.     if (playerx, playery) == (exitx, exity):
157.         displayMaze(maze)
158.         print('You have reached the exit! Good job!')
159.         print('Thanks for playing!')
160.         sys.exit()

Exploring the Program

Try to find the answers to the following questions. Experiment with some modifications to the code and rerun the program to see what effect the changes have.

  1. What error message do you get if you change character == START on line 74 to character == EXIT?
  2. What happens if you change playery + 1 on line 105 to playery – 1?
  3. What happens if you change (exitx, exity) on line 156 to (None, None)?
  4. What error message do you get if you change while True: on line 89 to while False:?
  5. What happens if you change break on line 104 to continue?
  6. What error message do you get if you change break on line 121 to continue?
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.25.74