-
Notifications
You must be signed in to change notification settings - Fork 1
/
bv_maze_navigation.py
374 lines (293 loc) · 12.6 KB
/
bv_maze_navigation.py
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
"""
This is a simple game in which the user navigates through a maze
The maze will be chosen at random from three options
I've never made a maze before... so the relative difficult is pretty random
First maze has a couple loops
Second maze is the largest and has no loops
Third maze is the smallest and has no loops
Fourth maze is generated using the function random_maze and is not in game yet (loops are possible, quality not ensured)
There are no enemies in the maze! Simply navigate your way through to find the exit
"""
import random
import numpy as np
import maze
def print_header():
print('=============================================')
print()
print(" BeardedVagabond's Maze Game")
print()
print('=============================================')
print()
def initialize():
"""
Creates the maze_layout list of rooms and the player object
:return: maze_choice, maze_layout, player
"""
# create all possible rooms
rooms = [maze.Room([0, 0, 0, 0], 'small'), # Closed room(0)
maze.Room([1, 1, 1, 1], 'small'), # Open room(1)
maze.Room([1, 0, 1, 0], 'small'), # NS corridor(2)
maze.Room([0, 1, 0, 1], 'small'), # EW corridor(3)
maze.Room([1, 0, 0, 1], 'small'), # NW corner(4)
maze.Room([1, 1, 0, 0], 'small'), # NE corner(5)
maze.Room([0, 1, 1, 0], 'small'), # SE corner(6)
maze.Room([0, 0, 1, 1], 'small'), # SW corner(7)
maze.Room([1, 1, 0, 1], 'small'), # EW with N tee(8)
maze.Room([1, 1, 1, 0], 'small'), # NS with E tee(9)
maze.Room([0, 1, 1, 1], 'small'), # EW with S tee(10)
maze.Room([1, 0, 1, 1], 'small'), # NS with W tee(11)
maze.Room([1, 0, 0, 0], 'small'), # N dead end(12)
maze.Room([0, 1, 0, 0], 'small'), # E dead end(13)
maze.Room([0, 0, 1, 0], 'small'), # S dead end(14)
maze.Room([0, 0, 0, 1], 'small'), # W dead end(15)
]
# create list using room indexes (will be converted when chosen)
maze_1 = [[13, 3, 10, 7, 14, 0, 0],
[13, 3, 4, 5, 11, 0, 0],
[6, 7, 6, 3, 8, 7, 0],
[2, 5, 1, 3, 15, 2, 0],
[5, 3, 8, 7, 6, 4, 0],
[0, 0, 0, 5, 11, 0, 0],
[0, 0, 0, 0, 5, 3, 15],
]
maze_2 = [[13, 10, 3, 3, 7, 14, 0, 14, 0, 0, 0],
[0, 2, 13, 3, 4, 2, 0, 2, 0, 0, 0],
[0, 5, 10, 3, 7, 2, 0, 9, 3, 3, 7],
[0, 13, 4, 6, 8, 4, 14, 12, 14, 0, 2],
[0, 0, 6, 8, 3, 7, 2, 0, 5, 3, 11],
[0, 13, 1, 3, 7, 12, 2, 6, 7, 13, 11],
[0, 0, 2, 14, 5, 3, 8, 11, 5, 3, 4],
[0, 0, 5, 4, 0, 13, 3, 4, 0, 0, 0],
]
maze_3 = [[13, 3, 7, 13, 7, 0, 14],
[14, 13, 8, 7, 2, 0, 2],
[2, 6, 7, 5, 8, 7, 2],
[5, 4, 5, 3, 3, 8, 4],
]
maze_4 = random_maze(10)
print('Layout for unused random maze (for demonstration/inspection):')
vizualize_maze(maze_4)
# create list of mazes, choose random maze for game, convert maze indexes to rooms
mazes = [maze_1, maze_2, maze_3] # random maze not used at this time
maze_choice = random.choice(range(0, len(mazes)))
maze_layout = mazes[maze_choice]
maze_layout = index_to_rooms(maze_layout, rooms)
# verify player name input
player_name = ''
while not player_name:
player_name = input('What is your name? ')
if not player_name:
print('Input not recognized. Please re-enter a name.\n')
player = maze.Player(player_name, 0, 0)
return maze_choice, maze_layout, player
def vizualize_maze(maze_to_draw):
"""
Uses Box Drawing unicode (u2500 - u257F) to draw the maze_to_draw
:param maze_to_draw: Maze to be visualized
:return: UI printout of maze_to_draw
"""
# Create dictionary for box drawing unicode lookup
# NOTE: each character uses a space before the symbol
encoding = {0: ' \u2573', # Closed room
1: ' \u253C', # Open room
2: ' \u2502', # NS corridor
3: ' \u2500', # EW corridor
4: ' \u2518', # NW corner
5: ' \u2514', # NE corner
6: ' \u250C', # SE corner
7: ' \u2510', # SW corner
8: ' \u2534', # EW with N tee
9: ' \u251C', # NS with E tee
10: ' \u252C', # EW with S tee
11: ' \u2524', # NS with W tee
12: ' \u257D', # N dead end
13: ' \u257E', # E dead end
14: ' \u257F', # S dead end
15: ' \u257C', # W dead end
}
# Determine shape of maze_to_draw
x_len = len(maze_to_draw[0])
y_len = len(maze_to_draw)
draw = ['' for x in range(y_len)]
# Encode maze_to_draw
for i in range(0, x_len): # columns
for j in range(0, y_len): # rows
draw[j] += encoding[maze_to_draw[j][i]]
for row in draw:
print(row)
def random_maze(size):
"""
Creates a random square maze of dimension = size
:param size: Square dimension of desired maze
:return: A list describing a random maze using index format
"""
# Pad maze with 0 with zero index perimeter
size += 2
new_maze = np.empty((size, size))
new_maze[0, :] = np.zeros((1, size))
new_maze[-1, :] = np.zeros((1, size))
new_maze[:, 0] = np.zeros(size)
new_maze[:, -1] = np.zeros(size)
# Define possible indexes for each compass direction (all options)
north = [1, 2, 4, 5, 8, 9, 11, 12]
east = [1, 3, 5, 6, 8, 9, 10, 13]
south = [1, 2, 6, 7, 9, 10, 11, 14]
west = [1, 3, 4, 7, 8, 10, 11, 15]
# Set starting position
new_maze[1][1] = 13
# Loop through by row ensuring room connections
for i in range(1, size - 1):
for j in range(0, size - 2):
# Determine if a connection is needed to adjacent rooms
left_door = 1 if new_maze[i][j] in east else 0
top_door = 1 if new_maze[i - 1][j + 1] in south else 0
# bottom_door = 1 if new_maze[i + 1][j + 1] in north else 0
# right_door = 1 if new_maze[i][j + 1] in west else 0
if i < size - 2: # for all rows above bottom row of actual maze
if j < size - 3: # for all columns before rightmost of actual maze
# Choose room at random that will connect with adjacent rooms
if left_door:
if top_door:
options = list(set(west) & set(north))
else:
options = list(set(west) - set(north))
else:
if top_door:
options = list(set(north) - set(west))
else:
options = list((set(south) & set(east)) - set(north) - set(west))
else:
# Right edge of maze... can never go east
if left_door:
if top_door:
options = list((set(west) & set(north)) - set(east))
else:
options = list((set(west) - set(north)) - set(east))
else:
if top_door:
options = list(set(north) - set(west) - set(east))
else:
options = list(set(south) - set(north) - set(west) - set(east))
else: # Bottom edge of maze... can never go south
if j < size - 3: # for all columns before rightmost of actual maze
if left_door:
if top_door:
options = list((set(west) & set(north)) - set(south))
else:
options = list((set(west) - set(north)) - set(south))
else:
if top_door:
options = list(set(north) - set(west) - set(south))
else:
options = list(set(east) - set(west) - set(north) - set(south))
else:
# Bottom right corner... can only go west and north
if left_door:
if top_door:
options = list((set(west) & set(north)) - set(south) - set(east))
else:
options = list((set(west) - set(north)) - set(south) - set(east))
else:
if top_door:
options = list(set(north) - set(west) - set(south) - set(east))
else:
options = [0]
if i == 1 and j == 1:
new_maze[1][1] = 13 # enforce starting position
new_maze[i][j + 1] = random.choice(list(set(options) - {15})) # make sure dead end not beside start
else:
new_maze[i][j + 1] = random.choice(options)
# Extract actual maze
new_maze = new_maze[1:-1, 1:-1]
return np.ndarray.tolist(new_maze.astype(int))
def index_to_rooms(maze_x, rooms):
"""
This function replaces maze lists of indexes with room objects
:param maze_x: Maze to be recast into rooms
:param rooms: List of possible rooms
:return: List describing maze layout with room objects
"""
i = 0
for x in maze_x:
j = 0
for y in x:
maze_x[i][j] = rooms[y]
j += 1
i += 1
return maze_x
def game_loop(maze_choice, maze_layout, player):
"""
Runs the main game loop
Reads command input from the player and responds accordingly
:param maze_choice: int index of which maze was chosen
:param maze_layout: list describing the layout of the maze rooms
:param player: Player object with inputted name
:return: UI output
"""
# Set end of maze conditions
x_finish = 6 if maze_choice == 0 else 7 if maze_choice == 1 else 0 if maze_choice == 2 else 99
y_finish = 6 if maze_choice == 0 else 0 if maze_choice == 1 else 1 if maze_choice == 2 else 99
while True:
cmd = input('Do you wish to [M]ove, [L]ook around, [C]heck map, or E[x]it? ')
if not cmd:
print("No input detected, please re-enter a command\n")
continue
cmd = cmd.lower().strip()
if cmd == 'm':
move = 0
while not move:
move = move_loop(maze_layout, move, player)
elif cmd == 'l':
player.look(maze_layout[player.y_location][player.x_location])
print()
elif cmd == 'x':
print('Thanks for playing!')
break
elif cmd == 'c':
player.check_map(maze_choice)
print()
else:
print(f"I'm sorry, {cmd} was not recognized. Please re-enter a command.\n")
# Check end of maze condition
if player.x_location == x_finish and player.y_location == y_finish:
print(f'{player.name} has made it to the end of the maze! Congratulations!!')
print("'Thanks for playing!")
break
def move_loop(maze_layout, move, player):
print('Where would you like to move?')
direction = ''
while not direction:
direction = input('[N]orth, [E]ast, [S]outh, [W]est: ')
if not direction:
print("No input detected, please re-enter a direction\n")
continue
direction = direction.lower().strip()
if direction == 'n':
move = player.move(direction, maze_layout)
if move:
print(f'{player.name} moves north into the next room\n')
elif direction == 'e':
move = player.move(direction, maze_layout)
if move:
print(f'{player.name} moves east into the next room\n')
elif direction == 's':
move = player.move(direction, maze_layout)
if move:
print(f'{player.name} moves south into the next room\n')
elif direction == 'w':
move = player.move(direction, maze_layout)
if move:
print(f'{player.name} moves west into the next room\n')
else:
print(f"Sorry, the command {direction} was not recognized. Please re-enter a command.\n")
return move
def main():
"""
Contains function calls in order required to execute the game
:return: Standard 0 for completion and 1 for error
"""
print_header()
maze_choice, maze_layout, player = initialize()
game_loop(maze_choice, maze_layout, player)
if __name__ == '__main__':
main()