Files
maze_solver/main.py.bak
klein panic aeaa436c25 first commit
2024-10-23 18:44:54 -04:00

263 lines
8.4 KiB
Python

# 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()