From 95fcae04d81441721c5e3874c4b4932c7e1f2075 Mon Sep 17 00:00:00 2001 From: Krzysztof Rudnicki Date: Wed, 22 Mar 2023 17:30:25 +0100 Subject: [PATCH] feat: maze generation --- .pylintrc | 3 + main.py | 153 ++++++++++++++++++------------- mazes/maze.txt | 10 -- mazes/maze1.txt | 10 -- notWorking/mazeDeadEnd.txt | 5 - notWorking/mazeNoEnd.txt | 32 ------- notWorking/mazeNoEndNoStart.txt | 32 ------- notWorking/mazeNoPath.txt | 32 ------- notWorking/mazeNoStart.txt | 32 ------- notWorking/mazeNoStartNoPath.txt | 32 ------- solvedMazes/0solvedmaze.txt | 10 -- solvedMazes/0solvedmaze1.txt | 10 -- 12 files changed, 91 insertions(+), 270 deletions(-) create mode 100644 .pylintrc delete mode 100644 mazes/maze.txt delete mode 100644 mazes/maze1.txt delete mode 100644 notWorking/mazeDeadEnd.txt delete mode 100644 notWorking/mazeNoEnd.txt delete mode 100644 notWorking/mazeNoEndNoStart.txt delete mode 100644 notWorking/mazeNoPath.txt delete mode 100644 notWorking/mazeNoStart.txt delete mode 100644 notWorking/mazeNoStartNoPath.txt delete mode 100644 solvedMazes/0solvedmaze.txt delete mode 100644 solvedMazes/0solvedmaze1.txt diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..4501ff29 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,3 @@ +[DESIGN] +# Maximum number of statements in function / method body +max-statements=16 \ No newline at end of file diff --git a/main.py b/main.py index 91e7fa90..a5c306c1 100644 --- a/main.py +++ b/main.py @@ -50,7 +50,7 @@ class MazeSolver: for row_i, row in enumerate(self.maze): for col_i, cell in enumerate(row): - if cell == "maze_data": + if cell == "S": start = (row_i, col_i) elif cell == "E": end = (row_i, col_i) @@ -81,26 +81,8 @@ class MazeSolver: # find path through maze - def solve(self): - """Solves the maze""" - queue = [] - # set means that values inside can not repeat - visited = set() - # https://docs.python.org/3/library/heapq.html - # 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 - heapq.heappush( - queue, (self.heuristic_manhattan(self.start), self.start, [self.start]) - ) - - # Go through queue until it'maze_data empty - # Find neighbor (which is not wall) closest to the - # END point (based on heuristic) - # Go there and repeat - # if cannot find path it starts over but skips the path that lead it to - # dead end - path = None + def solve_loop(self, queue, visited): + """ Goes through maze and finds the path """ while queue: # pop first element of heap # first value is skipped and we only save current position and path @@ -124,6 +106,27 @@ class MazeSolver: print() return path + def solve(self): + """Solves the maze""" + queue = [] + # set means that values inside can not repeat + visited = set() + # https://docs.python.org/3/library/heapq.html + # 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 + heapq.heappush( + queue, (self.heuristic_manhattan(self.start), self.start, [self.start]) + ) + + # Go through queue until it'maze_data empty + # Find neighbor (which is not wall) closest to the + # END point (based on heuristic) + # Go there and repeat + # if cannot find path it starts over but skips the path that lead it to + # dead end + return self.solve_loop(queue, visited) + # This heuristic returns the Manhattan distance between the given position # and the maze'maze_data end def heuristic_manhattan(self, position): @@ -167,23 +170,27 @@ def print_maze(maze, path=None): print(cell, end="") print() - -def save_maze(maze, path=None, saved_file="Maze", iteration=0): +def save_maze(maze, solved=True, path=None, saved_file="Maze", iteration=0): """Saves maze from array to txt file""" - if not os.path.exists("solvedMazes"): - os.mkdir("solvedMazes") - with open(f"solvedMazes/{iteration}solved{saved_file}", "w", encoding="utf8") as file: - maze_file = file.read() - if path is None: - path = [] - for row_i, row in enumerate(maze): - for col_i, cell in enumerate(row): - if (row_i, col_i) in path: - maze_file.write("*") - else: - maze_file.write(cell) - maze_file.write("\n") - + if solved: + folder_name = "solvedMazes" + else: + folder_name = "generatedMazes" + if not os.path.exists(folder_name): + os.mkdir(folder_name) + with open(f"{folder_name}/{iteration}{saved_file}", "w", encoding="utf8") as maze_file: + if path is None: + path = [] + for row_i, row in enumerate(maze): + for col_i, cell in enumerate(row): + if (row_i, col_i) in path: + maze_file.write("*") + else: + maze_file.write(cell) + if solved: + maze_file.write("\n") + if not solved: + maze_file.write("\n") def make_maze(width=16, height=8): """ generate maze with given width and height """ @@ -214,23 +221,14 @@ def make_maze(width=16, height=8): for horizontal_line, vertical_line in zip(hor, ver): maze_data += "".join(horizontal_line + ["\n"] + vertical_line + ["\n"]) maze_data_list = list(maze_data) - maze_data_list[3 * width + 3] = "maze_data" + maze_data_list[3 * width + 3] = "S" maze_data_list[len(maze_data_list) - (3 * width + 6)] = "E" maze_data = "".join(maze_data_list) return maze_data - -# Ran first in the code -if __name__ == "__main__": - start_time = time.perf_counter() - # print(sys.argv) - FILE_NAME = "maze.txt" - TEST_MODE = False - FOLDER_NAME = "" - if len(sys.argv) > 1: - FILE_NAME = sys.argv[1] - if sys.argv[1] == "-h" or sys.argv[1] == "--help": - print( +def print_help(): + """prints help""" + print( """python main.py - run the script against default maze file (any file named maze.txt in the code directory) @@ -240,36 +238,61 @@ python main.py -t --test non interactive (does not print steps) for testing different heuristics, goes through entire folder of mazes file and compares heuristic speed and path length -python main.py -g [NUMBER] - generates as many mazes as entered in +python main.py -g --generate [NUMBER] - generates as many mazes as entered in Number parameter and puts it in the mazes folder""" ) + +def test_mode(): + """ Loads and solves multiple mazes in order to compare heuristics """ + for filename in os.listdir(FOLDER_NAME): + filename_directory = os.path.join(FOLDER_NAME, filename) + print(filename_directory) + # Open and load text file to array + loaded_maze_test = load_maze(filename_directory) + # Initialize MazeSolver object with maze as parameter + solver_test = MazeSolver(loaded_maze_test, TEST_MODE) + # Find path using MazeSolver solve method + solved_path = solver_test.solve() + save_maze(loadedMaze, True, solved_path, filename, 0) + + +# Ran first in the code +if __name__ == "__main__": + start_time = time.perf_counter() + # print(sys.argv) + FILE_NAME = "maze.txt" + TEST_MODE = False + FOLDER_NAME = "" + GENERATE_AMOUNT = 0 + if len(sys.argv) > 1: + FILE_NAME = sys.argv[1] + if sys.argv[1] == "-h" or sys.argv[1] == "--help": + print_help() sys.exit() if sys.argv[1] == "-t" or sys.argv[1] == "--test": TEST_MODE = True FILE_NAME = "maze.txt" if len(sys.argv) > 2: FOLDER_NAME = sys.argv[2] + if sys.argv[1] == '-g' or sys.argv[1] == '--generate': + if len(sys.argv) > 2: + GENERATE_AMOUNT = int(sys.argv[2]) + if GENERATE_AMOUNT > 0: + for n in range(GENERATE_AMOUNT): + GENERATED_MAZE = make_maze() + save_maze(GENERATED_MAZE, False, None, f'generated{n}.txt') + sys.exit() # Open and load text file to array loadedMaze = load_maze(FILE_NAME) # Initialize MazeSolver object with maze as parameter solver = MazeSolver(loadedMaze, TEST_MODE) # Find path using MazeSolver solve method - solvedPath = solver.solve() + SOLVED_PATH = solver.solve() if not TEST_MODE: - print_maze(loadedMaze, solvedPath) + print_maze(loadedMaze, SOLVED_PATH) if TEST_MODE and FOLDER_NAME != "": - for filename in os.listdir(FOLDER_NAME): - filename_directory = os.path.join(FOLDER_NAME, filename) - print(filename_directory) - # Open and load text file to array - loadedMaze = load_maze(filename_directory) - # Initialize MazeSolver object with maze as parameter - solver = MazeSolver(loadedMaze, TEST_MODE) - # Find path using MazeSolver solve method - solvedPath = solver.solve() - save_maze(loadedMaze, solvedPath, filename, 0) - save_maze(loadedMaze, solvedPath, FILE_NAME, 0) + test_mode() + save_maze(loadedMaze, True, SOLVED_PATH, FILE_NAME, 0) end_time = time.perf_counter() execution_time = end_time - start_time - print(make_maze()) print(f"The execution time is: {execution_time}") diff --git a/mazes/maze.txt b/mazes/maze.txt deleted file mode 100644 index 4ca4b64b..00000000 --- a/mazes/maze.txt +++ /dev/null @@ -1,10 +0,0 @@ -########## -#S # -# #### # -# # # -# # ## # -# # # -##### ## -# # # -# #E # -########## diff --git a/mazes/maze1.txt b/mazes/maze1.txt deleted file mode 100644 index 4ca4b64b..00000000 --- a/mazes/maze1.txt +++ /dev/null @@ -1,10 +0,0 @@ -########## -#S # -# #### # -# # # -# # ## # -# # # -##### ## -# # # -# #E # -########## diff --git a/notWorking/mazeDeadEnd.txt b/notWorking/mazeDeadEnd.txt deleted file mode 100644 index 4fbe61e9..00000000 --- a/notWorking/mazeDeadEnd.txt +++ /dev/null @@ -1,5 +0,0 @@ -################# -# # -# ####### # -#S # E# -################# \ No newline at end of file diff --git a/notWorking/mazeNoEnd.txt b/notWorking/mazeNoEnd.txt deleted file mode 100644 index acf1cf03..00000000 --- a/notWorking/mazeNoEnd.txt +++ /dev/null @@ -1,32 +0,0 @@ -################################ -# S # # # -# ######### #### ## # # -# # # # # -# # ######### ####### # -# # # # -# # #### # ####### ####### -# # # # # # -# ####### ########### # # -# # ##### # -#### ######### # # # # - # # # #### # -#### ########### # # -# # ##### ##### -# ######### # # # -# ###### ##### -# ####### ####### # -# # # # ##### -# # ######### # # # -# # # # ###### -# ####### ######### # -# # # -# ######### ############ # -# # # # -# ######### #### ####### # -# # # # # -# # ####### # ######### # -# # # # # # -# # # #### ######### #### -# # # # # -# ######### ###### ##### # -################################ diff --git a/notWorking/mazeNoEndNoStart.txt b/notWorking/mazeNoEndNoStart.txt deleted file mode 100644 index ffdd9a08..00000000 --- a/notWorking/mazeNoEndNoStart.txt +++ /dev/null @@ -1,32 +0,0 @@ -################################ -# # # # -# ######### #### ## # # -# # # # # -# # ######### ####### # -# # # # -# # #### # ####### ####### -# # # # # # -# ####### ########### # # -# # ##### # -#### ######### # # # # - # # # #### # -#### ########### # # -# # ##### ##### -# ######### # # # -# ###### ##### -# ####### ####### # -# # # # ##### -# # ######### # # # -# # # # ###### -# ####### ######### # -# # # -# ######### ############ # -# # # # -# ######### #### ####### # -# # # # # -# # ####### # ######### # -# # # # # # -# # # #### ######### #### -# # # # # -# ######### ###### ##### # -################################ diff --git a/notWorking/mazeNoPath.txt b/notWorking/mazeNoPath.txt deleted file mode 100644 index 08c5f55e..00000000 --- a/notWorking/mazeNoPath.txt +++ /dev/null @@ -1,32 +0,0 @@ -################################ -# # # # -# ######### #### ## # # -# # # # # -# # ######### ####### # -# # # # -# # #### # ####### ####### -# # # # # # -# ####### ########### # # -# # ##### # -#### ######### # # # # - # # # #### # -#### ########### # # -# # ##### ##### -# ######### # # # -# ###### ##### -# ####### ####### # -# # # # ##### -# # ######### # # # -# # # # ###### -# ####### ######### # -# # # -# ######### ############ # -# # # # -# ######### #### ####### # -# # # # # -# # ####### # ######### # -# # # # # # -# # # #### ######### #### -# # # # # # -# ######### ###### #####E# -################################ diff --git a/notWorking/mazeNoStart.txt b/notWorking/mazeNoStart.txt deleted file mode 100644 index a4cebbd7..00000000 --- a/notWorking/mazeNoStart.txt +++ /dev/null @@ -1,32 +0,0 @@ -################################ -# # # # -# ######### #### ## # # -# # # # # -# # ######### ####### # -# # # # -# # #### # ####### ####### -# # # # # # -# ####### ########### # # -# # ##### # -#### ######### # # # # - # # # #### # -#### ########### # # -# # ##### ##### -# ######### # # # -# ###### ##### -# ####### ####### # -# # # # ##### -# # ######### # # # -# # # # ###### -# ####### ######### # -# # # -# ######### ############ # -# # # # -# ######### #### ####### # -# # # # # -# # ####### # ######### # -# # # # # # -# # # #### ######### #### -# # # # # -# ######### ###### #####E# -################################ diff --git a/notWorking/mazeNoStartNoPath.txt b/notWorking/mazeNoStartNoPath.txt deleted file mode 100644 index 3ea422ad..00000000 --- a/notWorking/mazeNoStartNoPath.txt +++ /dev/null @@ -1,32 +0,0 @@ -################################ -# S # # # -# ######### #### ## # # -# # # # # -# # ######### ####### # -# # # # -# # #### # ####### ####### -# # # # # # -# ####### ########### # # -# # ##### # -#### ######### # # # # - # # # #### # -#### ########### # # -# # ##### ##### -# ######### # # # -# ###### ##### -# ####### ####### # -# # # # ##### -# # ######### # # # -# # # # ###### -# ####### ######### # -# # # -# ######### ############ # -# # # # -# ######### #### ####### # -# # # # # -# # ####### # ######### # -# # # # # # -# # # #### ######### #### -# # # # # # -# ######### ###### #####E# -################################ diff --git a/solvedMazes/0solvedmaze.txt b/solvedMazes/0solvedmaze.txt deleted file mode 100644 index 648d7368..00000000 --- a/solvedMazes/0solvedmaze.txt +++ /dev/null @@ -1,10 +0,0 @@ -########## -#******* # -# ####* # -# # **# -# # ##*# -# #**# -##### *## -# # * # -# #* # -########## diff --git a/solvedMazes/0solvedmaze1.txt b/solvedMazes/0solvedmaze1.txt deleted file mode 100644 index 648d7368..00000000 --- a/solvedMazes/0solvedmaze1.txt +++ /dev/null @@ -1,10 +0,0 @@ -########## -#******* # -# ####* # -# # **# -# # ##*# -# #**# -##### *## -# # * # -# #* # -##########