# main.py import tkinter as tk from render import Render import time import threading from maze_generator import generate_maze import numpy as np from tkinter import messagebox from pathfinding_algorithms import bfs_generator, dfs_generator, a_star_generator class MazeSolverApp: def __init__(self, root): self.root = root self.render = Render(root, self) self.timer_running = False self.start_time = None self.elapsed_time = 0 self.timer_id = None self.maze = None self.solving = False def start_timer(self): if not self.timer_running: self.timer_running = True self.start_time = time.time() - self.elapsed_time self.update_timer() def stop_timer(self): if self.timer_running: self.timer_running = False if self.timer_id: self.root.after_cancel(self.timer_id) def reset_timer(self): self.stop_timer() self.elapsed_time = 0 self.render.update_timer_label("00:00") def update_timer(self): if self.timer_running: self.elapsed_time = time.time() - self.start_time minutes = int(self.elapsed_time // 60) seconds = int(self.elapsed_time % 60) self.render.update_timer_label(f"{minutes:02d}:{seconds:02d}") self.timer_id = self.root.after(1000, self.update_timer) def generate_maze(self): params = self.render.get_maze_parameters() if params is None: return # Invalid parameters, abort self.reset_timer() # Generate maze in a separate thread to avoid freezing the UI threading.Thread(target=self._generate_maze_thread, args=(params,)).start() def _generate_maze_thread(self, params): try: maze = generate_maze( rows=params['rows'], cols=params['cols'], generation_algorithm=params['generation_algorithm'], seed=params['seed'], wall_density=params['wall_density'] ) self.maze = maze # Update the UI in the main thread self.root.after(0, self.render.draw_maze, self.maze) self.root.after(0, self.start_timer) print("Maze generated.") except Exception as e: print(f"Error generating maze: {e}") messagebox.showerror("Error", f"An error occurred while generating the maze:\n{e}") def solve_maze(self): if not self.maze: messagebox.showwarning("No Maze", "Please generate a maze first.") return if self.solving: messagebox.showinfo("Solving", "Maze is already being solved.") return self.solving = True self.reset_timer() self.start_time = time.time() self.start_timer() self.steps = 0 # Get selected algorithm algorithm = self.render.update_algorithm_selection() # Initialize the solver generator if algorithm == "BFS": self.solver_generator = bfs_generator(self.maze, (1, 1), (self.maze.shape[0]-2, self.maze.shape[1]-2)) elif algorithm == "DFS": self.solver_generator = dfs_generator(self.maze, (1, 1), (self.maze.shape[0]-2, self.maze.shape[1]-2)) elif algorithm == "A*": self.solver_generator = a_star_generator(self.maze, (1, 1), (self.maze.shape[0]-2, self.maze.shape[1]-2)) else: messagebox.showerror("Error", f"Unknown algorithm: {algorithm}") self.solving = False self.stop_timer() return # Start the solving process self.root.after(0, self._process_solver_step) def _process_solver_step(self): try: action, cell, steps = next(self.solver_generator) self.steps = steps if action == 'visit': self.render.mark_visited(cell) elif action == 'enqueue': self.render.mark_frontier(cell) elif action == 'path': self.render.mark_path(cell) elif action == 'done': self.solving = False self.stop_timer() self.show_summary() return # Schedule the next step self.root.after(10, self._process_solver_step) # Adjust delay as needed for visualization speed except StopIteration: self.solving = False self.stop_timer() self.show_summary() def show_summary(self): elapsed_time = time.time() - self.start_time params = self.render.get_maze_parameters() algorithm = self.render.update_algorithm_selection() summary = ( f"Algorithm: {algorithm}\n" f"Time Taken: {elapsed_time:.2f} seconds\n" f"Steps: {self.steps}\n" f"Maze Size: {params['rows']}x{params['cols']}\n" f"Generation Algorithm: {params['generation_algorithm']}\n" f"Wall Density: {params['wall_density']}\n" f"Seed: {params['seed'] or 'None'}" ) messagebox.showinfo("Maze Solved!", summary) def stop_solving(self): if self.solving: # Currently, our solving algorithms are not interruptible. # Implementing a stoppable solving process would require more complex threading control. messagebox.showinfo("Stop Solving", "Stopping the solver is not implemented yet.") else: messagebox.showinfo("Not Solving", "No solving process is currently running.") # Pathfinding algorithms from collections import deque import heapq def get_adjacent_cells(cell, maze): row, col = cell directions = [(-1,0), (1,0), (0,-1), (0,1)] neighbors = [] for dr, dc in directions: r, c = row + dr, col + dc if 0 <= r < maze.shape[0] and 0 <= c < maze.shape[1] and maze[r][c] == 0: neighbors.append((r, c)) return neighbors def bfs(maze, start, end): queue = deque([start]) visited = set() visited.add(start) parent = {} while queue: current = queue.popleft() if current == end: break for neighbor in get_adjacent_cells(current, maze): if neighbor not in visited: queue.append(neighbor) visited.add(neighbor) parent[neighbor] = current # Reconstruct path path = [] if end in parent: cell = end while cell != start: path.append(cell) cell = parent[cell] path.append(start) path.reverse() return path def dfs(maze, start, end): stack = [start] visited = set() visited.add(start) parent = {} while stack: current = stack.pop() if current == end: break for neighbor in get_adjacent_cells(current, maze): if neighbor not in visited: stack.append(neighbor) visited.add(neighbor) parent[neighbor] = current # Reconstruct path path = [] if end in parent: cell = end while cell != start: path.append(cell) cell = parent[cell] path.append(start) path.reverse() return path def a_star(maze, start, end): heap = [] heapq.heappush(heap, (0, start)) came_from = {} g_score = {start: 0} f_score = {start: heuristic(start, end)} while heap: current_f, current = heapq.heappop(heap) if current == end: break for neighbor in get_adjacent_cells(current, maze): tentative_g = g_score[current] + 1 if neighbor not in g_score or tentative_g < g_score[neighbor]: came_from[neighbor] = current g_score[neighbor] = tentative_g f_score[neighbor] = tentative_g + heuristic(neighbor, end) heapq.heappush(heap, (f_score[neighbor], neighbor)) # Reconstruct path path = [] if end in came_from: cell = end while cell != start: path.append(cell) cell = came_from[cell] path.append(start) path.reverse() return path def heuristic(a, b): # Use Manhattan distance as heuristic return abs(a[0] - b[0]) + abs(a[1] - b[1]) if __name__ == "__main__": root = tk.Tk() root.title("Maze Solver") app = MazeSolverApp(root) root.mainloop()