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

157 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import numpy as np
import random
from collections import deque
def generate_maze(rows, cols, generation_algorithm="Recursive Backtracker", seed=None, wall_density=0.3, difficulty=5):
if seed is not None:
random.seed(seed)
np.random.seed(seed)
else:
seed = random.randint(0, 999999) # Generate a random seed if not provided
# Adjust wall density and complexity based on difficulty
adjusted_wall_density = wall_density + (difficulty * 0.08) # More severe increase in wall density
adjusted_wall_density = min(adjusted_wall_density, 0.7) # Cap wall density for solvability
maze = None
is_solved = False
# Use a loop to regenerate maze if unsolvable
while not is_solved:
if generation_algorithm == "Recursive Backtracker":
maze = recursive_backtracker_maze(rows, cols, adjusted_wall_density, difficulty)
elif generation_algorithm == "Prim's":
maze = prim_maze(rows, cols, adjusted_wall_density, difficulty)
else:
raise ValueError(f"Unknown generation algorithm: {generation_algorithm}")
# Check if the generated maze is solvable
is_solved = is_solvable(maze, (1, 1), (rows - 2, cols - 2))
return maze, seed # Return the generated maze along with the seed
def recursive_backtracker_maze(rows, cols, wall_density, difficulty):
maze = np.ones((rows, cols), dtype=int)
# Initialize the stack with the starting point
stack = [(1, 1)]
maze[1][1] = 0 # Start point
# Track the main path
main_path = []
# Iterative backtracker logic
while stack:
r, c = stack[-1] # Get the current cell from the stack
directions = [(2, 0), (-2, 0), (0, 2), (0, -2)]
random.shuffle(directions)
carved = False
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 < nr < rows-1 and 0 < nc < cols-1 and maze[nr][nc] == 1:
maze[nr - dr//2][nc - dc//2] = 0 # Remove wall between
maze[nr][nc] = 0 # Mark the next cell as a passage
stack.append((nr, nc)) # Add the next cell to the stack
carved = True
main_path.append((nr, nc)) # Track the main path
break # Stop after carving one passage
if not carved:
stack.pop() # Backtrack if no carving was possible
# Add complexity based on difficulty - Branch off from the main path
branch_factor = difficulty * 2 # Scale with difficulty (higher means more branches)
for _ in range(branch_factor): # Use `_` to indicate we don't need the index
if len(main_path) > 2: # Only branch if theres space
r, c = random.choice(main_path)
directions = [(2, 0), (-2, 0), (0, 2), (0, -2)]
random.shuffle(directions)
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 < nr < rows-1 and 0 < nc < cols-1 and maze[nr][nc] == 1:
maze[nr - dr//2][nc - dc//2] = 0 # Remove wall between
maze[nr][nc] = 0 # Create false branch
break # Stop after adding one branch
# Add random walls based on wall_density (avoid adding walls along the main path)
for r in range(1, rows-1):
for c in range(1, cols-1):
if maze[r][c] == 0 and random.random() < wall_density * 0.05:
maze[r][c] = 1 # Add wall
return maze
def prim_maze(rows, cols, wall_density, difficulty):
maze = np.ones((rows, cols), dtype=int)
start_r, start_c = 1, 1
maze[start_r][start_c] = 0
walls = []
main_path = [(start_r, start_c)] # Track the main path
def add_walls(r, c):
directions = [(-1,0), (1,0), (0,-1), (0,1)]
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 < nr < rows-1 and 0 < nc < cols-1 and maze[nr][nc] == 1:
walls.append((nr, nc, r, c))
add_walls(start_r, start_c)
while walls:
idx = random.randint(0, len(walls)-1)
wall = walls.pop(idx)
wr, wc, pr, pc = wall
opposite_r, opposite_c = wr + (wr - pr), wc + (wc - pc)
if 0 < opposite_r < rows-1 and 0 < opposite_c < cols-1:
if maze[opposite_r][opposite_c] == 1:
maze[wr][wc] = 0
maze[opposite_r][opposite_c] = 0
main_path.append((opposite_r, opposite_c)) # Track main path
add_walls(opposite_r, opposite_c)
# Add complexity based on difficulty - More branches from the main path
branch_factor = difficulty * 2 # More branches with higher difficulty
for _ in range(branch_factor): # Use `_` to indicate we don't need the index
if len(main_path) > 2:
r, c = random.choice(main_path)
directions = [(-1,0), (1,0), (0,-1), (0,1)]
random.shuffle(directions)
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 < nr < rows-1 and 0 < nc < cols-1 and maze[nr][nc] == 1:
maze[nr][nc] = 0 # Create a false branch
break
# Add random walls based on wall_density
for r in range(1, rows-1):
for c in range(1, cols-1):
if maze[r][c] == 0 and random.random() < wall_density * 0.05:
maze[r][c] = 1 # Add wall
return maze
def is_solvable(maze, start, end):
queue = deque([start])
visited = set([start])
while queue:
current = queue.popleft()
if current == end:
return True
for neighbor in get_adjacent_cells(current, maze):
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return False
# Helper to get adjacent cells
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