mirror of
https://github.com/Xevion/processing-projects.git
synced 2025-12-06 17:16:04 -06:00
kruskal and sidewinder maze generation sketches
This commit is contained in:
224
mazes/MazeGenKruskal/MazeGenKruskal.pyde
Normal file
224
mazes/MazeGenKruskal/MazeGenKruskal.pyde
Normal file
@@ -0,0 +1,224 @@
|
||||
import random, time, string
|
||||
|
||||
class Cell:
|
||||
def __init__(self, x, y):
|
||||
self.x, self.y = x, y
|
||||
self.identity = None
|
||||
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 neighbors(self, identityCheck=True):
|
||||
global offsets
|
||||
neighbors = []
|
||||
for offset in offsets:
|
||||
neighbor = (self.x + offset[0], self.y + offset[1])
|
||||
# If the neighbor isn't real
|
||||
if not valid(neighbor):
|
||||
continue
|
||||
# If the neighbor hasn't been claimed
|
||||
elif not grid[neighbor[0]][neighbor[1]].visited:
|
||||
neighbors.append(neighbor)
|
||||
continue
|
||||
# We're checking for their identity
|
||||
elif identityCheck:
|
||||
# If the neighbor isn't like me
|
||||
if grid[neighbor[0]][neighbor[1]].identity != self.identity:
|
||||
neighbors.append(neighbor)
|
||||
continue
|
||||
return neighbors
|
||||
|
||||
def render(self):
|
||||
global divX, divY, showNumbers
|
||||
translate(self.x * divX, self.y * divY)
|
||||
# Drawing Cell Background
|
||||
# Visited, Unvisited, Highlighted
|
||||
if 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+1, divY+1)
|
||||
|
||||
# Drawing Cell Lines
|
||||
stroke(255)
|
||||
fill(255)
|
||||
strokeWeight(2.5)
|
||||
if showNumbers:
|
||||
textSize(50)
|
||||
textAlign(CENTER, CENTER)
|
||||
text(symbolize(self.identity), divX / 2.0, divY / 2.0)
|
||||
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, grid
|
||||
# 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 symbolize(n):
|
||||
stri = ''
|
||||
while n > 0:
|
||||
curLetter = (n - 1) % 26
|
||||
stri += string.ascii_uppercase[curLetter]
|
||||
n = (n - (curLetter + 1)) / 26
|
||||
return stri
|
||||
|
||||
|
||||
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 columns, rows, grid, divX, divY, offsets
|
||||
# Bottom, Right, Left, Top
|
||||
offsets = [(0, 1), (1, 0), (-1, 0), (0, -1)]
|
||||
columns, rows = 10, 10
|
||||
divX, divY = width / float(columns), height / float(rows)
|
||||
grid = [[Cell(x, y) for y in range(rows)] for x in range(columns)]
|
||||
|
||||
global treeSet, i, nodesAdded, showNumbers
|
||||
try:
|
||||
showNumbers
|
||||
except:
|
||||
showNumbers = False
|
||||
i = 0
|
||||
nodesAdded = 0
|
||||
treeSet = []
|
||||
|
||||
# Tree Class. But it's really not a tree.
|
||||
class Tree:
|
||||
def __init__(self, identity):
|
||||
self.tree = []
|
||||
self.identifier = identity
|
||||
|
||||
def append(self, node):
|
||||
self.tree.append(node)
|
||||
grid[node[0]][node[1]].identity = self.identifier
|
||||
grid[node[0]][node[1]].visited = True
|
||||
|
||||
# Merge another tree with this tree
|
||||
def merge(self, other):
|
||||
for node in other.tree:
|
||||
grid[node[0]][node[1]].identity = self.identifier
|
||||
self.tree.append(node)
|
||||
|
||||
# Find a node in the list with an edge
|
||||
def getEdge(self, debug=False):
|
||||
random.shuffle(self.tree)
|
||||
# For every node in the tree
|
||||
for index, node in enumerate(self.tree):
|
||||
# if the node has any neighbors that aren't like us, return it
|
||||
if len(grid[node[0]][node[1]].neighbors()) > 0:
|
||||
return node
|
||||
print('Couldn\'t find an edge')
|
||||
|
||||
def setup():
|
||||
size(1000, 1000)
|
||||
noLoop()
|
||||
generate()
|
||||
frameRate(10)
|
||||
|
||||
def checkMaze():
|
||||
for row in grid:
|
||||
for cell in row:
|
||||
if not cell.visited:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Kruskall's algorithm
|
||||
def tick():
|
||||
# choose a node, find it's tree
|
||||
# if it has no tree, create a tree with a unvisited neighbor and open a wall between them
|
||||
# if it's in a tree
|
||||
# get the tree to return a node on the edge (has a neighbor that isn't in it's own tree)
|
||||
# merge the two trees together (the neighboring node's tree is added to the edge node's tree)
|
||||
# open the wall between the two nodes and the two trees have been merged properly
|
||||
# continue until no edges can be found in any tree (how to implement this without guesswork or expensive iterating?)
|
||||
|
||||
global treeSet, i, nodesAdded
|
||||
# choose a random cell to work on, whether it's in a 'tree' or is unvisited
|
||||
choice = random.randint(0, columns-1), random.randint(0, rows-1)
|
||||
|
||||
if nodesAdded == columns * rows:
|
||||
print('Maze completed ({} cells)'.format(nodesAdded))
|
||||
noLoop()
|
||||
return True
|
||||
|
||||
# If the node is unclaimed
|
||||
if grid[choice[0]][choice[1]].identity == None:
|
||||
# Find it's unclaimed neighbors (if any)
|
||||
neighbors = grid[choice[0]][choice[1]].neighbors()
|
||||
neighbors = [neighbor for neighbor in neighbors if grid[neighbor[0]][neighbor[1]].identity == None]
|
||||
# We got a unclaimed lone node, just skip it for now (crap implementation, but needless work)
|
||||
if len(neighbors) == 0:
|
||||
return
|
||||
else:
|
||||
tree = Tree(i)
|
||||
i += 1
|
||||
nodesAdded += 2
|
||||
neighbor = random.choice(neighbors)
|
||||
openWalls(choice[0], choice[1], neighbor[0], neighbor[1])
|
||||
tree.append(choice)
|
||||
tree.append(neighbor)
|
||||
treeSet.append(tree)
|
||||
else:
|
||||
# get the identifier of our choice
|
||||
choiceIdentity = grid[choice[0]][choice[1]].identity
|
||||
# find an edge of the treeset and change our choice cell to that edge
|
||||
choice = treeSet[choiceIdentity].getEdge()
|
||||
neighbors = grid[choice[0]][choice[1]].neighbors()
|
||||
# get the neighbors of it that are different
|
||||
neighbor = random.choice(neighbors)
|
||||
# If the neighbor to the chosen cell's treeSet's edge cell is in another treeSet
|
||||
if grid[neighbor[0]][neighbor[1]].identity != None:
|
||||
get = treeSet[grid[neighbor[0]][neighbor[1]].identity]
|
||||
treeSet[grid[neighbor[0]][neighbor[1]].identity] = None
|
||||
treeSet[choiceIdentity].merge(get)
|
||||
# If it's just an unclaimed cell
|
||||
else:
|
||||
nodesAdded += 1
|
||||
treeSet[choiceIdentity].append(neighbor)
|
||||
openWalls(choice[0], choice[1], neighbor[0], neighbor[1])
|
||||
|
||||
# Render the maze
|
||||
def render():
|
||||
background(0)
|
||||
for column in grid:
|
||||
for cell in column:
|
||||
cell.render()
|
||||
|
||||
def draw():
|
||||
for _ in range(columns):
|
||||
# if the maze is complete, it'll return True instead of None, if so, break the loop
|
||||
if tick():
|
||||
break
|
||||
render()
|
||||
|
||||
# Switch off the number display
|
||||
def keyPressed():
|
||||
global showNumbers
|
||||
showNumbers = not showNumbers
|
||||
redraw()
|
||||
|
||||
# Regenerate the maze
|
||||
def mouseClicked():
|
||||
loop()
|
||||
generate()
|
||||
2
mazes/MazeGenKruskal/sketch.properties
Normal file
2
mazes/MazeGenKruskal/sketch.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
mode=Python
|
||||
mode.id=jycessing.mode.PythonMode
|
||||
159
mazes/MazeGenSidewinder/MazeGenSidewinder.pyde
Normal file
159
mazes/MazeGenSidewinder/MazeGenSidewinder.pyde
Normal file
@@ -0,0 +1,159 @@
|
||||
import random
|
||||
|
||||
class Cell:
|
||||
def __init__(self, x, y):
|
||||
self.x, self.y = x, y
|
||||
self.right, self.bottom, self.visited, self.live = True, True, False, False
|
||||
|
||||
# Identify the neighbors of the cell
|
||||
def neighbors(self):
|
||||
global offsets
|
||||
neighbors = []
|
||||
for offset in offsets:
|
||||
neighbor = (self.x + offset[0], self.y + offset[1])
|
||||
if not valid(neighbor):
|
||||
continue
|
||||
if grid[neighbor[0]][neighbor[1]].visited:
|
||||
continue
|
||||
neighbors.append(neighbor)
|
||||
return neighbors
|
||||
|
||||
# Render the single cell
|
||||
def render(self):
|
||||
global divX, divY
|
||||
translate(self.x * divX, self.y * divY)
|
||||
# Drawing Cell Background
|
||||
# Visited, Unvisited, Highlighted
|
||||
|
||||
if self.live:
|
||||
fill(244, 117, 117)
|
||||
elif self.visited:
|
||||
fill(255)
|
||||
else:
|
||||
fill(204)
|
||||
noStroke()
|
||||
rect(0, 0, divX, divY)
|
||||
|
||||
# Drawing Cell Lines
|
||||
stroke(0)
|
||||
fill(255)
|
||||
strokeWeight(2.5)
|
||||
if self.bottom:
|
||||
line(0, divY, divX, divY)
|
||||
if self.right:
|
||||
line(divX, 0, divX, divY)
|
||||
resetMatrix()
|
||||
|
||||
# Open walls between two cells on the grid
|
||||
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
|
||||
|
||||
# Validates whether a coordinate is valid with the curret columns and rows set
|
||||
def valid(coordinate):
|
||||
global columns, rows
|
||||
return not (coordinate[0] < 0 or coordinate[0] >= columns or coordinate[1] < 0 or coordinate[1] >= rows)
|
||||
|
||||
# Generates a new grid and cellList (with start) for the maze generation.
|
||||
# Serves mostly to ease the process of regenerating a maze without restarting the Sketch
|
||||
def generate(xx=None, yy=None):
|
||||
global columns, rows, offsets
|
||||
# Bottom, Right, Left, Top
|
||||
offsets = [(0, 1), (1, 0), (-1, 0), (0, -1)]
|
||||
columns, rows = 25, 25
|
||||
|
||||
global grid, divX, divY
|
||||
divX, divY = width / float(columns), height / float(rows)
|
||||
grid = [[Cell(x, y) for y in range(rows)] for x in range(columns)]
|
||||
|
||||
global current, runSet, runSetActive
|
||||
current = 0, 0
|
||||
runSet = []
|
||||
runSetActive = False
|
||||
|
||||
def pixelToCoordinates(x, y):
|
||||
return int(x / float
|
||||
(divX)), int(y / float(divY))
|
||||
|
||||
def setup():
|
||||
size(750, 750)
|
||||
generate()
|
||||
|
||||
# Runs the cell.render() action on every cell
|
||||
def render():
|
||||
background(0)
|
||||
for row in grid:
|
||||
for cell in row:
|
||||
cell.render()
|
||||
|
||||
def tick():
|
||||
global current, runSet, runSetActive
|
||||
if runSetActive:
|
||||
if len(runSet) > 0:
|
||||
print('a')
|
||||
get = runSet[0]
|
||||
if get == []:
|
||||
return
|
||||
print('b')
|
||||
print(runSet, get)
|
||||
choice = random.choice(get)
|
||||
print('c')
|
||||
openWalls(choice[0], choice[1], choice[0], choice[1] - 1)
|
||||
print('d')
|
||||
del runSet[0]
|
||||
return
|
||||
else:
|
||||
runSet = [[]]
|
||||
runSetActive = False
|
||||
|
||||
# Are we done with the maze?
|
||||
if current[0] == columns and current[1] == rows - 1:
|
||||
if len(runSet) > 0:
|
||||
runSetActive = True
|
||||
return
|
||||
print('Done')
|
||||
render()
|
||||
noLoop()
|
||||
# Are we on the horozontal edge?
|
||||
elif current[0] == columns:
|
||||
if not runSetActive:
|
||||
if current[1] > 0:
|
||||
for coord in runSet[-1]:
|
||||
grid[coord[0]][coord[1]].live = False
|
||||
runSetActive = True
|
||||
current = 0, current[1] + 1
|
||||
# Keep carving runSets
|
||||
else:
|
||||
# If you're on the ceiling of the maze, just carve and skip the whole thing.
|
||||
if current[1] == 0:
|
||||
openWalls(current[0], current[1], current[0] + 1, current[1])
|
||||
# Keep carving east
|
||||
elif current[0] == 0 or not random.choice([True, False]):
|
||||
runSet[-1].append((current[0], current[1]))
|
||||
grid[current[0]][current[1]].live = True
|
||||
openWalls(current[0], current[1], current[0] + 1, current[1])
|
||||
# Break out new runSet
|
||||
else:
|
||||
for coord in runSet[-1]:
|
||||
grid[coord[0]][coord[1]].live = False
|
||||
runSet.append([(current[0], current[1])])
|
||||
if not runSetActive:
|
||||
current = current[0] + 1, current[1]
|
||||
|
||||
def draw():
|
||||
for _ in range(columns):
|
||||
tick()
|
||||
render()
|
||||
|
||||
def mouseClicked():
|
||||
loop()
|
||||
generate(mouseX, mouseY)
|
||||
2
mazes/MazeGenSidewinder/sketch.properties
Normal file
2
mazes/MazeGenSidewinder/sketch.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
mode=Python
|
||||
mode.id=jycessing.mode.PythonMode
|
||||
Reference in New Issue
Block a user