Skip to content

Commit f979849

Browse files
authoredFeb 5, 2025
Update Maze-Solver-A-to-B.py
1 parent dd121c2 commit f979849

File tree

1 file changed

+92
-53
lines changed

1 file changed

+92
-53
lines changed
 

‎Maze-Solver-A-to-B.py

+92-53
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import pygame
22
from random import randint
33
import random
4+
from collections import deque
45

56
pygame.init()
67

7-
GRID_SIZE = 100
8-
SQUARE_SIZE = 5
9-
SPEED = float('inf')
8+
GRID_SIZE = 20
9+
SQUARE_SIZE = 25
10+
SPEED = 10 # SPEED can be changed to speed up the visual process
1011

1112
WHITE = (255, 255, 255)
12-
BLACK = (0, 0, 0)
13-
RED = (255, 0, 0)
1413
BLUE = (0, 0, 255)
15-
GREEN = (0, 255, 0)
14+
PURPLE = (255, 0, 255)
15+
BLACK = (0, 0, 0)
16+
1617

18+
# Line drawing function
1719

18-
def lines(screen, clock, tup, x, y):
20+
def lines(screen, tup, x, y):
1921
if tup[0]:
2022
pygame.draw.line(screen, BLACK, (x, y + SQUARE_SIZE), (x + SQUARE_SIZE, y + SQUARE_SIZE), SQUARE_SIZE // 5)
2123
if tup[1]:
@@ -24,14 +26,12 @@ def lines(screen, clock, tup, x, y):
2426
pygame.draw.line(screen, BLACK, (x, y), (x, y + SQUARE_SIZE), SQUARE_SIZE // 5)
2527
if tup[3]:
2628
pygame.draw.line(screen, BLACK, (x + SQUARE_SIZE, y), (x + SQUARE_SIZE, y + SQUARE_SIZE), SQUARE_SIZE // 5)
27-
clock.tick(SPEED)
28-
pygame.display.flip()
2929

3030

31-
def new_maze():
32-
# New mazes are created by using depth-first search
33-
# to fill out a grid from a random spot on that grid without overlapping with discovered "cells"
31+
# New mazes are created by using depth-first search to fill out a grid from a random spot on that grid
32+
# without overlapping with discovered "cells"
3433

34+
def new_maze(screen):
3535
start = randint(0, (GRID_SIZE ** 2 - 1))
3636
discovered = [False] * (GRID_SIZE ** 2)
3737
todo = [(start, -1)]
@@ -56,75 +56,114 @@ def new_maze():
5656
random.shuffle(consider)
5757
todo += consider
5858

59+
for cell in connected:
60+
tup = [True, True, True, True]
61+
if cell + GRID_SIZE in connected[cell]:
62+
tup[0] = False
63+
if cell - GRID_SIZE in connected[cell]:
64+
tup[1] = False
65+
if cell - 1 in connected[cell]:
66+
tup[2] = False
67+
if cell + 1 in connected[cell]:
68+
tup[3] = False
69+
x = (cell % GRID_SIZE) * SQUARE_SIZE
70+
y = (cell // GRID_SIZE) * SQUARE_SIZE
71+
lines(screen, tup, x, y)
5972
return connected
6073

6174

62-
def draw_maze(screen, clock, start, graph):
63-
# To visually show the maze being drawn, I used pygame to display the solver discovering walls as it moves throughout the invisible maze.
64-
# The intention was to make it look as if a person who was plopped into a random maze were filling out their own map of said maze.
65-
# Green squares mark fully processed "cells", blue squares mark discovered but not processed "cells", and the black lines represent the walls blocking movement from each "cell"
75+
# Paths are drawn by using breadth-first search to first determine the fastest path
76+
# from point A to B (The first blue to purple)
6677

78+
def draw_path(screen, clock, start, graph):
6779
pygame.draw.line(screen, BLACK, (0, 0), (GRID_SIZE * SQUARE_SIZE - 1, 0), SQUARE_SIZE // 5)
6880
pygame.draw.line(screen, BLACK, (GRID_SIZE * SQUARE_SIZE - 1, 0),
6981
(GRID_SIZE * SQUARE_SIZE - 1, GRID_SIZE * SQUARE_SIZE - 1), SQUARE_SIZE // 5)
7082
pygame.draw.line(screen, BLACK, (0, (GRID_SIZE * SQUARE_SIZE) - 1),
7183
(GRID_SIZE * SQUARE_SIZE - 1, GRID_SIZE * SQUARE_SIZE - 1), SQUARE_SIZE // 5)
7284
pygame.draw.line(screen, BLACK, (0, 0), (0, GRID_SIZE * SQUARE_SIZE - 1), SQUARE_SIZE // 5)
7385

74-
# This depth-first search algorithm both does all of the jobs said above and also displays the "cells" in their updated states
75-
discovered = [False] * (len(graph))
76-
processed = [False] * (len(graph))
77-
todo = [(start)]
78-
connected = {}
79-
while todo:
80-
square = todo.pop()
81-
row = square // GRID_SIZE
82-
column = square % GRID_SIZE
86+
# The endpoint of the maze is randomly generated
87+
end = randint(0, (GRID_SIZE ** 2 - 1))
88+
89+
# Breadth-first search is used to find the fastest point from A to B
90+
discovered = deque([start])
91+
processed = set()
92+
paths = {start: [start]}
93+
while discovered:
94+
if end in discovered:
95+
break
96+
node = discovered.popleft()
97+
if node not in processed:
98+
processed.add(node)
99+
for vertex in graph[node]:
100+
if not vertex == node:
101+
paths[vertex] = list(paths[node])
102+
paths[vertex].append(vertex)
103+
discovered += graph[node]
104+
105+
row = end // GRID_SIZE
106+
column = end % GRID_SIZE
107+
x = column * SQUARE_SIZE
108+
y = row * SQUARE_SIZE
109+
tup = [True, True, True, True]
110+
for neighbor in graph[end]:
111+
if neighbor == end + GRID_SIZE:
112+
tup[0] = False
113+
elif neighbor == end - GRID_SIZE:
114+
tup[1] = False
115+
elif neighbor == end - 1:
116+
tup[2] = False
117+
elif neighbor == end + 1:
118+
tup[3] = False
119+
rectangle = ((x, y), (SQUARE_SIZE, SQUARE_SIZE))
120+
pygame.draw.rect(screen, PURPLE, rectangle)
121+
lines(screen, tup, x, y)
122+
clock.tick(SPEED)
123+
pygame.display.flip()
124+
125+
# The fastest path from point A to point B is now being displayed
126+
for current_cell in paths[end]:
127+
row = current_cell // GRID_SIZE
128+
column = current_cell % GRID_SIZE
83129
x = column * SQUARE_SIZE
84130
y = row * SQUARE_SIZE
85-
rectangle = ((x, y), (SQUARE_SIZE, SQUARE_SIZE))
86-
pygame.display.flip()
87131
tup = [True, True, True, True]
88-
pygame.draw.rect(screen, RED, rectangle)
132+
for neighbor in graph[current_cell]:
133+
if neighbor == current_cell + GRID_SIZE:
134+
tup[0] = False
135+
elif neighbor == current_cell - GRID_SIZE:
136+
tup[1] = False
137+
elif neighbor == current_cell - 1:
138+
tup[2] = False
139+
elif neighbor == current_cell + 1:
140+
tup[3] = False
141+
rectangle = ((x, y), (SQUARE_SIZE, SQUARE_SIZE))
142+
pygame.draw.rect(screen, BLUE, rectangle)
143+
lines(screen, tup, x, y)
89144
clock.tick(SPEED)
90145
pygame.display.flip()
91-
if discovered[square] and not processed[square]:
92-
tup = list(connected[square])
93-
pygame.draw.rect(screen, GREEN, rectangle)
94-
lines(screen, clock, tup, x, y)
95-
processed[square] = True
96-
elif not discovered[square]:
97-
discovered[square] = True
98-
todo.append(square)
99-
for neighbor in graph[square]:
100-
if neighbor == square + GRID_SIZE:
101-
tup[0] = False
102-
elif neighbor == square - GRID_SIZE:
103-
tup[1] = False
104-
elif neighbor == square - 1:
105-
tup[2] = False
106-
elif neighbor == square + 1:
107-
tup[3] = False
108-
if not discovered[neighbor]:
109-
todo.append(neighbor)
110-
connected[square] = list(tup)
111-
pygame.draw.rect(screen, BLUE, rectangle)
112-
lines(screen, clock, tup, x, y)
113146

114147

115148
def main():
116149
clock = pygame.time.Clock()
117150
screen = pygame.display.set_mode((GRID_SIZE * SQUARE_SIZE, GRID_SIZE * SQUARE_SIZE))
118151

152+
# This continuously makes new mazes to be solved
119153
while True:
120154
for event in pygame.event.get():
121155
if event.type == pygame.QUIT:
122156
pygame.quit()
123157

124158
screen.fill(WHITE)
125-
start = randint(0, (GRID_SIZE ** 2 - 1)) # Picks random starting point for the filler
126-
g = new_maze() # Generates a new maze to fill
127-
draw_maze(screen, clock, start, g) # Fills maze
159+
start = randint(0, (GRID_SIZE ** 2 - 1))
160+
161+
# Mazes are of course randomly generated but, in the right format,
162+
# could be predetermined and put into the "new_maze" function as a replacement for "g"
163+
g = new_maze(screen)
164+
draw_path(screen, clock, start, g)
165+
166+
pygame.display.flip()
128167

129168

130169
if __name__ == "__main__":

0 commit comments

Comments
 (0)
Please sign in to comment.