mirror of
https://github.com/Xevion/processing-projects.git
synced 2025-12-06 09:16:02 -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