first commit
This commit is contained in:
273
render.py
Normal file
273
render.py
Normal file
@@ -0,0 +1,273 @@
|
||||
# render.py
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from tkinter import messagebox
|
||||
|
||||
class Render:
|
||||
def __init__(self, root, app):
|
||||
self.root = root
|
||||
self.app = app
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
# Configure root window's grid
|
||||
self.root.columnconfigure(0, weight=1)
|
||||
self.root.columnconfigure(1, weight=4)
|
||||
self.root.rowconfigure(0, weight=1)
|
||||
|
||||
# Left sidebar frame
|
||||
self.sidebar = ttk.Frame(self.root, padding="10")
|
||||
self.sidebar.grid(row=0, column=0, sticky="NSWE")
|
||||
|
||||
# Main canvas frame
|
||||
self.main_frame = ttk.Frame(self.root, padding="10")
|
||||
self.main_frame.grid(row=0, column=1, sticky="NSWE")
|
||||
|
||||
# Canvas for maze visualization
|
||||
self.canvas = tk.Canvas(self.main_frame, width=600, height=600, bg='white')
|
||||
self.canvas.pack(fill="both", expand=True)
|
||||
|
||||
# Populate sidebar with controls
|
||||
self.create_sidebar_controls()
|
||||
|
||||
def create_sidebar_controls(self):
|
||||
# Maze Parameters Label
|
||||
params_label = ttk.Label(self.sidebar, text="Maze Parameters", font=("Helvetica", 12, "bold"))
|
||||
params_label.pack(pady=(0, 10))
|
||||
|
||||
# Rows Entry
|
||||
ttk.Label(self.sidebar, text="Rows:").pack(anchor='w')
|
||||
self.rows_entry = ttk.Entry(self.sidebar)
|
||||
self.rows_entry.insert(0, "31") # Default value
|
||||
self.rows_entry.pack(fill='x', pady=5)
|
||||
|
||||
# Columns Entry
|
||||
ttk.Label(self.sidebar, text="Columns:").pack(anchor='w')
|
||||
self.cols_entry = ttk.Entry(self.sidebar)
|
||||
self.cols_entry.insert(0, "31") # Default value
|
||||
self.cols_entry.pack(fill='x', pady=5)
|
||||
|
||||
# Dead Ends Scale
|
||||
ttk.Label(self.sidebar, text="Dead Ends:").pack(anchor='w')
|
||||
self.dead_ends_scale = ttk.Scale(self.sidebar, from_=1, to=10, orient='horizontal')
|
||||
self.dead_ends_scale.set(10) # Default value
|
||||
self.dead_ends_scale.pack(fill='x', pady=5)
|
||||
|
||||
# Branching Factor Scale
|
||||
ttk.Label(self.sidebar, text="Branching Factor:").pack(anchor='w')
|
||||
self.branching_factor_scale = ttk.Scale(self.sidebar, from_=1, to=10, orient='horizontal') # Adjust max as needed
|
||||
self.branching_factor_scale.set(3) # Default value
|
||||
self.branching_factor_scale.pack(fill='x', pady=5)
|
||||
|
||||
# Maze Generation Algorithm Dropdown
|
||||
ttk.Label(self.sidebar, text="Generation Algorithm:").pack(anchor='w', pady=(10, 0))
|
||||
self.gen_algorithm_var = tk.StringVar()
|
||||
self.gen_algorithm_combobox = ttk.Combobox(self.sidebar, textvariable=self.gen_algorithm_var, state="readonly")
|
||||
self.gen_algorithm_combobox['values'] = ("Recursive Backtracker", "Prim's")
|
||||
self.gen_algorithm_combobox.current(0)
|
||||
self.gen_algorithm_combobox.pack(fill='x', pady=5)
|
||||
|
||||
# Seed Entry
|
||||
ttk.Label(self.sidebar, text="Seed (optional):").pack(anchor='w', pady=(10, 0))
|
||||
self.seed_entry = ttk.Entry(self.sidebar)
|
||||
self.seed_entry.pack(fill='x', pady=5)
|
||||
|
||||
# Wall Density Scale
|
||||
ttk.Label(self.sidebar, text="Wall Density:").pack(anchor='w', pady=(10, 0))
|
||||
self.density_scale = ttk.Scale(self.sidebar, from_=0, to=1, orient='horizontal')
|
||||
self.density_scale.set(0.3) # Default value
|
||||
self.density_scale.pack(fill='x', pady=5)
|
||||
|
||||
# Generate Maze Button
|
||||
self.generate_button = ttk.Button(self.sidebar, text="Generate Maze", command=self.app.generate_maze)
|
||||
self.generate_button.pack(fill='x', pady=(10, 10))
|
||||
|
||||
# Algorithm Selection Dropdown for Solving
|
||||
ttk.Label(self.sidebar, text="Select Algorithm:").pack(anchor='w')
|
||||
self.algorithm_var = tk.StringVar()
|
||||
self.algorithm_combobox = ttk.Combobox(self.sidebar, textvariable=self.algorithm_var, state="readonly")
|
||||
self.algorithm_combobox['values'] = ("BFS", "DFS", "A*")
|
||||
self.algorithm_combobox.current(0)
|
||||
self.algorithm_combobox.pack(fill='x', pady=5)
|
||||
|
||||
# Start Button
|
||||
self.start_button = ttk.Button(self.sidebar, text="Start", command=self.app.solve_maze)
|
||||
self.start_button.pack(fill='x', pady=(20, 5))
|
||||
|
||||
# Stop Button
|
||||
self.stop_button = ttk.Button(self.sidebar, text="Stop", command=self.app.stop_solving)
|
||||
self.stop_button.pack(fill='x', pady=5)
|
||||
|
||||
# Timer Label
|
||||
self.timer_label = ttk.Label(self.sidebar, text="00:00", font=("Helvetica", 14))
|
||||
self.timer_label.pack(pady=(20, 0))
|
||||
|
||||
def update_timer_label(self, time_str):
|
||||
self.timer_label.config(text=time_str)
|
||||
|
||||
def get_maze_parameters(self):
|
||||
try:
|
||||
rows = int(self.rows_entry.get())
|
||||
cols = int(self.cols_entry.get())
|
||||
dead_ends = int(self.dead_ends_scale.get())
|
||||
branching_factor = int(self.branching_factor_scale.get())
|
||||
generation_algorithm = self.gen_algorithm_var.get()
|
||||
seed_input = self.seed_entry.get()
|
||||
if seed_input == "":
|
||||
seed = None
|
||||
else:
|
||||
seed = int(seed_input)
|
||||
wall_density = float(self.density_scale.get())
|
||||
|
||||
# Ensure rows and cols are odd numbers
|
||||
if rows % 2 == 0:
|
||||
rows += 1
|
||||
if cols % 2 == 0:
|
||||
cols += 1
|
||||
|
||||
# Store original values to check for changes
|
||||
original_rows = rows
|
||||
original_cols = cols
|
||||
|
||||
# Strict 1:1 or 1:2/2:1 ratio enforcement
|
||||
if abs(rows / cols - 1) < abs((rows / (cols * 2)) - 1) and abs(rows / cols - 1) < abs(((rows * 2) / cols) - 1):
|
||||
# Enforce 1:1 ratio
|
||||
cols = rows
|
||||
elif abs((rows / (cols * 2)) - 1) < abs(((rows * 2) / cols) - 1):
|
||||
# Enforce 1:2 ratio (rows:cols)
|
||||
cols = max(1, rows * 2)
|
||||
else:
|
||||
# Enforce 2:1 ratio (rows:cols)
|
||||
rows = max(1, cols * 2)
|
||||
|
||||
# Update the row and column input fields only if changes were made
|
||||
if rows != original_rows or cols != original_cols:
|
||||
self.rows_entry.delete(0, tk.END)
|
||||
self.rows_entry.insert(0, str(rows))
|
||||
self.cols_entry.delete(0, tk.END)
|
||||
self.cols_entry.insert(0, str(cols))
|
||||
|
||||
# Display message only when adjustments were made
|
||||
messagebox.showinfo("Adjusted Dimensions",
|
||||
f"The row-to-column ratio was adjusted to match the closest ratio (1:1, 1:2, or 2:1).\n\n"
|
||||
f"New Dimensions: {rows} rows, {cols} columns.")
|
||||
|
||||
return {
|
||||
'rows': rows,
|
||||
'cols': cols,
|
||||
'dead_ends': dead_ends,
|
||||
'branching_factor': branching_factor,
|
||||
'generation_algorithm': generation_algorithm,
|
||||
'seed': seed,
|
||||
'wall_density': wall_density
|
||||
}
|
||||
except ValueError:
|
||||
messagebox.showerror("Invalid Input", "Please enter valid values for all parameters.\n\n"
|
||||
"Ensure that Rows and Columns are integers.\n"
|
||||
"Seed (if provided) should be an integer.")
|
||||
return None
|
||||
|
||||
def update_algorithm_selection(self):
|
||||
return self.algorithm_var.get()
|
||||
|
||||
def draw_maze(self, maze):
|
||||
self.canvas.delete("all")
|
||||
rows, cols = maze.shape
|
||||
|
||||
# Calculate cell size and allow small margins for separation
|
||||
cell_width = self.canvas.winfo_width() / cols
|
||||
cell_height = self.canvas.winfo_height() / rows
|
||||
|
||||
for r in range(rows):
|
||||
for c in range(cols):
|
||||
x0 = c * cell_width
|
||||
y0 = r * cell_height
|
||||
x1 = x0 + cell_width - 1 # Slightly reduce size to restore previous look
|
||||
y1 = y0 + cell_height - 1 # Slightly reduce size to restore previous look
|
||||
if maze[r][c] == 1:
|
||||
self.canvas.create_rectangle(x0, y0, x1, y1, fill="black", outline="")
|
||||
else:
|
||||
self.canvas.create_rectangle(x0, y0, x1, y1, fill="white", outline="")
|
||||
|
||||
# Mark start and end points
|
||||
self.canvas.create_rectangle(1 * cell_width, 1 * cell_height,
|
||||
2 * cell_width, 2 * cell_height, fill="green", outline="")
|
||||
self.canvas.create_rectangle((cols - 2) * cell_width, (rows - 2) * cell_height,
|
||||
(cols - 1) * cell_width, (rows - 1) * cell_height, fill="red", outline="")
|
||||
|
||||
def highlight_cell(self, cell, color):
|
||||
rows, cols = self.app.maze.shape
|
||||
cell_width = self.canvas.winfo_width() / cols
|
||||
cell_height = self.canvas.winfo_height() / rows
|
||||
r, c = cell
|
||||
|
||||
# Skip start and end
|
||||
if (r, c) == (1, 1) or (r, c) == (rows - 2, cols - 2):
|
||||
return
|
||||
|
||||
x0 = c * cell_width
|
||||
y0 = r * cell_height
|
||||
x1 = x0 + cell_width
|
||||
y1 = y0 + cell_height
|
||||
|
||||
# Overlay the cell with the specified color
|
||||
self.canvas.create_rectangle(x0, y0, x1, y1, fill=color, outline='')
|
||||
|
||||
def draw_path(self, path):
|
||||
if not path:
|
||||
return
|
||||
rows, cols = self.app.maze.shape
|
||||
cell_width = self.canvas.winfo_width() / cols
|
||||
cell_height = self.canvas.winfo_height() / rows
|
||||
|
||||
for cell in path:
|
||||
r, c = cell
|
||||
x0 = c * cell_width
|
||||
y0 = r * cell_height
|
||||
x1 = x0 + cell_width
|
||||
y1 = y0 + cell_height
|
||||
# Avoid overwriting start and end
|
||||
if cell != (1, 1) and cell != (rows - 2, cols - 2):
|
||||
self.canvas.create_rectangle(x0, y0, x1, y1, fill="blue", outline="")
|
||||
|
||||
# Re-mark start and end to ensure their colors remain
|
||||
self.canvas.create_rectangle(1 * cell_width, 1 * cell_height,
|
||||
2 * cell_width, 2 * cell_height, fill="green", outline="")
|
||||
self.canvas.create_rectangle((cols - 2) * cell_width, (rows - 2) * cell_height,
|
||||
(cols - 1) * cell_width, (rows - 1) * cell_height, fill="red", outline="")
|
||||
|
||||
|
||||
def mark_visited(self, cell):
|
||||
rows, cols = self.app.maze.shape
|
||||
cell_width = self.canvas.winfo_width() / cols
|
||||
cell_height = self.canvas.winfo_height() / rows
|
||||
r, c = cell
|
||||
x0 = c * cell_width
|
||||
y0 = r * cell_height
|
||||
x1 = x0 + cell_width
|
||||
y1 = y0 + cell_height
|
||||
self.canvas.create_rectangle(x0, y0, x1, y1, fill="lightblue", outline="")
|
||||
|
||||
def mark_frontier(self, cell):
|
||||
rows, cols = self.app.maze.shape
|
||||
cell_width = self.canvas.winfo_width() / cols
|
||||
cell_height = self.canvas.winfo_height() / rows
|
||||
r, c = cell
|
||||
x0 = c * cell_width
|
||||
y0 = r * cell_height
|
||||
x1 = x0 + cell_width
|
||||
y1 = y0 + cell_height
|
||||
self.canvas.create_rectangle(x0, y0, x1, y1, fill="orange", outline="")
|
||||
|
||||
def mark_path(self, cell):
|
||||
rows, cols = self.app.maze.shape
|
||||
cell_width = self.canvas.winfo_width() / cols
|
||||
cell_height = self.canvas.winfo_height() / rows
|
||||
r, c = cell
|
||||
x0 = c * cell_width
|
||||
y0 = r * cell_height
|
||||
x1 = x0 + cell_width
|
||||
y1 = y0 + cell_height
|
||||
self.canvas.create_rectangle(x0, y0, x1, y1, fill="blue", outline="")
|
||||
|
||||
Reference in New Issue
Block a user