def create(self, row_count, col_count, algorithm): """Creates a maze for a given row and column count.""" if (row_count or col_count) <= 0: raise utils.MazeError( "Row or column count cannot be smaller than zero.") self.maze = np.zeros((2 * row_count + 1, 2 * col_count + 1, 3), dtype=np.uint8) if algorithm == Maze.Create.C: return self._recursive_backtracking_c() if algorithm == Maze.Create.BACKTRACKING: return self._recursive_backtracking() if algorithm == Maze.Create.HUNT: return self._hunt_and_kill() if algorithm == Maze.Create.ELLER: return self._eller() if algorithm == Maze.Create.SIDEWINDER: return self._sidewinder() if algorithm == Maze.Create.PRIM: return self._prim() if algorithm == Maze.Create.KRUSKAL: return self._kruskal() raise utils.MazeError( "Wrong algorithm <{}>.\n" "Use \"Maze.Create.<algorithm>\" to choose an algorithm.".format( algorithm))
def solve(self, start, end, algorithm): """Solves a maze from start to finish.""" if self.maze is None: raise utils.MazeError( "Maze is not assigned.\n" "Use the \"create\" or \"load_maze\" method to create or load a maze." ) start = start if start else (0, 0) end = end if end else (self.row_count - 1, self.col_count - 1) if not (0 <= start[0] < self.row_count and 0 <= start[1] < self.col_count): raise utils.MazeError("Start point <{}> is out of range.".format(start)) if not (0 <= end[0] < self.row_count and 0 <= end[1] < self.col_count): raise utils.MazeError("End point <{}> is out of range.".format(end)) start = tuple([x for x in start]) end = tuple([x for x in end]) self.solution = self.maze.copy() if algorithm == Maze.Solve.C: return self._depth_first_search_c(start, end) if algorithm == Maze.Solve.DEPTH: return self._depth_first_search(start, end) if algorithm == Maze.Solve.BREADTH: return self._breadth_first_search(start, end) raise utils.MazeError( "Wrong algorithm <{}>.\n" "Use \"Algorithm.Solve.<algorithm>\" to choose an algorithm.".format(algorithm) )
def create(self, row_count, col_count, algorithm): """Creates a maze for a given row and column count.""" if (row_count or col_count) <= 0: raise utils.MazeError("Row or column count cannot be smaller than zero.") self.maze = np.zeros((row_count, col_count , 3), dtype=np.uint8) if algorithm == Maze.Create.KRUSKAL: return self._kruskal() raise utils.MazeError( "Wrong algorithm <{}>.\n" "Use \"Maze.Create.<algorithm>\" to choose an algorithm.".format(algorithm) )
def _breadth_first_search(self, start, end): """ Solves a maze using breadth-first search. :param start: tuple with start coordinates :param end: tuple with end coordinates :return: None """ visited = self.maze.copy( ) # List of visited cells, value of visited cell is 0 queue = collections.deque() # List of cells [cell, ...] cell = utils.stack_empty( ) # Tuple of current cell with according stack ((x, y), stack) x, y = start cell = utils.stack_push(cell, (x, y)) queue.append(cell) visited[x, y, 0] = 0 # Mark as visited while queue: self._enqueue(queue, visited) if queue[0][0] == end: # Stop if end has been found cell = utils.stack_push(queue[0], end) # Push end into cell return utils.draw_path(self.solution, utils.stack_deque(cell)) raise utils.MazeError("No solution found.")
def load_maze(self, file_name="maze.png"): """Loads the maze from png.""" if not os.path.isfile(file_name): raise util.MazeError( "Cannot load maze because <{}> does not exist.".format( file_name)) self.maze = util.downscale(np.array(Image.open(file_name)))
def save_solution(self, file_name="solution.png", scale=3): """Saves the solution as png.""" if self.solution is None: raise util.MazeError( "Cannot save solution because it is not assigned.\n" "Use the \"solve\" method to solve a maze." ) Image.fromarray(util.upscale(self.solution, scale), "RGB").save(file_name, "png")
def save_maze(self, file_name="maze.png", scale=3): """Saves the maze as png.""" if self.maze is None: raise util.MazeError( "Cannot save maze because it is not assigned.\n" "Use the \"create\" or \"load_maze\" method to create or load a maze." ) Image.fromarray(util.upscale(self.maze, scale), "RGB").save(file_name, "png")
def col_count_with_walls(self): """ Returns column count with walls. :return: int """ try: return self.maze.shape[1] except Exception: raise util.MazeError( "Unable to get col count. Maze and solution are not assigned.")
def load_maze(self, file_name="maze.png"): """ Loads maze from png. :param file_name: file name of file to load :return: None """ if not os.path.isfile(file_name): raise util.MazeError( "Cannot load maze because <{}> does not exist.".format( file_name)) self.maze = util.downscale(np.array(Image.open(file_name)))
def _depth_first_search(self, start, end): """Solves a maze using depth-first search.""" visited = self.maze.copy() # List of visited cells, value of visited cell is 0 stack = collections.deque() # List of visited cells [(x, y), ...] x, y = start visited[x, y, 0] = 0 # Mark as visited while x and y: while x and y: stack.append((x, y)) if (x, y) == end: # Stop if end has been found return utils.draw_path(self.solution, stack) x, y = self._solve_walk(x, y, visited) x, y = self._solve_backtrack(stack, visited) raise utils.MazeError("No solution found.")
def _load_dll(self): """ Loads C dll and sets parameter types. :return: None """ pth = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lib", "maze32.dll") self._dll = ctypes.cdll.LoadLibrary(pth) try: ndpointer = np.ctypeslib.ndpointer(ctypes.c_uint8, flags="C_CONTIGUOUS") except Exception: raise util.MazeError( "Failed loading maze32.dll from <{}>".format(pth)) self._dll.recursive_backtracking.argtypes = [ ndpointer, ctypes.c_int, ctypes.c_int, ctypes.c_int ] self._dll.depth_first_search.argtypes = [ ndpointer, ndpointer, ctypes.c_int, ctypes.c_int, ctypes.c_int ]