├── FUNDING.yml
├── images
├── maze.png
├── path.png
├── hexmaze.png
├── maskmaze.png
├── mazespsd.psd
├── polygon.png
├── colormode1.png
├── colormode2.png
├── ellersMaze.png
├── heuristics.png
├── kruskalMaze.png
├── polarmaze.png
├── primsMaze.png
├── wilsonMaze.png
├── aldousBroderMaze.png
├── binarytreeMaze.png
├── growingtreeMaze.png
├── huntandkillMaze.png
├── sidewinderMaze.png
├── simplePrimesMaze.png
└── recursiveBacktracker.png
├── constants.py
├── ui
├── __pycache__
│ ├── ui.cpython-39.pyc
│ ├── colors.cpython-39.pyc
│ └── setup.cpython-39.pyc
├── colors.py
├── setup.py
└── ui.py
├── __pycache__
└── constants.cpython-39.pyc
├── classes
├── __pycache__
│ ├── cell.cpython-39.pyc
│ ├── grid.cpython-39.pyc
│ ├── mask.cpython-39.pyc
│ ├── color.cpython-39.pyc
│ ├── ellers.cpython-39.pyc
│ ├── hexCell.cpython-39.pyc
│ ├── hexGrid.cpython-39.pyc
│ ├── kruskal.cpython-39.pyc
│ ├── prims.cpython-39.pyc
│ ├── wilson.cpython-39.pyc
│ ├── __init__.cpython-39.pyc
│ ├── heuristic.cpython-39.pyc
│ ├── polarCell.cpython-39.pyc
│ ├── polarGrid.cpython-39.pyc
│ ├── aldousBroder.cpython-39.pyc
│ ├── binaryTree.cpython-39.pyc
│ ├── growingTree.cpython-39.pyc
│ ├── huntandkill.cpython-39.pyc
│ ├── sideWinder.cpython-39.pyc
│ ├── weightedCell.cpython-39.pyc
│ ├── weightedGrid.cpython-39.pyc
│ └── recursiveBacktracker.cpython-39.pyc
├── __init__.py
├── polarCell.py
├── weightedCell.py
├── mask.py
├── color.py
├── weightedGrid.py
├── heuristic.py
├── aldousBroder.py
├── binaryTree.py
├── growingTree.py
├── wilson.py
├── polarGrid.py
├── sideWinder.py
├── huntandkill.py
├── hexGrid.py
├── hexCell.py
├── recursiveBacktracker.py
├── cell.py
├── kruskal.py
├── prims.py
├── ellers.py
└── grid.py
├── utils
└── resizeImage.py
├── weightedMaze.py
├── hexMaze.py
├── polarMaze.py
├── imageMaze.py
├── main.py
└── README.md
/FUNDING.yml:
--------------------------------------------------------------------------------
1 | patreon: auctux
2 | ko_fi: auctux
3 |
--------------------------------------------------------------------------------
/images/maze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/maze.png
--------------------------------------------------------------------------------
/images/path.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/path.png
--------------------------------------------------------------------------------
/images/hexmaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/hexmaze.png
--------------------------------------------------------------------------------
/images/maskmaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/maskmaze.png
--------------------------------------------------------------------------------
/images/mazespsd.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/mazespsd.psd
--------------------------------------------------------------------------------
/images/polygon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/polygon.png
--------------------------------------------------------------------------------
/images/colormode1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/colormode1.png
--------------------------------------------------------------------------------
/images/colormode2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/colormode2.png
--------------------------------------------------------------------------------
/images/ellersMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/ellersMaze.png
--------------------------------------------------------------------------------
/images/heuristics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/heuristics.png
--------------------------------------------------------------------------------
/images/kruskalMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/kruskalMaze.png
--------------------------------------------------------------------------------
/images/polarmaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/polarmaze.png
--------------------------------------------------------------------------------
/images/primsMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/primsMaze.png
--------------------------------------------------------------------------------
/images/wilsonMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/wilsonMaze.png
--------------------------------------------------------------------------------
/images/aldousBroderMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/aldousBroderMaze.png
--------------------------------------------------------------------------------
/images/binarytreeMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/binarytreeMaze.png
--------------------------------------------------------------------------------
/images/growingtreeMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/growingtreeMaze.png
--------------------------------------------------------------------------------
/images/huntandkillMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/huntandkillMaze.png
--------------------------------------------------------------------------------
/images/sidewinderMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/sidewinderMaze.png
--------------------------------------------------------------------------------
/images/simplePrimesMaze.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/simplePrimesMaze.png
--------------------------------------------------------------------------------
/constants.py:
--------------------------------------------------------------------------------
1 | width, height = 800, 800
2 | size = (width, height)
3 |
4 | cell_size = 40
5 | rows = width//cell_size
6 | cols = height//cell_size
7 |
--------------------------------------------------------------------------------
/images/recursiveBacktracker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/images/recursiveBacktracker.png
--------------------------------------------------------------------------------
/ui/__pycache__/ui.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/ui/__pycache__/ui.cpython-39.pyc
--------------------------------------------------------------------------------
/__pycache__/constants.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/__pycache__/constants.cpython-39.pyc
--------------------------------------------------------------------------------
/ui/__pycache__/colors.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/ui/__pycache__/colors.cpython-39.pyc
--------------------------------------------------------------------------------
/ui/__pycache__/setup.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/ui/__pycache__/setup.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/cell.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/cell.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/grid.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/grid.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/mask.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/mask.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/color.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/color.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/ellers.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/ellers.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/hexCell.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/hexCell.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/hexGrid.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/hexGrid.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/kruskal.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/kruskal.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/prims.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/prims.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/wilson.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/wilson.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/__init__.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/__init__.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/heuristic.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/heuristic.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/polarCell.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/polarCell.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/polarGrid.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/polarGrid.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/aldousBroder.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/aldousBroder.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/binaryTree.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/binaryTree.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/growingTree.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/growingTree.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/huntandkill.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/huntandkill.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/sideWinder.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/sideWinder.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/weightedCell.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/weightedCell.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/weightedGrid.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/weightedGrid.cpython-39.pyc
--------------------------------------------------------------------------------
/classes/__pycache__/recursiveBacktracker.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Josephbakulikira/Procedural-Maze-Generator-Algorithms/HEAD/classes/__pycache__/recursiveBacktracker.cpython-39.pyc
--------------------------------------------------------------------------------
/utils/resizeImage.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | from PIL import Image
3 | from constants import *
4 |
5 | im = Image.open("./images/shape0.jpg")
6 | w, h = im.size
7 |
8 | im_resized = im.resize((w, h), Image.ANTIALIAS)
9 | im_resized.save("./images/shape0Resized.png", "PNG")
10 |
--------------------------------------------------------------------------------
/classes/__init__.py:
--------------------------------------------------------------------------------
1 | from classes.binaryTree import BinaryTree
2 | from classes.sideWinder import SideWinder
3 | from classes.aldousBroder import AldousBroder
4 | from classes.huntandkill import HuntAndKill
5 | from classes.recursiveBacktracker import RecursiveBacktracker
6 | from classes.kruskal import Kruskals
7 | from classes.wilson import Wilson
8 | from classes.prims import SimplePrims, Prims
9 | from classes.grid import Grid
10 | from classes.growingTree import GrowingTree
11 | from classes.ellers import Ellers
--------------------------------------------------------------------------------
/classes/polarCell.py:
--------------------------------------------------------------------------------
1 | from classes.cell import Cell
2 |
3 | class PolarCell(Cell):
4 | def __init__(self, x, y, size):
5 | super().__init__(x, y, size)
6 | self.clockwise = None
7 | self.c_clockwise = None
8 | self.inward = None
9 | self.outward = []
10 | self.neighbours = []
11 |
12 | def SetNeighbours(self):
13 | self.neighbours = []
14 | if self.clockwise:
15 | self.neighbours.append(self.clockwise)
16 | if self.c_clockwise:
17 | self.neighbours.append(self.c_clockwise)
18 | if self.inward:
19 | self.neighbours.append(self.inward)
20 | self.neighbours += self.outward
21 |
22 | return self.neighbours
23 |
--------------------------------------------------------------------------------
/ui/colors.py:
--------------------------------------------------------------------------------
1 | white = (255,255,255)
2 | blue = (0,0,255)
3 | green = (0,255,0)
4 | red = (255,0,0)
5 | black = (0,0,0)
6 | orange = (255,100,10)
7 | yellow = (255,255,0)
8 | blue_green = (0,255,170)
9 | marroon = (115,0,0)
10 | lime = (180,255,100)
11 | pink = (255,100,180)
12 | purple = (240,0,255)
13 | gray = (127,127,127)
14 | magenta = (255,0,230)
15 | brown = (100,40,0)
16 | forest_green = (0,50,0)
17 | navy_blue = (0,0,100)
18 | rust = (210,150,75)
19 | dandilion_yellow = (255,200,0)
20 | highlighter = (255,255,100)
21 | sky_blue = (0,255,255)
22 | light_gray = (200,200,200)
23 | dark_gray = (50,50,50)
24 | tan = (230,220,170)
25 | coffee_brown =(200,190,140)
26 | moon_glow = (235,245,255)
27 |
--------------------------------------------------------------------------------
/classes/weightedCell.py:
--------------------------------------------------------------------------------
1 | from classes.cell import Cell
2 | from classes.heuristic import Heuristic
3 |
4 | class WeightedCell(Cell):
5 | def __init__(self, x, y, size):
6 | super().__init__(x, y, size)
7 | self.weight = 1
8 | self.neighbours = []
9 | def CalculateHeuristic(self, rows, cols):
10 | weights = Heuristic(rows, cols)
11 | pending = [self]
12 | while len(pending) > 0:
13 | pending.sort(key=lambda cell: weights.GetRecord(cell))
14 | this = pending[0]
15 | pending.remove(this)
16 |
17 | for cell in this.connections:
18 | val = 0 if not weights.GetRecord(this) else weights.GetRecord(this)
19 | total = val + cell.weight
20 | if not weights.GetRecord(cell) or total < weights.GetRecord(cell):
21 | pending.append(cell)
22 | weights.SetRecord(cell, total)
23 | weights.SetRecord(self, 0)
24 | return weights
25 |
--------------------------------------------------------------------------------
/ui/setup.py:
--------------------------------------------------------------------------------
1 | from ui.ui import *
2 | from ui.colors import *
3 | from constants import width, height
4 |
5 | # panel = Panel()
6 | #
7 | # StartButton = Button("Start", (width - 270, 180), 150, 50, 0, (10, 10, 30), (255, 255, 255))
8 | PressEnter = TextUI("press 'Enter or Return' to Start", (width//2, height//2), white)
9 | PressEnter.fontSize = 25
10 |
11 | # AlgorithmChoice = DropDownButton("Select", (width - 350, 400), 300, 50, 11, 2, (10, 10, 30), (255, 255, 255))
12 | # AlgorithmChoice.childs[0].text = "Wilson"
13 | # AlgorithmChoice.childs[1].text = "Binary Tree"
14 | # AlgorithmChoice.childs[2].text = "Kruskal's"
15 | # AlgorithmChoice.childs[3].text = "SideWinder"
16 | # AlgorithmChoice.childs[4].text = "Hund and Kill"
17 | # AlgorithmChoice.childs[5].text = "Aldous Broder"
18 | # AlgorithmChoice.childs[6].text = "Recursive Backtracker"
19 | # AlgorithmChoice.childs[7].text = "Simplified Prims"
20 | # AlgorithmChoice.childs[8].text = "Prims"
21 | # AlgorithmChoice.childs[9].text = "Growing Tree"
22 | # AlgorithmChoice.childs[10].text = "Eller's"
23 |
24 | # AlgorithmChoice.currentIndex = 1
25 |
--------------------------------------------------------------------------------
/classes/mask.py:
--------------------------------------------------------------------------------
1 | import random
2 | from classes.grid import Grid
3 |
4 | class Mask:
5 | def __init__(self, rows, cols):
6 | self.rows = rows
7 | self.cols = cols
8 | self.boolGrid = [[True for y in range(rows)] for x in range(cols)]
9 | self.count = 0
10 |
11 | def Count(self):
12 | count = 0
13 | for x in range(self.cols):
14 | for y in range(self.rows):
15 | if self.boolGrid[x][y]:
16 | count += 1
17 | self.count = count
18 | return count
19 |
20 | def Random(self):
21 | while True:
22 | x = random.randint(0, self.cols-1)
23 | y = random.randint(0, self.rows-1)
24 | if self.boolGrid[x][y]:
25 | return x, y
26 |
27 | class GridMask(Grid):
28 | def __init__(self, rows, cols, cell_size, mask):
29 | super().__init__(rows, cols, cell_size)
30 | self.mask = mask
31 |
32 | def UpdateGrid(self):
33 | for x in range(self.cols):
34 | for y in range(self.rows):
35 | if self.mask.boolGrid[x][y] == False:
36 | self.cells[x][y] = None
37 |
38 | self.PrepareGrid()
39 |
40 | def GetRandomCell(self):
41 | x , y = self.mask.Random()
42 | return self.cells[x][y]
43 |
44 | def GetSize(self):
45 | return self.mask.Count()
46 |
--------------------------------------------------------------------------------
/weightedMaze.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from constants import *
3 | from ui.colors import *
4 | from classes.recursiveBacktracker import RecursiveBacktracker
5 | from classes.weightedGrid import WeightedGrid
6 |
7 | # Initialize pygame
8 | pygame.init()
9 | screen = pygame.display.set_mode(size)
10 | clock = pygame.time.Clock()
11 | fps = 30
12 |
13 | grid = WeightedGrid(rows, cols, cell_size)
14 | grid.ConfigureCells()
15 | recursive_backtracker = RecursiveBacktracker(grid, "GREEN")
16 | recursive_backtracker.starting_node = grid.cells[0][0]
17 | recursive_backtracker.end_node = grid.cells[cols-1][rows-1]
18 | recursive_backtracker.starting_node.isStartingNode = True
19 | recursive_backtracker.end_node.isgoalNode = True
20 |
21 | show_text = False
22 | color_mode = False
23 | show_path = False
24 |
25 | run = True
26 | while run:
27 | screen.fill(black)
28 | # Set Caption and fps
29 | clock.tick(fps)
30 | frame_rate = int(clock.get_fps())
31 | pygame.display.set_caption(f"Maze Generator - FPS: {frame_rate}")
32 |
33 | # Handle events
34 | for event in pygame.event.get():
35 | if event.type == pygame.QUIT:
36 | run = False
37 | if event.type == pygame.KEYDOWN:
38 | if event.key == pygame.K_ESCAPE:
39 | run = False
40 | elif event.key == pygame.K_h:
41 | show_text = not show_text
42 | elif event.key == pygame.K_SPACE:
43 | color_mode = not color_mode
44 | elif event.key == pygame.K_s:
45 | show_path = not show_path
46 |
47 | recursive_backtracker.Generate(screen, show_text, color_mode, show_path)
48 | # hexGrid.Show(screen, show_text, color_mode, show_path)
49 | # pygame.display.flip()
50 |
51 | pygame.quit()
52 |
--------------------------------------------------------------------------------
/classes/color.py:
--------------------------------------------------------------------------------
1 | from ui.colors import *
2 | import pygame
3 | import colorsys
4 |
5 | def hsv_to_rgb(h, s, v):
6 | return tuple(round(i * 255) for i in colorsys.hsv_to_rgb(h, s, v))
7 |
8 | class GridColor:
9 | heuristics = None
10 | farthest = None
11 | max_distance = None
12 |
13 | def __init__(self, color_str = "RED"):
14 | self.color = color_str
15 | # print(self.color)
16 | def distances(self, heuristics, goal, start, grid):
17 | self.heuristics = heuristics
18 | self.farthest, self.max_distance = self.heuristics.GetFarthest(goal, start, grid)
19 |
20 | def UpdateColor(self, cell):
21 | if self.color != "HSV":
22 | val = self.heuristics.GetRecord(cell)
23 | distance = 0 if val == None else val
24 | intensity = (self.max_distance - distance)/self.max_distance
25 | dark = min(int(255 * intensity), 255)
26 | bright = min( int(128 + (127 * intensity)),255)
27 |
28 | colors = {
29 | "RED": (bright, dark, dark),
30 | "BLUE": (dark, dark, bright),
31 | "GREEN": (dark, bright, dark),
32 | "YELLOW": (bright, bright, dark),
33 | "CYAN": (dark, bright, bright),
34 | "PURPLE": (bright, dark, bright),
35 | "PURPLE_E": (bright, dark, bright),
36 | "GREEN_E":(0, bright, 0),
37 | "BLUE_E": (0, 0, bright),
38 | "RED_E": (bright, 0, 0)
39 | }
40 |
41 | return tuple(colors[self.color])
42 | else:
43 | val = self.heuristics.GetRecord(cell)
44 | hue = 0 if val == None else val
45 | _color = hsv_to_rgb((hue/360), 1, 1)
46 | # print(_color)
47 | return _color
48 |
--------------------------------------------------------------------------------
/hexMaze.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from constants import *
3 | from ui.colors import *
4 | from classes.recursiveBacktracker import RecursiveBacktracker
5 | from classes.hexGrid import HexGrid
6 |
7 | # Initialize pygame
8 | pygame.init()
9 | screen = pygame.display.set_mode(size)
10 | clock = pygame.time.Clock()
11 | fps = 30
12 |
13 | hexGrid = HexGrid(13, 11, cell_size)
14 | # hexGrid.PrepareGrid()
15 | hexGrid.ConfigureCells()
16 | recursive_backtracker = RecursiveBacktracker(hexGrid, "RED")
17 | recursive_backtracker.starting_node = hexGrid.cells[0][0]
18 | recursive_backtracker.end_node = hexGrid.cells[10][12]
19 | recursive_backtracker.starting_node.isStartingNode = True
20 | recursive_backtracker.end_node.isgoalNode = True
21 |
22 | show_text = False
23 | color_mode = False
24 | show_path = False
25 |
26 | run = True
27 | while run:
28 | screen.fill(black)
29 | # Set Caption and fps
30 | clock.tick(fps)
31 | frame_rate = int(clock.get_fps())
32 | pygame.display.set_caption(f"Maze Generator - FPS: {frame_rate}")
33 |
34 | # Handle events
35 | for event in pygame.event.get():
36 | if event.type == pygame.QUIT:
37 | run = False
38 | if event.type == pygame.KEYDOWN:
39 | if event.key == pygame.K_ESCAPE:
40 | run = False
41 | elif event.key == pygame.K_h:
42 | show_text = not show_text
43 | elif event.key == pygame.K_SPACE:
44 | color_mode = not color_mode
45 | elif event.key == pygame.K_s:
46 | show_path = not show_path
47 |
48 | recursive_backtracker.Generate(screen, show_text, color_mode, show_path)
49 | # hexGrid.Show(screen, show_text, color_mode, show_path)
50 | # pygame.display.flip()
51 |
52 | pygame.image.save(screen, "./images/hexmaze.png")
53 | pygame.quit()
54 |
--------------------------------------------------------------------------------
/polarMaze.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from constants import *
3 | from ui.colors import *
4 | from classes.recursiveBacktracker import RecursiveBacktracker
5 | from classes.polarGrid import PolarGrid
6 | # Initialize pygame
7 | pygame.init()
8 | screen = pygame.display.set_mode(size)
9 | clock = pygame.time.Clock()
10 | fps = 30
11 |
12 | polarGrid = PolarGrid(8, 1, 50)
13 | polarGrid.cells = polarGrid.PrepareGrid()
14 | polarGrid.ConfigureCells()
15 | recursive_backtracker = RecursiveBacktracker(polarGrid, "PURPLE")
16 | recursive_backtracker.starting_node = polarGrid.cells[0][0]
17 | recursive_backtracker.starting_node.isStartingNode = True
18 | recursive_backtracker.end_node = polarGrid.cells[0][0]
19 | recursive_backtracker.end_node.isgoalNode = True
20 |
21 | show_text = False
22 | color_mode = False
23 | show_path = False
24 |
25 | run = True
26 | while run:
27 | screen.fill(black)
28 | # Set Caption and fps
29 | clock.tick(fps)
30 | frame_rate = int(clock.get_fps())
31 | pygame.display.set_caption(f"Maze Generator - FPS: {frame_rate}")
32 |
33 | # Handle events
34 | for event in pygame.event.get():
35 | if event.type == pygame.QUIT:
36 | run = False
37 | if event.type == pygame.KEYDOWN:
38 | if event.key == pygame.K_ESCAPE:
39 | run = False
40 | elif event.key == pygame.K_h:
41 | show_text = not show_text
42 | elif event.key == pygame.K_SPACE:
43 | color_mode = not color_mode
44 | elif event.key == pygame.K_s:
45 | show_path = not show_path
46 |
47 | recursive_backtracker.Generate(screen, show_text, color_mode, show_path)
48 | # polarGrid.Show(screen, show_text, color_mode, show_path)
49 | pygame.display.flip()
50 |
51 | pygame.image.save(screen, "./images/polarmaze.png")
52 | pygame.quit()
53 |
--------------------------------------------------------------------------------
/classes/weightedGrid.py:
--------------------------------------------------------------------------------
1 | from classes.grid import Grid
2 | from classes.weightedCell import WeightedCell
3 |
4 | class WeightedGrid(Grid):
5 | def __init__(self, rows, cols, cell_size):
6 | super().__init__(rows, cols, cell_size)
7 | self.farthest = None
8 | self.maximum = None
9 |
10 | def Distances(self, start, goal, distance):
11 | self.heuristics = distance
12 | self.farthest, self.maximum = self.heuristics.GetFarthest(goal, start, self)
13 |
14 | def PrepareGrid(self):
15 | self.cells = [[None for i in range(self.rows)] for j in range(self.cols)]
16 | for x in range(self.cols):
17 | for y in range(self.rows):
18 | self.cells[x][y] = WeightedCell(x, y, self.cell_size)
19 |
20 | def ConfigureCells(self):
21 | for x in range(self.cols):
22 | for y in range(self.rows):
23 | if self.cells[x][y]:
24 | self.cells[x][y].neighbours = []
25 | # East neighbour cell
26 | if x+1 < self.cols and self.cells[x+1][y]:
27 | self.cells[x][y].East = self.cells[x+1][y]
28 | self.cells[x][y].neighbours.append(self.cells[x+1][y])
29 | # West neightbour cell
30 | if x-1 >= 0 and self.cells[x-1][y]:
31 | self.cells[x][y].West = self.cells[x-1][y]
32 | self.cells[x][y].neighbours.append(self.cells[x-1][y])
33 |
34 | # North neighbour cell
35 | if y-1 >= 0 and self.cells[x][y-1]:
36 | self.cells[x][y].North = self.cells[x][y-1]
37 | self.cells[x][y].neighbours.append(self.cells[x][y-1])
38 | # South neighbour cell
39 | if y+1 < self.rows and self.cells[x][y+1]:
40 | self.cells[x][y].South = self.cells[x][y+1]
41 | self.cells[x][y].neighbours.append(self.cells[x][y+1])
42 |
--------------------------------------------------------------------------------
/classes/heuristic.py:
--------------------------------------------------------------------------------
1 | class Heuristic:
2 | def __init__(self, rows, cols):
3 | self.rows = rows
4 | self.cols = cols
5 | self.cells_record = [[None for y in range(rows)] for x in range(cols)]
6 |
7 | def SetRecord(self, cell, distance):
8 | self.cells_record[cell.x][cell.y] = distance
9 |
10 | def GetFarthest(self, goal, start, grid):
11 | max_distance = 0
12 | farthest= start
13 | for x in range(self.cols):
14 | for y in range(self.rows):
15 | dist = self.cells_record[x][y] if self.cells_record[x][y] else 0
16 | cell = grid.cells[x][y]
17 | if dist > max_distance:
18 | farthest = cell
19 | max_distance = dist
20 |
21 | return farthest, max_distance
22 |
23 | def Merge(self, other):
24 | new_distances = Heuristic(self.rows, self.cols)
25 | for x in range(self.cols):
26 | for y in range(self.rows):
27 | if other.cells_record[x][y] != None:
28 | new_distances.cells_record[x][y] = other.cells_record[x][y]
29 | else:
30 | new_distances.cells_record[x][y] = self.cells_record[x][y]
31 |
32 | return new_distances
33 |
34 | def BacktrackPath(self, goal, start):
35 | current = goal
36 | path_track = Heuristic(self.rows, self.cols)
37 | path_track.SetRecord(current, self.GetRecord(current))
38 | max_counter = self.rows * self.cols
39 | counter = 0
40 | while current != start:
41 | for cell in current.connections:
42 | if self.GetRecord(cell) < self.GetRecord(current):
43 | path_track.SetRecord(cell, self.GetRecord(cell))
44 | current = cell
45 | counter += 1
46 | if counter > max_counter:
47 | break
48 | return path_track
49 |
50 | def GetRecord(self, cell):
51 | return self.cells_record[cell.x][cell.y]
52 |
--------------------------------------------------------------------------------
/imageMaze.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from constants import *
3 | from ui.colors import *
4 | from classes.recursiveBacktracker import RecursiveBacktracker
5 | from classes.grid import Grid
6 | from classes.mask import Mask, GridMask
7 | import cv2
8 |
9 | # Initialize pygame
10 | pygame.init()
11 | screen = pygame.display.set_mode(size)
12 | clock = pygame.time.Clock()
13 | fps = 30
14 |
15 | image = cv2.imread("./images/polygon.png")
16 |
17 | show_text = False
18 | color_mode = False
19 | show_path = False
20 |
21 | mask = Mask(rows, cols)
22 | for i in range(rows):
23 | for j in range(cols):
24 |
25 | if image[j*cell_size][i*cell_size][0] == 255:
26 | mask.boolGrid[i][j] = False
27 |
28 | maskGrid = GridMask(rows, cols, cell_size, mask)
29 | maskGrid.UpdateGrid()
30 |
31 | recursive_backtracker = RecursiveBacktracker(maskGrid, "PURPLE_E")
32 | recursive_backtracker.starting_node = maskGrid.cells[cols//2][rows//2]
33 | recursive_backtracker.starting_node.isStartingNode = True
34 | recursive_backtracker.end_node = maskGrid.cells[cols//2][0]
35 | recursive_backtracker.end_node.isgoalNode = True
36 |
37 | run = True
38 | while run:
39 | #screen.fill(black)
40 |
41 | # Set Caption and fps
42 | clock.tick(fps)
43 | frame_rate = int(clock.get_fps())
44 | pygame.display.set_caption(f"Maze Generator - FPS: {frame_rate}")
45 |
46 | # Handle events
47 | for event in pygame.event.get():
48 | if event.type == pygame.QUIT:
49 | run = False
50 | if event.type == pygame.KEYDOWN:
51 | if event.key == pygame.K_ESCAPE:
52 | run = False
53 | elif event.key == pygame.K_h:
54 | show_text = not show_text
55 | elif event.key == pygame.K_SPACE:
56 | color_mode = not color_mode
57 | elif event.key == pygame.K_s:
58 | show_path = not show_path
59 |
60 | recursive_backtracker.Generate(screen, show_text, color_mode, show_path)
61 |
62 |
63 | pygame.image.save(screen, "./images/maskmaze.png")
64 | pygame.quit()
65 |
--------------------------------------------------------------------------------
/classes/aldousBroder.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | import random
3 | from classes.grid import Grid, Update
4 | from ui.colors import *
5 |
6 | """
7 | STEPS:
8 |
9 | 1. Choose a Cell. Any cell.
10 | 2. Choose a connected neighbor of the cell and travel to it.
11 | If the neighbor has not yet been visited,
12 | add the traveled edge to the spanning tree.
13 | 3. Repeat step 2 until all vertexes have been visited.
14 | """
15 | class AldousBroder:
16 | def __init__(self, grid, path_color="YELLOW"):
17 | self.grid = grid
18 | self.rows = grid.rows
19 | self.cols = grid.cols
20 | self.starting_node = grid.cells[0][0]
21 | self.starting_node.isStartingNode = True
22 | self.end_node = grid.cells[self.cols-1][self.rows-1]
23 | self.end_node.isgoalNode = True
24 | self.isDone = False
25 | self.shortest_path = None
26 | self.path_color = path_color
27 | if path_color == "HSV":
28 | self.grid.path_color = white
29 |
30 | def Generate(self, screen, show_heuristic, show_color_map, show_path):
31 | if not self.isDone:
32 | randomX = random.randint(0, self.cols-1)
33 | randomY = random.randint(0, self.rows-1)
34 | current = self.grid.cells[randomX][randomY]
35 | temp_color = current.color
36 | unvisited = self.rows * self.cols - 1
37 |
38 | while unvisited > 0:
39 |
40 | neighbour = random.choice(current.neighbours)
41 | if len(neighbour.connections) == 0:
42 | Grid.JoinAndDestroyWalls(current, neighbour)
43 | unvisited -= 1
44 |
45 | current.color = navy_blue
46 | self.grid.Show(screen, show_heuristic, show_color_map)
47 | pygame.display.flip()
48 | current.color = temp_color
49 | current = neighbour
50 | self.isDone = True
51 | Update(self, screen, show_heuristic, show_color_map, show_path)
52 |
53 | if show_path:
54 | self.grid.Show(screen, show_heuristic, show_color_map,self.shortest_path)
55 | else:
56 | self.grid.Show(screen, show_heuristic, show_color_map, None)
57 |
58 |
--------------------------------------------------------------------------------
/classes/binaryTree.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from classes.grid import Grid, Update
3 | from ui.colors import *
4 | import random
5 |
6 | """
7 | STEPS:
8 |
9 | This has to be the simplest
10 | This is how it works:
11 | - for every cell in the grid, randomly carve a passage either north, or west.
12 | That's it
13 | """
14 |
15 | class BinaryTree:
16 | def __init__(self, grid, path_color="BLUE"):
17 | self.grid = grid
18 | self.isDone = False
19 | self.starting_node = grid.cells[0][0]
20 | self.starting_node.isStartingNode = True
21 | self.end_node = grid.cells[self.grid.cols-1][self.grid.rows-1]
22 | self.end_node.isgoalNode = True
23 | self.show_path = True
24 | self.path_color = path_color
25 | self.shortest_path = None
26 | if path_color == "HSV":
27 | self.grid.path_color = white
28 |
29 | def Generate(self, screen, show_heuristic, show_color_map,show_path):
30 | if not self.isDone:
31 | for x in range(self.grid.cols):
32 | for y in range(self.grid.rows):
33 | neighbours = []
34 | self.grid.cells[x][y].isCurrent = True
35 | # Check two neighbours
36 | # check for north and south Neighbours
37 | if self.grid.cells[x][y].North != None:
38 | neighbours.append(self.grid.cells[x][y].North)
39 | if self.grid.cells[x][y].East != None:
40 | neighbours.append(self.grid.cells[x][y].East)
41 |
42 | # pick a random neighbour
43 | # if any in the neighbours list
44 | choice = None
45 | if len(neighbours) > 0:
46 | choice = random.choice(neighbours)
47 |
48 | if choice != None:
49 | Grid.JoinAndDestroyWalls(self.grid.cells[x][y], choice)
50 |
51 | self.grid.Show(screen, show_heuristic, show_color_map)
52 | self.grid.cells[x][y].isCurrent = False
53 | pygame.display.flip()
54 |
55 | self.isDone = True
56 | # self.grid.Braid(0.5)
57 |
58 | Update(self, screen, show_heuristic, show_color_map, show_path)
59 |
60 | if show_path:
61 | self.grid.Show(screen, show_heuristic, show_color_map, self.shortest_path)
62 | else:
63 | self.grid.Show(screen, show_heuristic, show_color_map)
64 |
65 |
--------------------------------------------------------------------------------
/classes/growingTree.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | import random
3 | from classes.grid import Grid, Update
4 |
5 | """
6 | STEPS:
7 |
8 | 1. Let C be a list of cells, initially empty. Add one cell to C, at random.
9 | 2. Choose a cell from C, and carve a passage to any unvisited neighbor of that cell,
10 | adding that neighbor to C as well. If there are no unvisited neighbors, remove the cell from C.
11 | 3. Repeat #2 until C is empty.
12 | """
13 |
14 | class GrowingTree:
15 | def __init__(self, grid, path_color="BLUE"):
16 | self.grid = grid
17 | self.cols = grid.cols
18 | self.rows = grid.rows
19 | self.isDone = False
20 | self.starting_node = grid.cells[0][0]
21 | self.starting_node.isStartingNode = True
22 | self.end_node = grid.cells[self.cols-1][self.rows-1]
23 | self.end_node.isgoalNode = True
24 | self.show_path = True
25 | self.path_color = path_color
26 | self.shortest_path = None
27 | if path_color == "HSV":
28 | self.grid.path_color = white
29 |
30 | def Generate(self, screen, show_heuristic, show_color_map,show_path):
31 | if not self.isDone:
32 | active = []
33 | rx = random.randint(0, self.cols-1)
34 | ry = random.randint(0, self.rows-1)
35 | start_at = self.grid.cells[rx][ry]
36 | active.append(start_at)
37 |
38 | while len(active) > 0:
39 | current = active[-1]
40 | available_neighbours = []
41 | for cell in current.neighbours:
42 | if len(cell.connections) == 0:
43 | available_neighbours.append(cell)
44 |
45 | if len(available_neighbours) > 0:
46 | neighbour = random.choice(available_neighbours)
47 |
48 | neighbour.isCurrent = True
49 | Grid.JoinAndDestroyWalls(current, neighbour)
50 | self.grid.Show(screen, show_heuristic, show_color_map)
51 | pygame.display.flip()
52 | neighbour.isCurrent = False
53 |
54 | active.append(neighbour)
55 | else:
56 | active.remove(current)
57 | self.isDone = True
58 | Update(self, screen, show_heuristic, show_color_map, show_path)
59 |
60 | if show_path:
61 | self.grid.Show(screen, show_heuristic, show_color_map, self.shortest_path)
62 | else:
63 | self.grid.Show(screen, show_heuristic, show_color_map)
64 |
65 |
--------------------------------------------------------------------------------
/classes/wilson.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from classes.grid import Grid, Update
3 | from ui.colors import *
4 | import random
5 |
6 |
7 | """
8 | STEPS:
9 |
10 | 1. Choose any vertex at random and add it to the UST.
11 | 2. Select any vertex that is not already in the UST
12 | and perform a random walk until you encounter a vertex that is in the UST.
13 | 3. Add the vertices and edges touched in the random walk to the UST.
14 | 4. Repeat 2 and 3 until all vertices have been added to the UST.
15 | """
16 |
17 | class Wilson:
18 | def __init__(self, grid, path_color="BLUE_E"):
19 | self.grid = grid
20 | self.rows = grid.rows
21 | self.cols =grid.cols
22 | self.path_color = path_color
23 | self.isDone = False
24 | self.starting_node = grid.cells[0][0]
25 | self.starting_node.isStartingNode = True
26 | self.end_node = grid.cells[self.cols-1][self.rows-1]
27 | self.end_node.isgoalNode = True
28 | self.shortest_path = None
29 | if path_color == "HSV":
30 | self.grid.path_color = white
31 |
32 | def Generate(self, screen, show_heuristic, show_color_map, show_path):
33 | if not self.isDone:
34 | unvisited = self.grid.Flatten()
35 | random_cell = random.choice(unvisited)
36 | unvisited.remove(random_cell)
37 | while len(unvisited) > 0:
38 | current = random.choice(unvisited)
39 | path = [current]
40 |
41 | while current in unvisited:
42 | current = random.choice(current.neighbours)
43 | if current in path:
44 | _index = path.index(current)
45 | path = path[:_index+1]
46 | else:
47 | path.append(current)
48 |
49 | if len(path) > 1:
50 | for i in range(len(path)-1):
51 | path[i+1].isCurrent = True
52 |
53 | Grid.JoinAndDestroyWalls(path[i], path[i+1], "Normal")
54 | unvisited.remove(path[i])
55 |
56 | self.grid.Show(screen, show_heuristic, show_color_map)
57 | path[i+1].isCurrent = False
58 | pygame.display.flip()
59 | self.isDone = True
60 | Update(self, screen, show_heuristic, show_color_map, show_path)
61 |
62 | if show_path:
63 | self.grid.Show(screen, show_heuristic, show_color_map, self.shortest_path)
64 | else:
65 | self.grid.Show(screen, show_heuristic, show_color_map)
66 |
67 |
--------------------------------------------------------------------------------
/classes/polarGrid.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | import random
3 | from classes.grid import Grid
4 | from classes.polarCell import PolarCell
5 | from ui.colors import *
6 | from constants import width ,height
7 | import math
8 |
9 | class PolarGrid(Grid):
10 | def __init__(self, rows, cols, cell_size):
11 | super().__init__(rows, cols, cell_size)
12 |
13 | def PrepareGrid(self):
14 | rows = [None for i in range(self.rows)]
15 | row_height = 1/self.rows
16 | rows[0] = [PolarCell(0, 0, self.cell_size)]
17 |
18 | for i in range(self.rows):
19 | if i > 0:
20 | radius = i / self.rows
21 | circumference = (2 * math.pi) * radius
22 |
23 | previous_count = len(rows[i-1])
24 | estimated_cell_width = circumference / previous_count
25 | ratio = int(estimated_cell_width / row_height)
26 |
27 | cells = previous_count * ratio
28 | rows[i] = [PolarCell(i, j, self.cell_size) for j in range(cells)]
29 |
30 | return rows
31 |
32 | def ConfigureCells(self):
33 | for i in range(len(self.cells)):
34 | for j in range(len(self.cells[i])):
35 | current = self.cells[i][j]
36 | x, y = current.x, current.y
37 | if x > 0:
38 | _index = (y+1) % (len(self.cells[x]))
39 | if _index < len(self.cells[x]):
40 | current.clockwise = self.cells[x][_index]
41 | _index = y-1
42 | if _index >= 0:
43 | current.c_clockwise = self.cells[x][_index]
44 |
45 | ratio = len(self.cells[x]) / len(self.cells[x-1])
46 | parent = self.cells[x-1][int(y/ratio)]
47 |
48 | parent.outward.append(current)
49 | current.inward = parent
50 |
51 | def GetRandomCell(self):
52 | x = random.randint(0, len(self.cells)-1)
53 | y = 0
54 | if len(self.cells[x]) > 0:
55 | y = random.randint(0, len(self.cells[x])-1)
56 |
57 | return self.cells[x][y]
58 |
59 | def Show(self, screen, show_heuristic=None, show_color_map=None, shortest_path = None):
60 | centerX = width//2
61 | centerY = height//2
62 | for x in range(len(self.cells)):
63 | for y in range(len(self.cells[x])):
64 | cell = self.cells[x][y]
65 | if x > 0:
66 | theta = (2 * math.pi) / len(self.cells[cell.x])
67 |
68 | inner_radius = cell.x * self.cell_size
69 | outer_radius = (cell.x + 1) * self.cell_size
70 |
71 | theta_counter_clockwise = cell.y * theta
72 | theta_clockwise = (cell.y + 1) * theta
73 |
74 | ax = centerX + (inner_radius * math.cos(theta_counter_clockwise))
75 | ay = centerY + (inner_radius * math.sin(theta_counter_clockwise))
76 |
77 | bx = centerX + (outer_radius * math.cos(theta_counter_clockwise))
78 | by = centerY + (outer_radius * math.sin(theta_counter_clockwise))
79 |
80 | cx = centerX + (inner_radius * math.cos(theta_clockwise))
81 | cy = centerY + (inner_radius * math.sin(theta_clockwise))
82 |
83 | dx = centerX + (outer_radius * math.cos(theta_clockwise))
84 | dy = centerY + (outer_radius * math.sin(theta_clockwise))
85 | if cell.inward :
86 | pygame.draw.line(screen, white, (ax, ay), (cx, cy), 5)
87 | if cell.clockwise :
88 | pygame.draw.line(screen, white, (cx, cy), (dx, dy), 5)
89 | pygame.draw.circle(screen, white, (centerX, centerY), self.rows * self.cell_size, 5)
90 |
--------------------------------------------------------------------------------
/classes/sideWinder.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from classes.grid import Grid, Update
3 | from ui.colors import *
4 | import random
5 | import time
6 | from ui.colors import *
7 |
8 | """
9 | STEPS:
10 |
11 | 1. Work through the grid row-wise, starting with the cell at 0,0.
12 | Initialize the “run” set to be empty.
13 | 2. Add the current cell to the “run” set.
14 | 3. For the current cell, randomly decide whether to carve east or not.
15 | 4. If a passage was carved, make the new cell the current cell and repeat steps 2-4.
16 | 5. If a passage was not carved, choose any one of the cells in the run set and carve a passage north.
17 | Then empty the run set, set the next cell in the row to be the current cell, and repeat steps 2-5.
18 | 6. Continue until all rows have been processed.
19 | """
20 |
21 | class SideWinder:
22 | def __init__(self, grid, path_color="RED"):
23 | self.grid = grid
24 | self.starting_node = grid.cells[0][0]
25 | self.starting_node.isStartingNode = True
26 | self.end_node = grid.cells[self.grid.cols-1][self.grid.rows-1]
27 | self.end_node.isgoalNode = True
28 | self.isDone = False
29 | self.speed = 1
30 | self.max_speed = 1
31 | self.show_path = True
32 | self.path_color = path_color
33 | self.shortest_path = None
34 | if path_color == "HSV":
35 | self.grid.path_color = white
36 |
37 | def Generate(self, screen, show_heuristic, show_color_map, show_path):
38 | if not self.isDone:
39 | for y in range(self.grid.rows):
40 | history = []
41 |
42 | for x in range(self.grid.cols):
43 | current = self.grid.cells[x][y]
44 |
45 | current.isCurrent = True
46 | history.append(current)
47 |
48 | at_eastern_edge = False
49 | at_northern_edge = False
50 |
51 | if current.East == None:
52 | at_eastern_edge = True
53 | if current.North == None:
54 | at_northern_edge = True
55 |
56 | if at_eastern_edge or (at_northern_edge == False and random.randint(0, 1)==1):
57 | random_cell = random.choice(history)
58 | if random_cell.North:
59 | Grid.JoinAndDestroyWalls(random_cell, random_cell.North)
60 | history.clear()
61 | else:
62 | Grid.JoinAndDestroyWalls(current, current.East)
63 |
64 | self.grid.Show(screen, show_heuristic, show_color_map)
65 | current.isCurrent = False
66 | pygame.display.flip()
67 |
68 | if self.speed < 1:
69 | time.sleep(self.max_speed - min(self.speed, self.max_speed))
70 | self.isDone = True
71 |
72 | Update(self, screen, show_heuristic, show_color_map, show_path)
73 |
74 | if show_path:
75 | self.grid.Show(screen, show_heuristic, show_color_map,self.shortest_path)
76 | else:
77 | self.grid.Show(screen, show_heuristic, show_color_map, None)
78 |
79 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from constants import *
3 | from ui.colors import *
4 | import classes
5 |
6 | from ui.setup import *
7 | # Initialize pygame
8 | pygame.init()
9 | screen = pygame.display.set_mode(size)
10 | clock = pygame.time.Clock()
11 | fps = 30
12 |
13 | binary_tree = classes.BinaryTree(classes.Grid(rows, cols, cell_size), "GREEN")
14 | wilson = classes.Wilson(classes.Grid(rows, cols, cell_size), "PURPLE_E")
15 | side_winder = classes.SideWinder(classes.Grid(rows, cols, cell_size), "BLUE")
16 | hunt_and_kill = classes.HuntAndKill(classes.Grid(rows, cols, cell_size), "RED")
17 | aldous_broder = classes.AldousBroder(classes.Grid(rows, cols, cell_size), "GREEN")
18 | recursive_backtracker = classes.RecursiveBacktracker(classes.Grid(rows, cols, cell_size), "BLUE")
19 | kruskal = classes.Kruskals(classes.Kruskals.State(classes.Grid(rows, cols, cell_size)))
20 | simplePrims = classes.SimplePrims(classes.Grid(rows, cols, cell_size), "CYAN")
21 | prims = classes.Prims(classes.Grid(rows, cols, cell_size))
22 | growingTree = classes.GrowingTree(classes.Grid(rows, cols, cell_size), "GREEN")
23 | ellers = classes.Ellers(classes.Grid(rows, cols, cell_size), 0, "RED")
24 |
25 | show_text = False
26 | color_mode = False
27 | show_path = False
28 | start = False
29 | run = True
30 | while run:
31 | #screen.fill(black)
32 | # Set Caption and fps
33 | clock.tick(fps)
34 | frame_rate = int(clock.get_fps())
35 | pygame.display.set_caption(f"Maze Generator - FPS: {frame_rate}")
36 |
37 | # Handle events
38 | for event in pygame.event.get():
39 | if event.type == pygame.QUIT:
40 | run = False
41 | if event.type == pygame.KEYDOWN:
42 | if event.key == pygame.K_ESCAPE:
43 | run = False
44 | if event.key == pygame.K_RETURN:
45 | start = not start
46 | elif event.key == pygame.K_h:
47 | show_text = not show_text
48 | elif event.key == pygame.K_SPACE:
49 | color_mode = not color_mode
50 | elif event.key == pygame.K_s:
51 | show_path = not show_path
52 | if event.type == pygame.MOUSEBUTTONDOWN:
53 | if event.button == 1:
54 | rightMouseClicked = True
55 |
56 | if start:
57 | # wilson.Generate(screen, show_text, color_mode, show_path)
58 | # binary_tree.Generate(screen, show_text, color_mode, show_path)
59 | kruskal.Generate(screen, show_text, color_mode, show_path)
60 | # side_winder.Generate(screen, show_text, color_mode, show_path)
61 | # hunt_and_kill.Generate(screen, show_text, color_mode, show_path)
62 | # aldous_broder.Generate(screen, show_text, color_mode, show_path)
63 | # recursive_backtracker.Generate(screen, show_text, color_mode, show_path)
64 | # simplePrims.Generate(screen, show_text, color_mode, show_path)
65 | # prims.Generate(screen, show_text, color_mode, show_path)
66 | # growingTree.Generate(screen, show_text, color_mode, show_path)
67 | # ellers.Generate(screen, show_text, color_mode, show_path)
68 | else:
69 | PressEnter.Render(screen)
70 |
71 | pygame.display.flip()
72 |
73 | pygame.image.save(screen, "./images/path.png")
74 | pygame.quit()
75 |
--------------------------------------------------------------------------------
/classes/huntandkill.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | import random
3 | from classes.grid import Grid, Update
4 | from ui.colors import *
5 |
6 | """
7 | STEPS:
8 |
9 | 1. Choose a starting cell.
10 | 2. Perform a random walk, carving passages to unvisited neighbors,
11 | until the current cell has no unvisited neighbors.
12 | 3. Enter “hunt” mode, where you scan the grid looking for an unvisited cell that is adjacent to a visited cell.
13 | If found, carve a passage between the two and let the formerly unvisited cell be the new starting cell.
14 | 4. Repeat steps 2 and 3 until the hunt mode scans the entire grid and finds no unvisited cells.
15 | """
16 |
17 | class HuntAndKill:
18 | def __init__(self, grid, path_color):
19 | self.grid = grid
20 | self.rows = grid.rows
21 | self.cols = grid.cols
22 | self.path_color = path_color
23 | self.isDone = False
24 | self.starting_node = grid.cells[0][0]
25 | self.starting_node.isStartingNode = True
26 | self.end_node = grid.cells[self.grid.cols-1][self.grid.rows-1]
27 | self.end_node.isgoalNode = True
28 | if path_color == "HSV":
29 | self.grid.path_color = white
30 | self.shortest_path = None
31 |
32 | def Generate(self, screen, show_heuristic, show_color_map, show_path):
33 | if not self.isDone:
34 | randomX, randomY = random.randint(0, self.cols-1), random.randint(0, self.rows-1)
35 | current = self.grid.cells[randomX][randomY]
36 | while current:
37 | unvisited_neighbours = [cell for cell in current.neighbours if len(cell.connections) == 0]
38 | if len(unvisited_neighbours) > 0:
39 | neighbour = random.choice(unvisited_neighbours)
40 | neighbour.isCurrent = True
41 | Grid.JoinAndDestroyWalls(current, neighbour)
42 |
43 | self.grid.Show(screen, show_heuristic, show_color_map)
44 | pygame.display.flip()
45 |
46 | neighbour.isCurrent = False
47 | current = neighbour
48 |
49 | else:
50 | current = None
51 | for x in range(self.cols):
52 | for y in range(self.rows):
53 | this = self.grid.cells[x][y]
54 | visited_neighbours = [cell for cell in this.neighbours if len(cell.connections) > 0]
55 | if len(this.connections) == 0 and len(visited_neighbours) > 0:
56 | current = this
57 | neighbour = random.choice(visited_neighbours)
58 | neighbour.isCurrent = True
59 | Grid.JoinAndDestroyWalls(current, neighbour)
60 |
61 | self.grid.Show(screen, show_heuristic, show_color_map)
62 | neighbour.isCurrent = False
63 |
64 | pygame.display.flip()
65 | break
66 |
67 | self.isDone = True
68 | Update(self, screen, show_heuristic, show_color_map, show_path)
69 |
70 | if show_path:
71 | self.grid.Show(screen, show_heuristic, show_color_map,self.shortest_path)
72 | else:
73 | self.grid.Show(screen, show_heuristic, show_color_map, None)
74 |
75 |
--------------------------------------------------------------------------------
/classes/hexGrid.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | import math
3 | from ui.colors import *
4 | from classes.grid import Grid
5 | from classes.hexCell import HexCell
6 | from constants import *
7 |
8 | class HexGrid(Grid):
9 | def __init__(self, rows, cols, cell_size):
10 | super().__init__(rows, cols, cell_size)
11 |
12 | def PrepareGrid(self):
13 | for x in range(self.cols):
14 | for y in range(self.rows):
15 | self.cells[x][y] = HexCell(x, y, self.cell_size)
16 |
17 | def ConfigureCells(self):
18 | for x in range(self.cols):
19 | for y in range(self.rows):
20 | current = self.cells[x][y]
21 | row, col = current.x, current.y
22 | north_diagonal, south_diagonal = 0, 0
23 |
24 | if (col%2) == 0:
25 | north_diagonal = row - 1
26 | south_diagonal = row
27 | else:
28 | north_diagonal = row
29 | south_diagonal = row + 1
30 |
31 | if row - 1 >= 0:
32 | current.North = self.cells[row - 1][col]
33 | if north_diagonal >= 0 and north_diagonal < self.cols:
34 | if col - 1 >= 0 :
35 | current.NorthWest = self.cells[north_diagonal][col-1]
36 | if north_diagonal >= 0 and north_diagonal < self.cols:
37 | if col + 1 < self.rows:
38 | current.NorthEast = self.cells[north_diagonal][col+1]
39 |
40 | if row + 1 < self.cols:
41 | current.South = self.cells[row+1][col]
42 | if south_diagonal >= 0 and south_diagonal < self.cols:
43 | if col + 1 < self.rows :
44 | current.SouthEast = self.cells[south_diagonal][col+1]
45 | if south_diagonal >= 0 and south_diagonal < self.cols:
46 | if col - 1 >= 0 :
47 | current.SouthWest = self.cells[south_diagonal][col-1]
48 |
49 | def Show(self, screen, show_heuristic, show_color_map, shortest_path = None):
50 | a_size = self.cell_size /2
51 | b_size = self.cell_size * math.sqrt(3)/2
52 |
53 | w = self.cell_size * 2
54 | h = b_size * 2
55 |
56 | if not self.isSorted and shortest_path:
57 | for x in range(self.cols):
58 | for y in range(self.rows):
59 | if shortest_path.cells_record[x][y]:
60 | val = shortest_path.cells_record[x][y]
61 | self.path_values.append(val)
62 | self.path[str(val)] = self.cells[x][y]
63 | self.path_values = sorted(self.path_values)
64 | self.isSorted = True
65 |
66 | for x in range(self.cols):
67 | for y in range(self.rows):
68 | self.cells[x][y].show_text = show_heuristic
69 | self.cells[x][y].show_highlight = show_color_map
70 | self.cells[x][y].Draw(screen, show_color_map, a_size, b_size, w, h, self.rows, self.cols)
71 |
72 | if shortest_path:
73 | for i in range(len(self.path_values)-1):
74 | pygame.draw.line(screen, orange, self.path[str(self.path_values[i])].GetCenter(), self.path[str(self.path_values[i+1])].GetCenter(), 2)
75 | pygame.draw.circle(screen, orange, self.path[str(self.path_values[i])].GetCenter(), self.cell_size//6)
76 |
--------------------------------------------------------------------------------
/classes/hexCell.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from ui.colors import *
3 | from classes.cell import Cell
4 | from constants import *
5 |
6 | pygame.font.init()
7 | text_font = pygame.font.SysFont("Arial", cell_size//3)
8 |
9 | class HexCell(Cell):
10 | def __init__(self, x, y, size):
11 | super().__init__(x, y, size)
12 | self.NorthEast = None
13 | self.NorthWest = None
14 | self.SouthEast = None
15 | self.SouthWest = None
16 | self.centerX = 0
17 | self.centerY = 0
18 |
19 | def SetNeighbours(self):
20 | self.neighbours = []
21 | if self.North:
22 | self.neighbours.append(self.North)
23 | if self.South:
24 | self.neighbours.append(self.South)
25 | if self.NorthEast:
26 | self.neighbours.append(self.NorthEast)
27 | if self.NorthWest:
28 | self.neighbours.append(self.NorthWest)
29 | if self.SouthEast:
30 | self.neighbours.append(self.SouthEast)
31 | if self.SouthWest:
32 | self.neighbours.append(self.SouthWest)
33 |
34 | def GetCenter(self):
35 | return (self.centerX, self.centerY)
36 |
37 | def Draw(self, screen, show_color_map, a_size, b_size, w, h, r, c):
38 | cx = self.size + 3 * self.y * a_size
39 | cy = b_size + self.x * h
40 | cy += b_size if (self.y%2)!= 0 else 0
41 |
42 | x_farWest = (cx -self.size)
43 | x_nearWest = (cx-a_size)
44 | x_nearEast = (cx+a_size)
45 | x_farEast = (cx+self.size)
46 |
47 | y_north = (cy-b_size)
48 | y_middle = cy
49 | y_south = (cy+b_size)
50 |
51 | self.centerY = cy
52 | self.centerX = cx
53 |
54 | color = self.color
55 | if self.isStartingNode:
56 | color = yellow
57 | if self.isCurrent:
58 | color = navy_blue
59 | elif self.isgoalNode:
60 | color = blue
61 |
62 |
63 | if self.show_highlight and self.visited:
64 | points = [(x_farWest, y_middle), (x_nearWest, y_north),
65 | (x_nearEast, y_north), (x_farEast, y_middle),
66 | (x_nearEast, y_south), (x_nearWest, y_south)]
67 | pygame.draw.polygon(screen, self.highlight, points)
68 | elif self.visited:
69 | points = [(x_farWest, y_middle), (x_nearWest, y_north),
70 | (x_nearEast, y_north), (x_farEast, y_middle),
71 | (x_nearEast, y_south), (x_nearWest, y_south)]
72 | pygame.draw.polygon(screen, color, points)
73 | else:
74 | points = [(x_farWest, y_middle), (x_nearWest, y_north),
75 | (x_nearEast, y_north), (x_farEast, y_middle),
76 | (x_nearEast, y_south), (x_nearWest, y_south)]
77 | pygame.draw.polygon(screen, black, points)
78 |
79 | if self.SouthWest or self.y == 0 or (self.x == c-1 and self.y % 2 != 0):
80 | pygame.draw.line(screen, black, (x_farWest, y_middle), (x_nearWest, y_south), 4)
81 | if self.NorthWest or self.y == 0 or (self.x == 0 and self.y % 2 == 0):
82 | pygame.draw.line(screen, black, (x_farWest, y_middle), (x_nearWest, y_north), 4)
83 | if self.North or self.x == 0:
84 | pygame.draw.line(screen, black, (x_nearWest, y_north), (x_nearEast, y_north), 4)
85 |
86 | if self.NorthEast or self.y == r-1 or (self.x == 0 and self.y%2==0):
87 | pygame.draw.line(screen, black, (x_nearEast, y_north), (x_farEast, y_middle), 4)
88 | if self.SouthEast or self.y == r-1 or (self.x == c-1 and self.y%2!=0):
89 | pygame.draw.line(screen, black, (x_farEast, y_middle), (x_nearEast, y_south), 4)
90 | if self.South or self.x == c-1:
91 | pygame.draw.line(screen, black, (x_nearEast, y_south), (x_nearWest, y_south), 4)
92 |
93 | if self.show_text:
94 | text_surface = text_font.render(str(int(self.cost)), True, self.textColor)
95 | text_rect = text_surface.get_rect(center=(self.centerX , self.centerY))
96 | screen.blit(text_surface, text_rect)
97 |
--------------------------------------------------------------------------------
/classes/recursiveBacktracker.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | import time
3 | from classes.grid import Grid, Update
4 | from classes.mask import Mask, GridMask
5 | from classes.polarGrid import PolarGrid
6 | from classes.weightedGrid import WeightedGrid
7 | from classes.hexGrid import HexGrid
8 | from ui.colors import *
9 | import random
10 |
11 | """
12 | STEPS:
13 |
14 | 1. Choose a starting point in the field.
15 | 2. Randomly choose a wall at that point and carve a passage through to the adjacent cell,
16 | but only if the adjacent cell has not been visited yet. This becomes the new current cell.
17 | 3. If all adjacent cells have been visited, back up to the last cell that has uncarved walls and repeat.
18 | 4. The algorithm ends when the process has backed all the way up to the starting point.
19 | """
20 |
21 | class RecursiveBacktracker:
22 | def __init__(self, grid, path_color):
23 | self.grid = grid
24 | self.rows = grid.rows
25 | self.cols = grid.cols
26 | self.path_color = path_color
27 | self.isDone = False
28 | self.starting_node = None
29 | self.end_node = None
30 |
31 | if type(self.grid) == Grid:
32 | self.starting_node = grid.cells[0][0]
33 | self.starting_node.isStartingNode = True
34 | self.end_node = grid.cells[self.cols-1][self.rows-1]
35 | self.end_node.isgoalNode = True
36 | if path_color == "HSV":
37 | self.grid.path_color = white
38 | self.shortest_path = None
39 |
40 | def Generate(self, screen, show_heuristic, show_color_map, show_path):
41 | if not self.isDone:
42 | gridtype = "Normal"
43 | stack = []
44 | initial_cell = None
45 | if type(self.grid) == Grid or type(self.grid) == WeightedGrid:
46 | randomX = random.randint(0, self.cols-1)
47 | randomY = random.randint(0, self.rows-1)
48 | initial_cell = self.grid.cells[randomX][randomY]
49 | elif type(self.grid) == PolarGrid:
50 | gridtype = "Polar"
51 | initial_cell = self.grid.GetRandomCell()
52 | elif type(self.grid) == GridMask:
53 | initial_cell = self.grid.GetRandomCell()
54 | elif type(self.grid) == HexGrid:
55 | gridtype = "Hex"
56 | randomX = random.randint(0, self.cols-1)
57 | randomY = random.randint(0, self.rows-1)
58 | initial_cell = self.grid.cells[randomX][randomY]
59 |
60 | stack.append(initial_cell)
61 |
62 | while len(stack) > 0:
63 | current = stack[-1]
64 | neighbours = []
65 | if type(self.grid) == PolarGrid or type(self.grid) == HexGrid:
66 | current.SetNeighbours()
67 | neighbours = [cell for cell in current.neighbours if len(cell.connections) == 0]
68 | else:
69 | neighbours = [cell for cell in current.neighbours if len(cell.connections) == 0]
70 |
71 | if len(neighbours) == 0:
72 | stack.pop()
73 | else:
74 | neighbour = random.choice(neighbours)
75 | Grid.JoinAndDestroyWalls(current, neighbour, gridtype)
76 | neighbour.isCurrent = True
77 | self.grid.Show(screen, show_heuristic, show_color_map)
78 | pygame.display.flip()
79 | neighbour.isCurrent = False
80 |
81 | stack.append(neighbour)
82 |
83 | self.isDone = True
84 | if type(self.grid) != PolarGrid:
85 | Update(self, screen, show_heuristic, show_color_map, show_path)
86 | if type(self.grid) != PolarGrid:
87 | if show_path:
88 | self.grid.Show(screen, show_heuristic, show_color_map,self.shortest_path)
89 | else:
90 | self.grid.Show(screen, show_heuristic, show_color_map, None)
91 | else:
92 | self.grid.Show(screen, show_heuristic, show_color_map, None)
93 | pygame.display.flip()
94 |
--------------------------------------------------------------------------------
/classes/cell.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from ui.colors import *
3 | from classes.heuristic import Heuristic
4 | from constants import *
5 |
6 | pygame.font.init()
7 | text_font = pygame.font.SysFont("Arial", cell_size//3)
8 | offset = 0
9 |
10 | class Cell:
11 | def __init__(self, x, y, size=40):
12 | self.x = x
13 | self.y = y
14 |
15 | self.cost = 0
16 | self.isgoalNode = False
17 | self.isStartingNode = False
18 | self.isCurrent = False
19 | self.isPath = False
20 | self.show_path = False
21 | self.highlight = white
22 |
23 | self.show_highlight = False
24 | self.size = size
25 | self.color= white
26 | self.wall_color= black
27 | self.wall_thickness = 4
28 | self.visited = False
29 | self.connections = []
30 | self.neighbours = []
31 | self.isAvailable = True
32 | # Walls -- neighbours
33 | self.North = None
34 | self.South = None
35 | self.East = None
36 | self.West = None
37 |
38 | self.textColor = (0, 0, 0)
39 | self.show_text = True
40 |
41 | def CalculateHeuristic(self, rows, cols):
42 | h_distances = Heuristic(rows, cols)
43 | frontier = [self]
44 | while len(frontier) > 0:
45 | new_frontier = []
46 | for c in frontier:
47 | for cell in c.connections:
48 | if h_distances.GetRecord(cell):
49 | continue
50 | val = 0 if h_distances.GetRecord(c) == None else h_distances.GetRecord(c)
51 | h_distances.SetRecord(cell, val+1)
52 | new_frontier.append(cell)
53 |
54 | frontier = new_frontier
55 | h_distances.SetRecord(self, 0)
56 | return h_distances
57 |
58 | def IsConneted(self, cell):
59 | if cell != None:
60 | if cell in self.connections and self in cell.connections:
61 | return True
62 | else:
63 | return False
64 |
65 | def Draw(self, screen, rows, cols):
66 | x = self.x * self.size
67 | y = self.y * self.size
68 |
69 | if not self.visited or not self.isAvailable:
70 | pygame.draw.rect(screen, black, [x, y, self.size-offset, self.size-offset])
71 | else:
72 | color = self.color
73 | if self.isStartingNode:
74 | color = yellow
75 | if self.isCurrent:
76 | color = navy_blue
77 | elif self.isgoalNode:
78 | color = blue
79 | pygame.draw.rect(screen, color, [x, y, self.size-offset, self.size-offset])
80 |
81 | if self.show_highlight:
82 | pygame.draw.rect(screen, self.highlight, [x, y, self.size-offset, self.size-offset])
83 |
84 | if self.North != None or self.y - 1 < 0:
85 | A = (x, y)
86 | B = (x + self.size, y)
87 | pygame.draw.line(screen, self.wall_color, A, B, self.wall_thickness)
88 | if self.South != None or self.y + 1 >= rows:
89 | A = (x, y + self.size)
90 | B = (x + self.size, y + self.size)
91 | pygame.draw.line(screen, self.wall_color, A, B, self.wall_thickness)
92 | if self.East != None or self.x + 1 >= cols:
93 | A = (x + self.size, y)
94 | B = (x + self.size, y + self.size)
95 | pygame.draw.line(screen, self.wall_color, A, B, self.wall_thickness)
96 | if self.West != None or self.x - 1 < 0:
97 | A = (x, y)
98 | B = (x, y + self.size)
99 | pygame.draw.line(screen, self.wall_color, A, B, self.wall_thickness)
100 |
101 | if self.show_text:
102 | text_surface = text_font.render(str(int(self.cost)), True, self.textColor)
103 | text_rect = text_surface.get_rect(center=(x + self.size//2, y + self.size/2))
104 | screen.blit(text_surface, text_rect)
105 |
106 | def __repr__(self):
107 | # DEBUG
108 | return f"({self.x}, {self.y}, {id(self)})"
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Procedural-Maze-Generator-Algorithms and Maze solver
2 |
3 | Check out my youtube channel : [Auctux](https://www.youtube.com/c/Auctux)
4 |
5 | ### Ressources
6 | - Thanks to Jamis Buck Book : [Mazes for programmers](https://www.amazon.in/gp/product/B013HA1UY4/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=auctux-21&creative=24630&linkCode=as2&creativeASIN=B013HA1UY4&linkId=cd1ab49c7f656324511f624fefe1ecda)
7 |
8 | ### Requirements
9 | > ***PYGAME*** : pip install pygame
10 |
11 | ### Commands Controls:
12 | - `Esc` to close the program
13 | - `Enter` to start to genrate the maze
14 | - `H` to show the heuristic cost value
15 | - `S` to show the path from the starting to the goal node
16 | - `Space` to switch between the color modes
17 |
18 | ## files
19 | - run ***main.py*** for grid mazes
20 | - run ***polarMaze.py*** for polarGrid maze
21 | - run ***hexMaze.py*** for hexGrid maze
22 | - run ***imageMaze.py*** for maskGrid maze
23 | - run ***weightedMaze.py*** for weightedGrid maze
24 |
25 | > you can also ***braid*** your mazes when it done , by calling the function `grid.Braid()`.
26 | ***braiding a maze*** simply assures that the maze doens't have any deadends
27 |
28 | ## colors modes
29 | Mode 1 :
30 |
31 | Mode 2 :
32 |
33 |
34 | ## other Maze Grid:
35 | Polar Grid :
36 |
37 | HexGrid :
38 |
39 | maskGrid :
40 |
41 |
42 | ## Show heuristic and path
43 | - heuristic :
44 | - showPath :
45 |
46 | ## Bugs & Unsolved Issues
47 | - The code need a lot of refactoring
48 | - the visualization of the polar maze need a lot of improvement
49 | - the the imagemaze.py file need to be refactored for it to be able to work on every size of images , for now it's only working for images wich has the same size with the screen size
50 | - the recursive backtracker class need a little bit of cleaning
51 | - add Comments
52 | - implement the ui interface
53 | - the djikistra algorithm need to take the weights of cells in consideration for weightedGrid
54 | ## mazes
55 | ---
56 | ```python:main.py
57 | initialize:
58 | binary_tree = BinaryTree(Grid(rows, cols, cell_size), "GREEN")
59 | wilson = Wilson(Grid(rows, cols, cell_size), "PURPLE_E")
60 | side_winder = SideWinder(Grid(rows, cols, cell_size), "BLUE")
61 | hunt_and_kill = HuntAndKill(Grid(rows, cols, cell_size), "RED")
62 | aldous_broder = AldousBroder(Grid(rows, cols, cell_size), "GREEN")
63 | recursive_backtracker = RecursiveBacktracker(Grid(rows, cols, cell_size), "BLUE")
64 | kruskal = Kruskals(Kruskals.State(Grid(rows, cols, cell_size)))
65 | simplePrims = SimplePrims(Grid(rows, cols, cell_size), "CYAN")
66 | prims = Prims(Grid(rows, cols, cell_size))
67 | growingTree = GrowingTree(Grid(rows, cols, cell_size), "GREEN")
68 | ellers = Ellers(Grid(rows, cols, cell_size), 0, "RED")
69 | mainloop():
70 | wilson.Generate(screen, show_text, color_mode, show_path)
71 | binary_tree.Generate(screen, show_text, color_mode, show_path)
72 | kruskal.Generate(screen, show_text, color_mode, show_path)
73 | side_winder.Generate(screen, show_text, color_mode, show_path)
74 | hunt_and_kill.Generate(screen, show_text, color_mode, show_path)
75 | aldous_broder.Generate(screen, show_text, color_mode, show_path)
76 | recursive_backtracker.Generate(screen, show_text, color_mode, show_path)
77 | simplePrims.Generate(screen, show_text, color_mode, show_path)
78 | prims.Generate(screen, show_text, color_mode, show_path)
79 | growingTree.Generate(screen, show_text, color_mode, show_path)
80 | ellers.Generate(screen, show_text, color_mode, show_path)
81 |
82 | ```
83 | ---
84 |
85 | - Aldous Broder
86 |
87 | - Binary Tree
88 |
89 | - Eller's Algorithm
90 |
91 | - Growing Tree
92 |
93 | - Hunt And kill
94 |
95 | - Kruskal's algorithm
96 |
97 | - Prims Algorithm
98 |
99 | - Simplified Prims
100 |
101 | - Wilson algorithm
102 |
103 | - Sidewinder Algorithm
104 |
105 | - Recursive Backtracker
106 |
107 |
108 |
109 | Enjoy ✌️
110 |
111 |
--------------------------------------------------------------------------------
/classes/kruskal.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from classes.grid import Grid, Update
3 | import random
4 |
5 | """
6 | STEPS:
7 |
8 | 1. Throw all of the edges in the graph into a big burlap sack.
9 | 2. Pull out the edge with the lowest weight. If the edge connects two disjoint trees,
10 | join the trees. Otherwise, throw that edge away.
11 | 3. Repeat until there are no more edges left.
12 |
13 | """
14 |
15 | class Kruskals:
16 | def __init__(self, state, path_color='BLUE'):
17 | self.state = state
18 | self.grid = state.grid
19 | self.isDone = False
20 | self.cols = self.grid.cols
21 | self.rows = self.grid.rows
22 | self.starting_node = self.grid.cells[0][0]
23 | self.starting_node.isStartingNode = True
24 | self.end_node = self.grid.cells[self.grid.cols-1][self.grid.rows-1]
25 | self.end_node.isgoalNode = True
26 | self.show_path = True
27 | self.path_color = path_color
28 | self.shortest_path = None
29 | if self.path_color == "HSV":
30 | self.grid.path_color = white
31 | class State:
32 | def __init__(self, grid):
33 | self.grid = grid
34 | self.cols = grid.cols
35 | self.rows = grid.rows
36 | self.neighbours = []
37 | self.cells_set = {}
38 | self.cells_in_set = {}
39 | for x in range(self.cols):
40 | for y in range(self.rows):
41 | set = len(self.cells_set)
42 | self.cells_set[self.grid.cells[x][y]] = set
43 | self.cells_in_set[set] = [self.grid.cells[x][y]]
44 |
45 | if self.grid.cells[x][y].South:
46 | self.neighbours.append([self.grid.cells[x][y], self.grid.cells[x][y].South])
47 | if self.grid.cells[x][y].East:
48 | self.neighbours.append([self.grid.cells[x][y], self.grid.cells[x][y].East])
49 | def CanMerge(self, left, right):
50 | return (self.cells_set[left] != self.cells_set[right])
51 |
52 | def Merge(self, left, right):
53 | Grid.JoinAndDestroyWalls(left ,right)
54 | winner = self.cells_set[left]
55 | loser = self.cells_set[right]
56 | losers = None
57 | if loser in self.cells_in_set:
58 | losers = self.cells_in_set[loser]
59 | else:
60 | losers = [right]
61 |
62 | for l in losers:
63 | self.cells_in_set[winner].append(l)
64 | self.cells_set[l] = winner
65 | del self.cells_in_set[loser]
66 |
67 | def AddCrossing(self, cell):
68 | if (len(cell.connections) > 0 or
69 | not self.CanMerge(cell.East, cell.West) or
70 | not self.CanMerge(cell.North, cell.South)):
71 | return False
72 |
73 | for c in self.neighbours:
74 | left, right = c[0], c[1]
75 | if left == cell or right == cell:
76 | self.neighbours.remove(c)
77 |
78 | if random.randint(0, 1) == 0:
79 | if cell.West != None:
80 | self.Merge(cell.West, cell)
81 | if cell.East != None:
82 | self.Merge(cell, cell.East)
83 |
84 | if cell.North and cell.North.East:
85 | self.Merge(cell.North, cell.North.East)
86 | if cell.South and cell.South.North:
87 | self.Merge(cell.South, cell.South.North)
88 | else:
89 | if cell.North:
90 | self.Merge(cell.North, cell)
91 | if cell.South:
92 | self.Merge(cell, cell.South)
93 |
94 | if cell.West and cell.West.East:
95 | self.Merge(cell.West, cell.West.East)
96 | self.Merge(cell.East, cell.East.West)
97 |
98 | def Generate(self, screen, show_heuristic, show_color_map,show_path):
99 | if not self.isDone:
100 | neighbours = self.state.neighbours
101 | random.shuffle(neighbours)
102 | while len(neighbours) > 0:
103 | poped = neighbours.pop()
104 | left, right = poped[0], poped[1]
105 |
106 | if self.state.CanMerge(left, right):
107 | self.state.Merge(left, right)
108 | self.grid.Show(screen, show_heuristic, show_color_map)
109 | pygame.display.flip()
110 | self.isDone = True
111 | Update(self, screen, show_heuristic, show_color_map, show_path)
112 |
113 | if show_path:
114 | self.grid.Show(screen, show_heuristic, show_color_map, self.shortest_path)
115 | else:
116 | self.grid.Show(screen, show_heuristic, show_color_map)
117 |
118 |
--------------------------------------------------------------------------------
/classes/prims.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from classes.grid import Grid, Update
3 | import random
4 |
5 | """
6 | STEPS:
7 |
8 | 1. Choose an arbitrary cell from G (the grid),
9 | and add it to some (initially empty) set V.
10 | 2. Choose the edge with the smallest weight from G,
11 | that connects a vertex in V with another cell not in V.
12 | 3. Add that edge to the minimal spanning tree, and the edge’s other cell to V.
13 | 4. Repeat steps 2 and 3 until V includes every vertex in G.
14 |
15 | """
16 |
17 | class SimplePrims:
18 | def __init__(self, grid, path_color="BLUE"):
19 | self.grid = grid
20 | self.cols = grid.cols
21 | self.rows = grid.rows
22 | self.isDone = False
23 | self.starting_node = grid.cells[0][0]
24 | self.starting_node.isStartingNode = True
25 | self.end_node = grid.cells[self.cols-1][self.rows-1]
26 | self.end_node.isgoalNode = True
27 | self.show_path = True
28 | self.path_color = path_color
29 | self.shortest_path = None
30 | if path_color == "HSV":
31 | self.grid.path_color = white
32 |
33 | def Generate(self, screen, show_heuristic, show_color_map,show_path):
34 | if not self.isDone:
35 | active = []
36 | rx = random.randint(0, self.cols-1)
37 | ry = random.randint(0, self.rows-1)
38 | start_at = self.grid.cells[rx][ry]
39 | active.append(start_at)
40 |
41 | while len(active) > 0:
42 | cell = random.choice(active)
43 | available_neighbours = []
44 | for c in cell.neighbours:
45 | if len(c.connections) == 0:
46 | available_neighbours.append(c)
47 |
48 | if len(available_neighbours) > 0:
49 | neighbour = random.choice(available_neighbours)
50 |
51 | neighbour.isCurrent = True
52 | Grid.JoinAndDestroyWalls(cell, neighbour)
53 | self.grid.Show(screen, show_heuristic, show_color_map)
54 | pygame.display.flip()
55 | neighbour.isCurrent = False
56 | active.append(neighbour)
57 | else:
58 | active.remove(cell)
59 | self.isDone = True
60 | Update(self, screen, show_heuristic, show_color_map, show_path)
61 |
62 | if show_path:
63 | self.grid.Show(screen, show_heuristic, show_color_map, self.shortest_path)
64 | else:
65 | self.grid.Show(screen, show_heuristic, show_color_map)
66 |
67 |
68 | class Prims(SimplePrims):
69 | def __init__(self, grid, path_color="RED"):
70 | super().__init__(grid, path_color)
71 |
72 | def Generate(self, screen, show_heuristic, show_color_map,show_path):
73 | if not self.isDone:
74 | active = []
75 | rx = random.randint(0, self.cols-1)
76 | ry = random.randint(0, self.rows-1)
77 | start_at = self.grid.cells[rx][ry]
78 | active.append(start_at)
79 |
80 | costs = {}
81 | for x in range(self.cols):
82 | for y in range(self.rows):
83 | costs[self.grid.cells[x][y]] = random.randint(0, 99)
84 | while len(active) > 0:
85 | current = None
86 | min1 = float("inf")
87 | for i in range(len(active)):
88 | if costs[active[i]] < min1:
89 | current = active[i]
90 | min1 = costs[active[i]]
91 | available_neighbours = []
92 | for c in current.neighbours:
93 | if len(c.connections) == 0:
94 | available_neighbours.append(c)
95 | if len(available_neighbours) > 0:
96 | neighbour = None
97 | min2 = float("inf")
98 | for i in range(len(available_neighbours)):
99 | if costs[available_neighbours[i]] < min2:
100 | neighbour = available_neighbours[i]
101 | min2 = costs[available_neighbours[i]]
102 |
103 | neighbour.isCurrent = True
104 | Grid.JoinAndDestroyWalls(current, neighbour)
105 | self.grid.Show(screen, show_heuristic, show_color_map)
106 | pygame.display.flip()
107 | neighbour.isCurrent = False
108 |
109 | active.append(neighbour)
110 | else:
111 | active.remove(current)
112 | self.isDone = True
113 | Update(self, screen, show_heuristic, show_color_map, show_path)
114 |
115 | if show_path:
116 | self.grid.Show(screen, show_heuristic, show_color_map, self.shortest_path)
117 | else:
118 | self.grid.Show(screen, show_heuristic, show_color_map)
119 |
120 |
--------------------------------------------------------------------------------
/classes/ellers.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | import random
3 | from classes.grid import Grid, Update
4 |
5 | """
6 | STEPS:
7 | 1. Initialize the cells of the first row to each exist in their own set.
8 | 2. Randomly join adjacent cells, but only if they are not in the same set.
9 | When joining adjacent cells, merge the cells of both sets into a single set,
10 | indicating that all cells in both sets are now connected.
11 | 3. For each set, randomly create vertical connections downward to the next row.
12 | Each remaining set must have at least one vertical connection.
13 | The cells in the next row thus connected must share the set of the cell above them.
14 | 4. Flesh out the next row by putting any remaining cells into their own sets.
15 | 5. Repeat until the last row is reached.
16 | 6. For the last row, join all adjacent cells that do not share a set, and omit the vertical connections, and you’re done!
17 | """
18 |
19 | class Ellers:
20 | def __init__(self, grid,starting_set=0, path_color="BLUE"):
21 | self.grid = grid
22 | self.cols = grid.cols
23 | self.rows = grid.rows
24 | self.isDone = False
25 | self.starting_node = grid.cells[0][0]
26 | self.starting_node.isStartingNode = True
27 | self.end_node = grid.cells[self.cols-1][self.rows-1]
28 | self.end_node.isgoalNode = True
29 | self.show_path = True
30 | self.path_color = path_color
31 | self.shortest_path = None
32 | if path_color == "HSV":
33 | self.grid.path_color = white
34 | self.starting_set = starting_set
35 |
36 | def Generate(self, screen, show_heuristic, show_color_map,show_path):
37 | if not self.isDone:
38 | row_state = State(self.starting_set, self.cols)
39 |
40 | for y in range(self.rows):
41 | for x in range(self.cols):
42 | cell = self.grid.cells[x][y]
43 | if not cell.West:
44 | continue
45 |
46 | _set = row_state.SetFor(cell)
47 | old_set = row_state.SetFor(cell.West)
48 |
49 | if _set != old_set:
50 | if cell.South == None or random.randint(0, 1) == 0:
51 | temp = cell.West
52 | temp.isCurrent = True
53 | Grid.JoinAndDestroyWalls(cell, temp)
54 | self.grid.Show(screen, show_heuristic, show_color_map)
55 | pygame.display.flip()
56 | temp.isCurrent = False
57 |
58 | row_state.Merge(old_set, _set)
59 |
60 | if self.grid.cells[0][y].South:
61 | next_row = row_state.Next()
62 |
63 | for set in row_state.cells_in_set:
64 | cells = row_state.cells_in_set[set].copy()
65 | random.shuffle(cells)
66 | for _index, _cell in enumerate(cells):
67 | if _index == 0 or random.randint(0,2) == 0:
68 | if _cell.South:
69 | temp = _cell.South
70 | temp.isCurrent = True
71 | Grid.JoinAndDestroyWalls(_cell, temp)
72 | self.grid.Show(screen, show_heuristic, show_color_map)
73 | pygame.display.flip()
74 | temp.isCurrent = False
75 |
76 | next_row.Record(row_state.SetFor(_cell), temp)
77 | row_state = next_row
78 | self.isDone = True
79 | Update(self, screen, show_heuristic, show_color_map, show_path)
80 |
81 | if show_path:
82 | self.grid.Show(screen, show_heuristic, show_color_map, self.shortest_path)
83 | else:
84 | self.grid.Show(screen, show_heuristic, show_color_map)
85 | pygame.display.flip()
86 |
87 | class State:
88 | def __init__(self, starting_set, rows):
89 | self.cols = rows
90 | self.cells_in_set = {}
91 | self.set_for_cell = [None for x in range(self.cols)]
92 | self.next_set = starting_set
93 |
94 | def Record(self, set, cell):
95 | self.set_for_cell[cell.x] = set
96 | if set not in self.cells_in_set:
97 | self.cells_in_set[set] = []
98 | self.cells_in_set[set].append(cell)
99 |
100 | def SetFor(self, cell):
101 | if not self.set_for_cell[cell.x]:
102 | self.Record(self.next_set, cell)
103 | self.next_set += 1
104 |
105 | return self.set_for_cell[cell.x]
106 |
107 | def Merge(self, winner, loser):
108 | for cell in self.cells_in_set[loser]:
109 | self.set_for_cell[cell.x] = winner
110 | self.cells_in_set[winner].append(cell)
111 |
112 | del self.cells_in_set[loser]
113 | def Next(self):
114 | return State(self.next_set, self.cols)
115 |
--------------------------------------------------------------------------------
/classes/grid.py:
--------------------------------------------------------------------------------
1 | from classes.cell import *
2 | from ui.colors import *
3 | from classes.color import GridColor
4 | import random
5 |
6 | class Grid:
7 | def __init__(self, rows, cols, cell_size):
8 | self.rows = rows
9 | self.cols = cols
10 | self.cell_size = cell_size
11 | # Initialize cells
12 | self.cells = [[Cell(x, y, cell_size) for y in range(rows)] for x in range(cols)]
13 | self.PrepareGrid()
14 | self.heuristics = None
15 | self.path_color = orange
16 | self.isSorted = False
17 | self.path = {}
18 | self.path_values = []
19 |
20 | def Flatten(self):
21 | flat_grid = []
22 | for x in range(self.cols):
23 | for y in range(self.rows):
24 | if self.cells[x][y]:
25 | flat_grid.append(self.cells[x][y])
26 | return flat_grid
27 |
28 | def PrepareGrid(self):
29 | for x in range(self.cols):
30 | for y in range(self.rows):
31 | if self.cells[x][y]:
32 | self.cells[x][y].neighbours = []
33 | # East neighbour cell
34 | if x+1 < self.cols and self.cells[x+1][y]:
35 | self.cells[x][y].East = self.cells[x+1][y]
36 | self.cells[x][y].neighbours.append(self.cells[x+1][y])
37 | # West neightbour cell
38 | if x-1 >= 0 and self.cells[x-1][y]:
39 | self.cells[x][y].West = self.cells[x-1][y]
40 | self.cells[x][y].neighbours.append(self.cells[x-1][y])
41 |
42 | # North neighbour cell
43 | if y-1 >= 0 and self.cells[x][y-1]:
44 | self.cells[x][y].North = self.cells[x][y-1]
45 | self.cells[x][y].neighbours.append(self.cells[x][y-1])
46 | # South neighbour cell
47 | if y+1 < self.rows and self.cells[x][y+1]:
48 | self.cells[x][y].South = self.cells[x][y+1]
49 | self.cells[x][y].neighbours.append(self.cells[x][y+1])
50 |
51 | def JoinAndDestroyWalls(A, B, gridType="Normal"):
52 | if A.isAvailable and B.isAvailable:
53 | A.visited = True
54 | B.visited = True
55 | A.connections.append(B)
56 | B.connections.append(A)
57 | # ---- Grid & Mask Grid -----
58 | if gridType == "Normal":
59 | if A.North == B:
60 | A.North, B.South = None, None
61 | elif A.South == B:
62 | A.South, B.North = None, None
63 | elif A.East == B:
64 | A.East, B.West = None, None
65 | elif A.West == B:
66 | A.West, B.East = None, None
67 | # ---- Polar Grid -------
68 | elif gridType == "Polar":
69 | if A.inward == B :
70 | A.inward = None
71 | B.outward.remove(A)
72 | elif B.inward == A:
73 | B.inward = None
74 | A.outward.remove(B)
75 | elif A.clockwise == B or B.c_clockwise == A:
76 | A.clockwise = None
77 | B.c_clockwise = None
78 | elif A.c_clockwise == B or B.clockwise == A:
79 | A.c_clockwise = None
80 | B.clockwise = None
81 | # ----- Hex Grid -------
82 | elif gridType == "Hex":
83 | if A.North == B:
84 | A.North, B.South = None, None
85 | elif A.South == B:
86 | A.South, B.North = None, None
87 | elif A.SouthEast == B:
88 | A.SouthEast, B.NorthWest = None, None
89 | elif A.SouthWest == B:
90 | A.SouthWest, B.NorthEast = None, None
91 | elif A.NorthEast == B:
92 | A.NorthEast, B.SouthWest = None, None
93 | elif A.NorthWest == B:
94 | A.NorthWest, B.SouthEast = None, None
95 |
96 | def Deadends(self):
97 | deadends_set = []
98 | for x in range(len(self.cells)):
99 | for y in range(len(self.cells[x])):
100 | if len(self.cells[x][y].connections) == 1:
101 | deadends_set.append(self.cells[x][y])
102 | return deadends_set
103 |
104 | def Braid(self, p=1):
105 | deadends = self.Deadends()
106 | random.shuffle(deadends)
107 |
108 | for cell in deadends:
109 | if len(cell.connections) != 1 or random.uniform(0, 1) > p:
110 | continue
111 | neighbours = [c for c in cell.neighbours if c not in cell.connections]
112 | best = []
113 | for c in neighbours:
114 | if len(c.connections ) == 1:
115 | best.append(c)
116 | if len(best) == 0:
117 | best = neighbours
118 |
119 | neighbour = random.choice(best)
120 | Grid.JoinAndDestroyWalls(cell, neighbour)
121 |
122 |
123 | def Show(self, screen, show_heuristic, show_color_map, shortest_path = None):
124 | if not self.isSorted and shortest_path:
125 | for x in range(self.cols):
126 | for y in range(self.rows):
127 | if shortest_path.cells_record[x][y]:
128 | val = shortest_path.cells_record[x][y]
129 | self.path_values.append(val)
130 | self.path[str(val)] = ((x+0.5)*self.cell_size, (y+0.5)*self.cell_size)
131 | self.path_values = sorted(self.path_values)
132 | self.isSorted = True
133 |
134 | for x in range(self.cols):
135 | for y in range(self.rows):
136 | if self.cells[x][y]:
137 | self.cells[x][y].show_text = show_heuristic
138 | self.cells[x][y].show_highlight = show_color_map
139 | self.cells[x][y].Draw(screen, self.rows, self.cols)
140 |
141 | if shortest_path:
142 | for i in range(len(self.path_values)-1):
143 | pygame.draw.line(screen, orange, self.path[str(self.path_values[i])], self.path[str(self.path_values[i+1])], 2)
144 | pygame.draw.circle(screen, orange, self.path[str(self.path_values[i])], self.cell_size//6)
145 |
146 | def Update(self, screen, show_heuristic, show_color_map, show_path):
147 | # Calculate the step of each cell from the starting node
148 | # it's gonna initialize a grid that store the cost of each cell
149 | # from the starting node
150 | h_distances = self.starting_node.CalculateHeuristic(self.grid.rows, self.grid.cols)
151 | self.grid.heuristics = h_distances
152 | for x in range(len(self.grid.cells)):
153 | for y in range(len(self.grid.cells[x])):
154 | if self.grid.cells[x][y]:
155 | self.grid.cells[x][y].cost = 0 if self.grid.heuristics.cells_record[x][y] == None else self.grid.heuristics.cells_record[x][y]
156 |
157 | # get the path from the goad node to the starting node
158 | shortest_path = h_distances.BacktrackPath(self.end_node, self.starting_node)
159 | for x in range(self.grid.cols):
160 | for y in range(self.grid.rows):
161 | if self.grid.cells[x][y]:
162 | # check if the cell is in the path grid
163 | # If it is then set it as path
164 | if shortest_path.GetRecord(self.grid.cells[x][y]):
165 | self.grid.cells[x][y].isPath = True
166 |
167 | colorGridShortestPath = GridColor(self.path_color)
168 | colorGridShortestPath.distances(shortest_path, self.end_node, self.starting_node, self.grid)
169 |
170 | temp_path = h_distances.Merge(shortest_path)
171 |
172 | colorGridMap = GridColor(self.path_color)
173 | colorGridMap.distances(temp_path, self.end_node, self.starting_node, self.grid)
174 |
175 | for x in range(self.grid.cols):
176 | for y in range(self.grid.rows):
177 | if self.grid.cells[x][y]:
178 | self.grid.cells[x][y].highlight = colorGridShortestPath.UpdateColor(self.grid.cells[x][y])
179 | self.grid.cells[x][y].color = colorGridMap.UpdateColor(self.grid.cells[x][y])
180 |
181 | self.grid.Show(screen, show_heuristic, show_color_map)
182 | pygame.display.flip()
183 | self.shortest_path = shortest_path
184 |
--------------------------------------------------------------------------------
/ui/ui.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from constants import width, height
3 |
4 | def translate(value, min1, max1, min2, max2):
5 | return min2 + (max2-min2)*((value-min1)/(max1-min1))
6 |
7 | class Button:
8 | def __init__(self, text, position = (width-230, 400) , w = 100, h= 50, border=0, color = (0, 0, 0), borderColor = (0, 0, 0)):
9 | self.text = text
10 | self.position = position
11 | self.w = w
12 | self.h = h
13 | self.border = border
14 | self.temp = color
15 | self.color = color
16 | self.borderColor = borderColor
17 | self.font = 'freesansbold.ttf'
18 | self.fontSize = 25
19 | self.textColor = (255, 255, 255)
20 | self.state = False
21 | self.action = None
22 | self.clicked = False
23 |
24 | def HandleMouse(self,mouseClicked, HoverColor = (50, 120, 140)):
25 | m = pygame.mouse.get_pos()
26 | if m[0] >= self.position[0] and m[0] <= self.position[0] + self.w:
27 | if m[1] >= self.position[1] and m[1] <= self.position[1] + self.h:
28 | self.color = HoverColor
29 | if mouseClicked :
30 | self.color = (200, 200, 200)
31 | if self.action == None and mouseClicked == True:
32 | self.state = not self.state
33 | else:
34 | self.color = self.temp
35 | else:
36 | self.color = self.temp
37 |
38 |
39 | def Render(self, screen, mouseClicked, checker=True):
40 | if checker:
41 | self.HandleMouse(mouseClicked)
42 | font = pygame.font.Font(self.font, self.fontSize)
43 | text = font.render(self.text, True, self.textColor)
44 | textRect = text.get_rect()
45 | textRect.center = (self.position[0]+self.w//2, self.position[1]+self.h//2)
46 | if self.border > 0:
47 | pygame.draw.rect(screen, self.borderColor, pygame.Rect(self.position[0] - self.border//2, self.position[1] - self.border//2, self.w + self.border, self.h + self.border))
48 | pygame.draw.rect(screen, self.color, pygame.Rect(self.position[0], self.position[1], self.w, self.h))
49 |
50 | screen.blit(text, textRect)
51 |
52 | class Panel:
53 | def __init__(self, position = (width-400, 90), w= 385, h= 800, color=(2, 3, 12), alpha=100):
54 | self.position = position
55 | self.w = w
56 | self.h = h
57 | self.color = color
58 | self.alpha = alpha
59 |
60 | def Render(self, screen):
61 | s = pygame.Surface((self.w, self.h))
62 | s.set_alpha(self.alpha)
63 | s.fill(self.color)
64 | screen.blit(s, (self.position[0], self.position[1]))
65 | #pygame.draw.rect(screen, self.color, pygame.Rect(self.position[0], self.position[1], self.w, self.h))
66 |
67 | class ToggleButton:
68 | def __init__(self, position= ((width-200, 400)), w = 30, h=30, state=False, color=(20, 40, 50), activeColor=(240, 140, 200)):
69 | self.position = position
70 | self.w = w
71 | self.h = h
72 | self.clicked = False
73 | self.state = state
74 | self.temp = (activeColor, color)
75 | self.activeColor = activeColor
76 | self.color = color
77 |
78 | def HandleMouse(self, HoverColor = (50, 120, 140)):
79 | m = pygame.mouse.get_pos()
80 |
81 | if m[0] >= self.position[0] and m[0] <= self.position[0] + self.w:
82 | if m[1] >= self.position[1] and m[1] <= self.position[1] + self.h:
83 | self.color = HoverColor
84 | self.activeColor = HoverColor
85 | if pygame.mouse.get_pressed()[0]:
86 | self.color = (255, 255, 255)
87 | if self.clicked:
88 | self.state = not self.state
89 | else:
90 | self.color = self.temp[1]
91 | self.activeColor =self.temp[0]
92 | else:
93 | self.color = self.temp[1]
94 | self.activeColor =self.temp[0]
95 |
96 | def Render(self, screen, clicked):
97 | self.HandleMouse()
98 | self.clicked = clicked
99 | if self.state == True:
100 | pygame.draw.rect(screen, self.activeColor, pygame.Rect(self.position[0], self.position[1], self.w, self.h))
101 | else:
102 | pygame.draw.rect(screen, self.color, pygame.Rect(self.position[0], self.position[1], self.w, self.h))
103 | return self.state
104 |
105 | class TextUI:
106 | def __init__(self,text, position, fontColor, anchor='center'):
107 | self.position = position
108 | self.text = text
109 | self.font = 'freesansbold.ttf'
110 | self.anchor = anchor
111 | self.fontSize = 18
112 | self.fontColor = fontColor
113 | def Render(self, screen):
114 | font = pygame.font.Font(self.font, self.fontSize)
115 | text = font.render(self.text, True, self.fontColor)
116 | textRect = text.get_rect()
117 | setattr(textRect, self.anchor, self.position)
118 | #textRect.center = (self.position[0], self.position[1])
119 | screen.blit(text, textRect)
120 |
121 | class DigitInput:
122 | def __init__(self,startingValue, position = (width-320, 100), w= 300, h= 600, color=(8, 3, 12)):
123 | self.position = position
124 | self.text = str(startingValue)
125 | self.fontColor = (255, 255, 255)
126 | self.fontSize = 18
127 | self.font = 'freesansbold.ttf'
128 | self.w = w
129 | self.h = h
130 | self.color = color
131 | self.value = int(self.text)
132 | self.hoverEnter = False
133 |
134 | def Check(self, backspace,val):
135 |
136 | if self.hoverEnter == True:
137 | if backspace == True:
138 |
139 | if len(str(self.value)) <= 0 or len(str(self.value))-1 <= 0:
140 | self.value = 0
141 | else:
142 | self.value = int(str(self.value)[:-1])
143 |
144 | else:
145 | if self.text.isdigit():
146 | self.value = int(str(self.value) + str(self.text))
147 | else:
148 | for el in self.text:
149 | if el.isdigit() != True:
150 | self.text = self.text.replace(el, "")
151 | backspace == False
152 | self.text = ""
153 |
154 | def updateText(self, val, pressed):
155 | m = pygame.mouse.get_pos()
156 | if m[0] >= self.position[0] and m[0] <= self.position[0] + self.w:
157 | if m[1] >= self.position[1] and m[1] <= self.position[1] + self.h:
158 | self.hoverEnter = True
159 | if pressed == True:
160 | self.text += val
161 | else:
162 | self.hoverEnter = False
163 | val = ""
164 | else:
165 | self.hoverEnter = False
166 | val = ""
167 |
168 |
169 | def Render(self, screen, val, backspace, pressed):
170 | self.updateText(val, pressed)
171 | self.Check(backspace, val)
172 | font = pygame.font.Font(self.font, self.fontSize)
173 | text = font.render(str(self.value), True, self.fontColor)
174 | textRect = text.get_rect()
175 | textRect.center = (self.position[0]+self.w//2, self.position[1]+self.h//2)
176 | pygame.draw.rect(screen, self.color, pygame.Rect(self.position[0], self.position[1], self.w, self.h))
177 | screen.blit(text, textRect)
178 |
179 | class Slider:
180 | def __init__(self,x, y, val, min1, max1, length, h, max=500):
181 | self.value = val
182 | self.x = x
183 | self.y = y
184 | self.h = h
185 | self.min1 = min1
186 | self.max1 = max1
187 | self.length = length
188 | self.lineColor = (20, 10, 20)
189 | self.rectradius = 10
190 | self.temp_radius = self.rectradius
191 | self.rectColor = (255, 255, 255)
192 | self.v = 0.2
193 | self.temp = self.lineColor
194 | self.max = max
195 |
196 | def Calculate(self, val):
197 | self.v = translate(val, 0, self.length, 0, 1)
198 | self.value = self.v * self.max
199 |
200 | def HandleMouse(self):
201 | mx, my = pygame.mouse.get_pos()
202 |
203 | if mx >= self.x and mx <= self.x + self.length:
204 | if my >= self.y and my <= self.y + self.h:
205 | self.rectradius = 15
206 | if pygame.mouse.get_pressed()[0]:
207 | self.Calculate(mx - self.x)
208 | else:
209 | self.lineColor = self.temp
210 | self.rectradius = self.temp_radius
211 | else:
212 | self.lineColor = self.temp
213 | self.rectradius = self.temp_radius
214 |
215 | def Render(self,screen):
216 | self.HandleMouse()
217 | pygame.draw.rect(screen, self.lineColor, pygame.Rect(self.x, self.y, self.length, self.h))
218 | x = int((self.v * self.length) + self.x)
219 | pygame.draw.rect(screen, self.rectColor, pygame.Rect(self.x, self.y, int( self.v * self.length), self.h))
220 | pygame.draw.circle(screen, (130, 213, 151), (x, self.y + (self.rectradius//2)), self.rectradius)
221 | return self.value
222 |
223 |
224 |
225 | ## Don't Mind the code below it's an absolute mess 🍝
226 | class DropDownButton:
227 | def __init__(self, text="Select", position = (width-230, 400) , w = 100, h= 50, children_size=2, border=0, color = (0, 0, 0), borderColor = (0, 0, 0)):
228 | self.text = text
229 | self.position = position
230 | self.w = w
231 | self.h = h
232 | self.border = border
233 | self.temp = color
234 | self.color = color
235 | self.children_size = children_size
236 | self.childs = []
237 | for i in range(self.children_size):
238 | button = Button("button " +str(i), (position[0], position[1] + h + h*i+ 2), w, h, border, color, borderColor)
239 | self.childs.append(button)
240 | self.borderColor = borderColor
241 | self.font = 'freesansbold.ttf'
242 | self.fontSize = 25
243 | self.textColor = (255, 255, 255)
244 | self.state = False
245 | self.action = None
246 | self.selected = False
247 | self.folded = False
248 | self.currentIndex = None
249 |
250 | def updateChildren(self, value):
251 | self.children_size = value
252 | for i in range(self.children_size):
253 | button = Button("button " +str(i), (self.position[0], self.position[1] + h + h*i+ 2), self.w, self.h, 0, self.border, self.color, self.borderColor)
254 | self.childs.append(button)
255 |
256 | def HandleMouse(self,mouseClicked, HoverColor = (50, 120, 140)):
257 | if self.folded == False:
258 | m = pygame.mouse.get_pos()
259 | if m[0] >= self.position[0] and m[0] <= self.position[0] + self.w:
260 | if m[1] >= self.position[1] and m[1] <= self.position[1] + self.h:
261 | self.color = HoverColor
262 | if mouseClicked == True:
263 | self.folded = not self.folded
264 | self.color = (200, 200, 200)
265 | if self.action == None and mouseClicked == True:
266 | self.state = not self.state
267 | else:
268 | self.color = self.temp
269 | else:
270 | self.color = self.temp
271 | else:
272 | m = pygame.mouse.get_pos()
273 | if m[0] >= self.position[0] and m[0] <= self.position[0] + self.w:
274 | if m[1] >= self.position[1] and m[1] <= self.position[1] + self.h:
275 | self.color = HoverColor
276 | if mouseClicked == True:
277 | self.folded = not self.folded
278 | self.color = (200, 200, 200)
279 | if self.action == None and mouseClicked == True:
280 | self.state = not self.state
281 | else:
282 | self.color = self.temp
283 | else:
284 | self.color = self.temp
285 | for child in self.childs:
286 | m = pygame.mouse.get_pos()
287 | if m[0] >= child.position[0] and m[0] <= child.position[0] + child.w:
288 | if m[1] >= child.position[1] and m[1] <= child.position[1] + child.h:
289 | child.color = HoverColor
290 | if mouseClicked==True:
291 | child.color = (200, 200, 200)
292 | self.text = child.text
293 | self.currentIndex = self.childs.index(child)
294 | self.folded = not self.folded
295 | if child.action == None and mouseClicked == True:
296 | child.state = not child.state
297 | else:
298 | child.color = child.temp
299 | else:
300 | child.color = child.temp
301 |
302 |
303 | def Render(self, screen, mouseClicked, checker = True):
304 | if checker:
305 | self.HandleMouse(mouseClicked)
306 | font = pygame.font.Font(self.font, self.fontSize)
307 | text = font.render(self.text, True, self.textColor)
308 | textRect = text.get_rect()
309 | textRect.center = (self.position[0]+self.w//2, self.position[1]+self.h//2)
310 | if self.border > 0:
311 | pygame.draw.rect(screen, self.borderColor, pygame.Rect(self.position[0] - self.border//2, self.position[1] - self.border//2, self.w + self.border, self.h + self.border))
312 | pygame.draw.rect(screen, self.color, pygame.Rect(self.position[0], self.position[1], self.w, self.h))
313 |
314 | screen.blit(text, textRect)
315 | if self.folded == True:
316 | for child in self.childs:
317 | child.Render(screen, mouseClicked, False)
318 |
--------------------------------------------------------------------------------