mirror of
https://github.com/Xevion/processing-projects.git
synced 2025-12-06 15:16:04 -06:00
maze gen v2 and solve approved with README setup for everything
This commit is contained in:
276
mazes/MazeGenSolve/MazeGenSolve.pyde
Normal file
276
mazes/MazeGenSolve/MazeGenSolve.pyde
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
import random, time
|
||||||
|
from timeit import default_timer as timer
|
||||||
|
|
||||||
|
class Cell:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x, self.y = x, y
|
||||||
|
self.right, self.bottom, self.visited, self.current, self.start, self.end = True, True, False, False, False, False
|
||||||
|
self.parent = None
|
||||||
|
self.marked = False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'Cell({}, {}, bottom: {}, right: {}, visited: {})'.format(self.x, self.y, self.bottom, self.right, self.visited)
|
||||||
|
|
||||||
|
def getNeighbor(self):
|
||||||
|
possible = self.neighbors()
|
||||||
|
possible = [thing for thing in possible if thing != None]
|
||||||
|
if possible:
|
||||||
|
choice = random.choice(possible)
|
||||||
|
return grid[choice[0]][choice[1]]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def pathingNeighbors(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def neighbors(self):
|
||||||
|
global offsets
|
||||||
|
neighbors = []
|
||||||
|
for offset in offsets:
|
||||||
|
neighbor = (self.x + offset[0], self.y + offset[1])
|
||||||
|
if not valid(neighbor):
|
||||||
|
neighbors.append(None)
|
||||||
|
continue
|
||||||
|
if grid[neighbor[0]][neighbor[1]].visited:
|
||||||
|
neighbors.append(None)
|
||||||
|
continue
|
||||||
|
neighbors.append(neighbor)
|
||||||
|
return neighbors
|
||||||
|
|
||||||
|
def crender(self):
|
||||||
|
global divX, divY
|
||||||
|
translate(self.x * divX, self.y * divY)
|
||||||
|
# Drawing Cell Background
|
||||||
|
# Visited, Unvisited, Highlighted
|
||||||
|
|
||||||
|
|
||||||
|
if self.start:
|
||||||
|
fill(190, 10, 10)
|
||||||
|
elif self.end:
|
||||||
|
fill(190, 10, 10)
|
||||||
|
elif self.marked:
|
||||||
|
fill(35, 155, 165)
|
||||||
|
elif self.visited:
|
||||||
|
fill(0, 42, 135)
|
||||||
|
else:
|
||||||
|
fill(0, 2, 30)
|
||||||
|
|
||||||
|
if self.current:
|
||||||
|
fill(0, 127, 196)
|
||||||
|
self.current = False
|
||||||
|
noStroke()
|
||||||
|
rect(0, 0, divX, divY)
|
||||||
|
|
||||||
|
# Drawing Cell Lines
|
||||||
|
stroke(255)
|
||||||
|
fill(255)
|
||||||
|
strokeWeight(2.5)
|
||||||
|
if not self.visited:
|
||||||
|
noStroke()
|
||||||
|
if self.bottom:
|
||||||
|
line(0, divY, divX, divY)
|
||||||
|
if self.right:
|
||||||
|
line(divX, 0, divX, divY)
|
||||||
|
resetMatrix()
|
||||||
|
|
||||||
|
class PathingCell:
|
||||||
|
def __init__(self, coords, parent=None):
|
||||||
|
self.coords = coords
|
||||||
|
self.parent = parent
|
||||||
|
self.h, self.g, self.f = 0, 0, 0
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.coords == other.coords
|
||||||
|
|
||||||
|
def openWalls(x1, y1, x2, y2):
|
||||||
|
global offsets
|
||||||
|
# Bottom, Right, Left, Top
|
||||||
|
offset = (x2 - x1, y2 - y1)
|
||||||
|
if offset == offsets[0]:
|
||||||
|
grid[x1][y1].bottom = False
|
||||||
|
if offset == offsets[1]:
|
||||||
|
grid[x1][y1].right = False
|
||||||
|
if offset == offsets[2]:
|
||||||
|
grid[x2][y2].right = False
|
||||||
|
if offset == offsets[3]:
|
||||||
|
grid[x2][y2].bottom = False
|
||||||
|
|
||||||
|
|
||||||
|
def valid(coordinate):
|
||||||
|
global columns, rows
|
||||||
|
return not (coordinate[0] < 0 or coordinate[0] >= columns or coordinate[1] < 0 or coordinate[1] >= rows)
|
||||||
|
|
||||||
|
def generate():
|
||||||
|
global complete, columns, rows, grid, divX, divY, current, offsets, start, end
|
||||||
|
# Bottom, Right, Left, Top
|
||||||
|
complete = False
|
||||||
|
offsets = [(0, 1), (1, 0), (-1, 0), (0, -1)]
|
||||||
|
columns, rows = 100, 100
|
||||||
|
divX, divY = width / float(columns), height / float(rows)
|
||||||
|
grid = [[Cell(x, y) for y in range(rows)] for x in range(columns)]
|
||||||
|
current = grid[random.randint(0, columns-1)][random.randint(0, rows-1)]
|
||||||
|
start = current
|
||||||
|
ends = [(random.randint(0, columns-1), random.randint(0, rows-1)) for _ in range(5)]
|
||||||
|
end = ends.pop(0)
|
||||||
|
prevDist = 0
|
||||||
|
for coord in ends:
|
||||||
|
dist = distance(start.x, start.y, coord[0], coord[1])
|
||||||
|
if dist > prevDist:
|
||||||
|
prevDist = dist
|
||||||
|
end = coord
|
||||||
|
end = grid[end[0]][end[1]]
|
||||||
|
end.end = True
|
||||||
|
current.visited = True
|
||||||
|
current.start = True
|
||||||
|
|
||||||
|
def setup():
|
||||||
|
size(750, 750)
|
||||||
|
# random.seed(1)
|
||||||
|
generate()
|
||||||
|
# noLoop()
|
||||||
|
|
||||||
|
def astar(grid, start, end):
|
||||||
|
start_cell = PathingCell(start, None)
|
||||||
|
end_cell = PathingCell(end, None)
|
||||||
|
|
||||||
|
open_list = [start_cell]
|
||||||
|
closed_list = []
|
||||||
|
|
||||||
|
# While the open list is not empty
|
||||||
|
while len(open_list) > 0:
|
||||||
|
# Find the best cell to loop no
|
||||||
|
current_cell, current_index = open_list[0], 0
|
||||||
|
for index, cell in enumerate(open_list):
|
||||||
|
if current_cell.f > cell.f:
|
||||||
|
current_cell = cell
|
||||||
|
current_index = index
|
||||||
|
|
||||||
|
# Remvoe the new current cell from the open list and to the closed list
|
||||||
|
closed_list.append(current_cell)
|
||||||
|
open_list.pop(current_index)
|
||||||
|
|
||||||
|
# Check if the current cell is the end cell (we've found the exit)
|
||||||
|
if current_cell == end_cell:
|
||||||
|
current = current_cell
|
||||||
|
path = []
|
||||||
|
while current is not None:
|
||||||
|
path.append(current.coords)
|
||||||
|
current = current.parent
|
||||||
|
return path[::-1]
|
||||||
|
|
||||||
|
children = []
|
||||||
|
# Bottom, Right, Left, Top
|
||||||
|
# Find neighbor cells we can move into
|
||||||
|
for index, offset in enumerate(offsets):
|
||||||
|
# Calculate the coordinate of the adjacent neighbor we're looking at
|
||||||
|
new_coord = (current_cell.coords[0] + offset[0], current_cell.coords[1] + offset[1])
|
||||||
|
# If the coordinate we're looking at is valid
|
||||||
|
if valid(new_coord):
|
||||||
|
# Create a new pathing cell
|
||||||
|
new_cell = PathingCell(new_coord, current_cell)
|
||||||
|
# is the bottom open?
|
||||||
|
if index == 0:
|
||||||
|
if not grid[current_cell.coords[0]][current_cell.coords[1]].bottom:
|
||||||
|
children.append(new_cell)
|
||||||
|
# is the right open?
|
||||||
|
elif index == 1:
|
||||||
|
if not grid[current_cell.coords[0]][current_cell.coords[1]].right:
|
||||||
|
children.append(new_cell)
|
||||||
|
# is the left side open?
|
||||||
|
elif index == 2:
|
||||||
|
if not grid[new_coord[0]][new_coord[1]].right:
|
||||||
|
children.append(new_cell)
|
||||||
|
# is the top open?
|
||||||
|
elif index == 3:
|
||||||
|
if not grid[new_coord[0]][new_coord[1]].bottom:
|
||||||
|
children.append(new_cell)
|
||||||
|
|
||||||
|
for child in children:
|
||||||
|
# is the child we looked at already closed?
|
||||||
|
skip = False
|
||||||
|
for closed_child in closed_list:
|
||||||
|
if child == closed_child:
|
||||||
|
skip = True
|
||||||
|
if skip:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# calculate algorithmic values
|
||||||
|
child.g = current_cell.g + 1
|
||||||
|
child.h = ((current_cell.coords[0] - end_cell.coords[0]) ** 2) + ((current_cell.coords[1] - end_cell.coords[1]) ** 2)
|
||||||
|
child.f = child.g + child.h
|
||||||
|
|
||||||
|
# is the cell already open? is the cell we made worst in path iterations?
|
||||||
|
for open_cell in open_list:
|
||||||
|
if child == open_cell and child.g > open_cell.g:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# open the new cell
|
||||||
|
open_list.append(child)
|
||||||
|
|
||||||
|
def randomizeOpen(chance=0.05):
|
||||||
|
global grid
|
||||||
|
for row in grid:
|
||||||
|
for cell in row:
|
||||||
|
if random.random() < chance:
|
||||||
|
if random.choice([True, False]):
|
||||||
|
cell.right = False
|
||||||
|
else:
|
||||||
|
cell.bottom = False
|
||||||
|
|
||||||
|
def mazeGenTick():
|
||||||
|
global current, next, i
|
||||||
|
for _ in range(500):
|
||||||
|
next = current.getNeighbor()
|
||||||
|
if next:
|
||||||
|
next.parent = current
|
||||||
|
openWalls(current.x, current.y, next.x, next.y)
|
||||||
|
current = next
|
||||||
|
grid[current.x][current.y].current = True
|
||||||
|
current.visited = True
|
||||||
|
else:
|
||||||
|
if current.parent != None:
|
||||||
|
current.parent
|
||||||
|
current = current.parent
|
||||||
|
grid[current.x][current.y].current = True
|
||||||
|
else:
|
||||||
|
global start
|
||||||
|
if current == start and current.getNeighbor() == None:
|
||||||
|
global complete
|
||||||
|
complete = True
|
||||||
|
print('Done')
|
||||||
|
|
||||||
|
def distance(x1, y1, x2, y2):
|
||||||
|
return sqrt(((x2 - x1) ** 2) + ((y2 - y1) ** 2))
|
||||||
|
|
||||||
|
# Render the maze itself
|
||||||
|
def render():
|
||||||
|
background(0)
|
||||||
|
for column in grid:
|
||||||
|
for cell in column:
|
||||||
|
cell.crender()
|
||||||
|
|
||||||
|
# Render the path of coordinates connecting them with ellipses and lines
|
||||||
|
def renderpath(path):
|
||||||
|
fill(48, 255, 103)
|
||||||
|
stroke(48, 255, 103)
|
||||||
|
for index, coord in enumerate(path[:-1]):
|
||||||
|
ellipse(coord[0] * divX + (divX * 0.5), coord[1] * divY + (divY * 0.5), divX / 3, divY / 3)
|
||||||
|
line(coord[0] * divX + (divX * 0.5), coord[1] * divY + (divY * 0.5), path[index + 1][0] * divX + (divX * 0.5), path[index + 1][1] * divY + (divY * 0.5))
|
||||||
|
ellipse(path[-1][0] * divX + (divX * 0.5), path[-1][1] * divY + (divY * 0.5), divX / 3, divY / 3)
|
||||||
|
|
||||||
|
def draw():
|
||||||
|
render()
|
||||||
|
global complete
|
||||||
|
if complete:
|
||||||
|
# randomizeOpen(0.15)
|
||||||
|
startTime = timer()
|
||||||
|
path = astar(grid, (start.x, start.y), (end.x, end.y))
|
||||||
|
endTime = timer()
|
||||||
|
# for coord in path:
|
||||||
|
# grid[coord[0]][coord[1]].marked = True
|
||||||
|
render()
|
||||||
|
print('Maze is {} x {} tiles with a solve path length of {} tiles.'.format(columns, rows, len(path)))
|
||||||
|
print('The solve path took about {}s to complete.'.format(round(endTime - startTime, 3)))
|
||||||
|
renderpath(path)
|
||||||
|
noLoop()
|
||||||
|
else:
|
||||||
|
mazeGenTick()
|
||||||
2
mazes/MazeGenSolve/sketch.properties
Normal file
2
mazes/MazeGenSolve/sketch.properties
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mode=Python
|
||||||
|
mode.id=jycessing.mode.PythonMode
|
||||||
146
mazes/MazeGenV2/MazeGenV2.pyde
Normal file
146
mazes/MazeGenV2/MazeGenV2.pyde
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import random, time
|
||||||
|
|
||||||
|
class Cell:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x, self.y = x, y
|
||||||
|
self.right, self.bottom, self.visited, self.current, self.start = True, True, False, False, False
|
||||||
|
def __str__(self):
|
||||||
|
return 'Cell({}, {}, bottom: {}, right: {}, visited: {})'.format(self.x, self.y, self.bottom, self.right, self.visited)
|
||||||
|
|
||||||
|
def getNeighbor(self):
|
||||||
|
possible = self.neighbors()
|
||||||
|
possible = [thing for thing in possible if thing != None]
|
||||||
|
if possible:
|
||||||
|
choice = random.choice(possible)
|
||||||
|
return grid[choice[0]][choice[1]]
|
||||||
|
return None
|
||||||
|
|
||||||
|
# def getNeighbor(self):
|
||||||
|
# possible = self.neighbors()
|
||||||
|
# if any([thing != None for thing in possible]):
|
||||||
|
# xchoice = None
|
||||||
|
# while xchoice == None:
|
||||||
|
# xchoice = random.choices(possible, weights=[2, 1, 1, 2], k=1)[0]
|
||||||
|
# return grid[xchoice[0]][xchoice[1]]
|
||||||
|
# else:
|
||||||
|
# return None
|
||||||
|
|
||||||
|
def neighbors(self):
|
||||||
|
global offsets
|
||||||
|
neighbors = []
|
||||||
|
for offset in offsets:
|
||||||
|
neighbor = (self.x + offset[0], self.y + offset[1])
|
||||||
|
if not valid(neighbor):
|
||||||
|
neighbors.append(None)
|
||||||
|
continue
|
||||||
|
if grid[neighbor[0]][neighbor[1]].visited:
|
||||||
|
neighbors.append(None)
|
||||||
|
continue
|
||||||
|
neighbors.append(neighbor)
|
||||||
|
return neighbors
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
global divX, divY
|
||||||
|
translate(self.x * divX, self.y * divY)
|
||||||
|
# Drawing Cell Background
|
||||||
|
# Visited, Unvisited, Highlighted
|
||||||
|
|
||||||
|
if self.start:
|
||||||
|
fill(28, 147, 158)
|
||||||
|
elif self.visited:
|
||||||
|
fill(0, 42, 135)
|
||||||
|
else:
|
||||||
|
fill(0, 2, 30)
|
||||||
|
|
||||||
|
if self.current:
|
||||||
|
fill(0, 127, 196)
|
||||||
|
self.current = False
|
||||||
|
noStroke()
|
||||||
|
rect(0, 0, divX, divY)
|
||||||
|
|
||||||
|
# Drawing Cell Lines
|
||||||
|
stroke(255)
|
||||||
|
fill(255)
|
||||||
|
strokeWeight(2.5)
|
||||||
|
if not self.visited:
|
||||||
|
noStroke()
|
||||||
|
if self.bottom:
|
||||||
|
line(0, divY, divX, divY)
|
||||||
|
if self.right:
|
||||||
|
line(divX, 0, divX, divY)
|
||||||
|
resetMatrix()
|
||||||
|
|
||||||
|
def openWalls(x1, y1, x2, y2):
|
||||||
|
global offsets
|
||||||
|
# Bottom, Right, Left, Top
|
||||||
|
offset = (x2 - x1, y2 - y1)
|
||||||
|
if offset == offsets[0]:
|
||||||
|
grid[x1][y1].bottom = False
|
||||||
|
if offset == offsets[1]:
|
||||||
|
grid[x1][y1].right = False
|
||||||
|
if offset == offsets[2]:
|
||||||
|
grid[x2][y2].right = False
|
||||||
|
if offset == offsets[3]:
|
||||||
|
grid[x2][y2].bottom = False
|
||||||
|
|
||||||
|
|
||||||
|
def valid(coordinate):
|
||||||
|
global columns, rows
|
||||||
|
return not (coordinate[0] < 0 or coordinate[0] >= columns or coordinate[1] < 0 or coordinate[1] >= rows)
|
||||||
|
|
||||||
|
def generate():
|
||||||
|
global complete, columns, rows, grid, divX, divY, current, offsets
|
||||||
|
# Bottom, Right, Left, Top
|
||||||
|
complete = 0
|
||||||
|
offsets = [(0, 1), (1, 0), (-1, 0), (0, -1)]
|
||||||
|
columns, rows = 50, 50
|
||||||
|
divX, divY = width / float(columns), height / float(rows)
|
||||||
|
grid = [[Cell(x, y) for y in range(rows)] for x in range(columns)]
|
||||||
|
current = grid[random.randint(0, columns-1)][random.randint(0, rows-1)]
|
||||||
|
current.visited = True
|
||||||
|
current.start = True
|
||||||
|
|
||||||
|
def setup():
|
||||||
|
size(750, 750)
|
||||||
|
frameRate(10000)
|
||||||
|
generate()
|
||||||
|
|
||||||
|
def mazeGenTick(loops=500):
|
||||||
|
global current, next, i
|
||||||
|
for _ in range(loops):
|
||||||
|
next = current.getNeighbor()
|
||||||
|
if next:
|
||||||
|
i = 0
|
||||||
|
next.parent = current
|
||||||
|
openWalls(current.x, current.y, next.x, next.y)
|
||||||
|
current = next
|
||||||
|
grid[current.x][current.y].current = True
|
||||||
|
current.visited = True
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
current.parent
|
||||||
|
current = current.parent
|
||||||
|
grid[current.x][current.y].current = True
|
||||||
|
except:
|
||||||
|
global complete
|
||||||
|
complete += 1
|
||||||
|
|
||||||
|
def render():
|
||||||
|
background(0)
|
||||||
|
for column in grid:
|
||||||
|
for cell in column:
|
||||||
|
cell.render()
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
def draw():
|
||||||
|
render()
|
||||||
|
global complete
|
||||||
|
if complete > 10:
|
||||||
|
# saveFrame("maze-###.png")
|
||||||
|
time.sleep(2.0)
|
||||||
|
generate()
|
||||||
|
if complete > 1:
|
||||||
|
mazeGenTick()
|
||||||
|
render()
|
||||||
|
else:
|
||||||
|
mazeGenTick(1)
|
||||||
2
mazes/MazeGenV2/sketch.properties
Normal file
2
mazes/MazeGenV2/sketch.properties
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mode=Python
|
||||||
|
mode.id=jycessing.mode.PythonMode
|
||||||
21
mazes/README.md
Normal file
21
mazes/README.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# mazes
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Projects that generate mazes using various algorithms. Developed primarily in 2018-2019. All of these are generating simple square mazes, just using different algorithms and occaisionally a little twist.
|
||||||
|
|
||||||
|
## Sketches
|
||||||
|
|
||||||
|
- **MazeGenV2** Second maze generation attempt. First *working* maze implementation. Recursive Backtracker algorithm.
|
||||||
|
|
||||||
|
- **MazeGenSolve** - Maze gen with a completed path shown after. Recursive Backtracker algorithm for Generation, A* algorithm for pathfinding (very similar in theory).
|
||||||
|
|
||||||
|
- **MazeGenBinaryTree** - Binary Tree algorithm
|
||||||
|
|
||||||
|
- **MazeGenGrowingTree** - Growing Tree algorithm
|
||||||
|
|
||||||
|
- **MazeGenKruskalTree** - Kruskal Tree algorithm
|
||||||
|
|
||||||
|
- **MazeGenSidewinder** - Sidewinder algorithm. A little bit buggy at the end, but properly implemented none the less.
|
||||||
|
|
||||||
|
- **MazeClustersCreator** - Not so much a maze as a interesting side-project when developing my maze generators. Somewhat buggy.
|
||||||
Reference in New Issue
Block a user