From cf9f4f108f8d19075fb90223131e4b841cd25091 Mon Sep 17 00:00:00 2001 From: Krzysztof Rudnicki Date: Tue, 28 Mar 2023 18:42:33 +0200 Subject: [PATCH 1/2] chore: change heuristic to euclidean, make random heuristic work --- main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 95dcacd3..89baafe3 100644 --- a/main.py +++ b/main.py @@ -100,7 +100,7 @@ class MazeSolver: for neighbor in self.get_neighbors(current): if neighbor not in visited: new_path = path + [neighbor] - heuristic, heuristic_time = self.heuristic_manhattan(neighbor) + heuristic, heuristic_time = self.heuristic_euclidean(neighbor) heuristic_total_time += heuristic_time heuristics_called += 1 heapq.heappush( @@ -120,7 +120,7 @@ class MazeSolver: # push onto the queue (which becomes heapq), element containing values # we use heapq so the element with lowest heuristic value will always # be at the top of heap - heuristic = self.heuristic_manhattan(self.start) + heuristic = self.heuristic_euclidean(self.start) heapq.heappush( queue, (heuristic, self.start, [self.start]) ) @@ -155,7 +155,7 @@ class MazeSolver: heuristic_time = end_time - start_time return heuristic, heuristic_time - def heuristic_random(self): + def heuristic_random(self, position): """Heuristic function that just returns random value between 0 and 1""" start_time = time.perf_counter() heuristic = random() From 8561573f0a4b23ce618e3e1f5dd211c6f9a588f6 Mon Sep 17 00:00:00 2001 From: Jakub Kliszko Date: Wed, 29 Mar 2023 01:42:09 +0200 Subject: [PATCH 2/2] print visited nodes, fix printing and saving, lint code # Conflicts: # main.py --- main.py | 71 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/main.py b/main.py index 89baafe3..ce1d4b8d 100644 --- a/main.py +++ b/main.py @@ -100,16 +100,17 @@ class MazeSolver: for neighbor in self.get_neighbors(current): if neighbor not in visited: new_path = path + [neighbor] - heuristic, heuristic_time = self.heuristic_euclidean(neighbor) + heuristic, heuristic_time = self.heuristic_euclidean( + neighbor) heuristic_total_time += heuristic_time heuristics_called += 1 heapq.heappush( queue, (heuristic, neighbor, new_path) ) if not self.test: - print_maze(self.maze, new_path) + print_maze(self.maze, path, visited) print() - return path, heuristic_total_time, heuristics_called + return path, visited, heuristic_total_time, heuristics_called def solve(self): """Solves the maze""" @@ -138,7 +139,8 @@ class MazeSolver: def heuristic_manhattan(self, position): """Heuristic function that uses Manhattan distance""" start_time = time.perf_counter() - heuristic = abs(position[0] - self.end[0]) + abs(position[1] - self.end[1]) + heuristic = abs(position[0] - self.end[0]) + \ + abs(position[1] - self.end[1]) end_time = time.perf_counter() heuristic_time = end_time - start_time return heuristic, heuristic_time @@ -149,7 +151,8 @@ class MazeSolver: """Heuristic function that uses Euclidean distance""" start_time = time.perf_counter() heuristic = ( - abs(position[0] - self.end[0]) ** 2 + abs(position[1] - self.end[1]) ** 2 + abs(position[0] - self.end[0]) ** 2 + + abs(position[1] - self.end[1]) ** 2 ) ** 0.5 end_time = time.perf_counter() heuristic_time = end_time - start_time @@ -180,14 +183,18 @@ def load_maze(maze_file_name): return maze -def print_maze(maze, path=None): +def print_maze(maze, path=None, visited=None): """Prints the maze""" if path is None: path = [] + if visited is None: + visited = [] for row_i, row in enumerate(maze): for col_i, cell in enumerate(row): - if (row_i, col_i) in path: + if (row_i, col_i) in path and cell == " ": print("*", end="") + elif (row_i, col_i) in visited and cell == " ": + print("·", end="") else: print(cell, end="") print() @@ -203,16 +210,21 @@ def create_maze_folder(solved): os.mkdir(folder_name) return folder_name -def save_maze(maze, solved=True, path=None, saved_file="Maze", iteration=0): + +def save_maze(maze, solved=True, path=None, visited=None, saved_file="Maze", iteration=0): """Saves maze from array to txt file""" folder_name = create_maze_folder(solved) - with open(f"{folder_name}/{iteration}{saved_file}", "w", encoding="utf8") as maze_file: - if path is None: - path = [] + if path is None: + path = [] + if visited is None: + visited = [] + with open(f"{folder_name}/{iteration}{os.path.basename(saved_file)}", "w", encoding="utf8") as maze_file: for row_i, row in enumerate(maze): for col_i, cell in enumerate(row): - if (row_i, col_i) in path: + if (row_i, col_i) in path and cell == " ": maze_file.write("*") + elif (row_i, col_i) in visited and cell == " ": + maze_file.write("·") else: maze_file.write(cell) if solved: @@ -220,6 +232,7 @@ def save_maze(maze, solved=True, path=None, saved_file="Maze", iteration=0): if not solved: maze_file.write("\n") + def fill_generated_maze(hor, ver, width): """ Fills generated maze array from horizontal and vertical lines """ maze_data = "" @@ -231,6 +244,7 @@ def fill_generated_maze(hor, ver, width): maze_data = "".join(maze_data_list) return maze_data + def make_maze(width=16, height=8): """ generate maze with given width and height """ vis = [[0] * width + [1] for _ in range(height)] + [[1] * (width + 1)] @@ -241,27 +255,30 @@ def make_maze(width=16, height=8): vis[y_coordinate][x_coordinate] = 1 neighbors = [(x_coordinate - 1, y_coordinate), - (x_coordinate, y_coordinate + 1), - (x_coordinate + 1, y_coordinate), - (x_coordinate, y_coordinate - 1)] + (x_coordinate, y_coordinate + 1), + (x_coordinate + 1, y_coordinate), + (x_coordinate, y_coordinate - 1)] shuffle(neighbors) for x_coordinate_neighbor, y_coordinate_neighbor in neighbors: if vis[y_coordinate_neighbor][x_coordinate_neighbor]: continue if x_coordinate_neighbor == x_coordinate: - hor[max(y_coordinate, y_coordinate_neighbor)][x_coordinate] = "# " + hor[max(y_coordinate, y_coordinate_neighbor) + ][x_coordinate] = "# " if y_coordinate_neighbor == y_coordinate: - ver[y_coordinate][max(x_coordinate, x_coordinate_neighbor)] = " " + ver[y_coordinate][max( + x_coordinate, x_coordinate_neighbor)] = " " walk(x_coordinate_neighbor, y_coordinate_neighbor) walk(randrange(width), randrange(height)) return fill_generated_maze(hor, ver, width) + def print_help(): """prints help""" print( -"""python main.py - run the script against default maze file + """python main.py - run the script against default maze file (any file named maze.txt in the code directory) python main.py filename.txt - run the script against filename.txt file @@ -275,7 +292,8 @@ compares heuristic speed and path length python main.py -g --generate [NUMBER] - generates as many mazes as entered in Number parameter and puts it in the generatedMazes folder""" - ) + ) + def test_mode(): """ Loads and solves multiple mazes in order to compare heuristics """ @@ -293,13 +311,13 @@ def test_mode(): solver_test = MazeSolver(loaded_maze, TEST_MODE) # Find path using MazeSolver solve method start_time = time.perf_counter() - solved_path, heuristic_total_time, heuristics_called = solver_test.solve() + solved_path, visited, heuristic_total_time, heuristics_called = solver_test.solve() heuristic_total_total_time += heuristic_total_time all_heuristic_called += heuristics_called end_time = time.perf_counter() sum_of_time += end_time - start_time sum_of_paths += len(solved_path) - save_maze(loaded_maze, True, solved_path, filename, 0) + save_maze(loaded_maze, True, solved_path, visited, filename, 0) files_amount += 1 if files_amount == 0: print("no mazes found! Generate some using python main.py -g [NUMBER]") @@ -315,6 +333,7 @@ def test_mode(): all_heuristic_called: {all_heuristic_called}, average_heuristic_time: {heuristic_total_total_time / all_heuristic_called}""") + def default(): """ Runs default operation - reads, solves and prints single maze from file """ # Open and load text file to array @@ -322,9 +341,10 @@ def default(): # Initialize MazeSolver object with maze as parameter solver = MazeSolver(loaded_maze, TEST_MODE) # Find path using MazeSolver solve method - solved_path = solver.solve() - print_maze(loaded_maze, solved_path) - save_maze(loaded_maze, True, solved_path, FILE_NAME, 0) + solved_path, visited, _, _ = solver.solve() + print_maze(loaded_maze, solved_path, visited) + save_maze(loaded_maze, True, solved_path, visited, FILE_NAME, 0) + # Ran first in the code if __name__ == "__main__": @@ -350,7 +370,8 @@ if __name__ == "__main__": GENERATE_AMOUNT = int(sys.argv[2]) for n in range(GENERATE_AMOUNT): GENERATED_MAZE = make_maze() - save_maze(GENERATED_MAZE, False, None, f'generated{n}.txt') + save_maze(GENERATED_MAZE, False, None, + None, f'generated{n}.txt') sys.exit() FILE_NAME = sys.argv[1] default()