= PyMaze
Normally this would be a very simple example of a game, maybe even in Season 1. However, if it is used there only introduce the very simple maze algorithms!
== main.py
from Window import Window
from Game import Game
def main():
window = Window()
window.setLogo("logo32x32.png")
window.setCaption("Maze Game by Appledog")
window.setSize(800, 600)
window.setFont("PxPlus_IBM_VGA_8x16-2x.ttf", 32)
game = Game(window)
game.start()
if __name__ == "__main__":
main()
As you can see from this code, this is going to be a pygame terminal framework game. We're going to use a 2x wide (40 column) font for this for aesthetic purposes.
== Game.py
Standard, but there are some changes to draw(), update() and so forth. Also observe the changes to the game data at the start of the class.
import random
import pygame
from Maze import Maze
class Game:
def __init__(self, window):
self.window = window
self.screen = window.screen
self.font = window.font
self.logo = window.logo
# Game Data
self.FPS = 60
self.clock = pygame.time.Clock()
self.maze = Maze(19, 14, 30, 30)
self.maze.binary_tree_maze()
self.maze.cwidth = 32
self.maze.cheight = 32
self.px = 1
self.py = 1
self.tx = random.randint(0, self.maze.width)
self.ty = random.randint(0, self.maze.height)
def start(self):
while True:
self.checkEvents()
self.update()
self.draw()
self.clock.tick(self.FPS)
def update(self):
if self.px == self.tx and self.py == self.ty:
print("YOU WIN!")
self.quit()
def draw(self):
self.screen.fill((0, 0, 0)) # Clear the screen.
self.maze.draw(self.screen)
self.drawText(self.tx, self.ty, "X", "gold")
self.drawText(self.px, self.py, "@", "gray")
pygame.display.flip() # update the display.
def checkEvents(self):
for event in pygame.event.get():
# Window Quit Event
if event.type == pygame.QUIT:
self.quit()
return
# Keyboard Events
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
self.quit()
elif event.key == pygame.K_LEFT:
if not self.maze.get_wall(self.px, self.py, 'west'):
self.px = self.px - 1
elif event.key == pygame.K_RIGHT:
if not self.maze.get_wall(self.px, self.py, 'east'):
self.px = self.px + 1
elif event.key == pygame.K_UP:
if not self.maze.get_wall(self.px, self.py, 'north'):
self.py = self.py - 1
elif event.key == pygame.K_DOWN:
if not self.maze.get_wall(self.px, self.py, 'south'):
self.py = self.py + 1
def drawText(self, at_x, at_y, text, color):
text_surface = self.font.render(text, False, color)
x = self.window.fontwidth * at_x
y = self.window.fontheight * at_y
self.screen.blit(text_surface, (x+2, y+2))
def quit(self):
quit()
exit()
== Window.py
Unchanged.
== Cell.py
class Cell:
def __init__(self):
self.north = True
self.east = True
self.west = True
self.south = True
self.wall_color = "gray"
Just a simple class to store the info about walls.
== Maze.py
The work is done here. Note the requirement for class Cell (shown above):
import random
import pygame
from Cell import Cell
class Maze:
def __init__(self, w, h, cw, ch):
self.width = w
self.height = h
self.cwidth = cw
self.cheight = ch
self.map = [[Cell() for _ in range(w)] for _ in range(h)]
def get_wall(self, x, y, d):
if d == 'north' and self.map[y][x].north:
return True
elif d == 'south' and self.map[y][x].south:
return True
elif d == 'west' and self.map[y][x].west:
return True
elif d == 'east' and self.map[y][x].east:
return True
# fail false.
return False
def set_wall(self, x, y, d, s):
if d == "north":
self.map[y][x].north = s
if y > 0:
self.map[y-1][x].north = s
elif d == "east":
self.map[y][x].east = s
if x < self.width:
self.map[y][x+1].west = s
elif d == "west":
self.map[y][x].west = s
if x > 0:
self.map[y][x-1].east = s
elif d == "south":
self.map[y][x].south = s
if y < self.width:
self.map[y+1][x].north = s
else:
print("unknown wall position")
quit()
exit()
def draw(self, screen):
line_color = "gray"
for y in range(self.height):
for x in range(self.width):
c = self.map[y][x]
ax = 2 + x * self.cwidth
ay = 2 + y * self.cheight
bx = ax + self.cwidth
by = ay + self.cheight
if c.north:
pygame.draw.line(screen, line_color, (ax, ay), (bx, ay))
if c.south:
pygame.draw.line(screen, line_color, (ax, by), (bx, by))
if c.west:
pygame.draw.line(screen, line_color, (ax, ay), (ax, by))
if c.east:
pygame.draw.line(screen, line_color, (bx, ay), (bx, by))
def binary_tree_maze(self):
for x in range(self.width):
for y in range(self.height):
if x == self.width - 1 and y == self.height - 1:
continue
elif x == self.width - 1:
self.set_wall(x, y, "south", False)
elif y == self.height - 1:
self.set_wall(x, y, "east", False)
else:
if random.choice([True, False]):
self.set_wall(x, y, "east", False)
else:
self.set_wall(x, y, "south", False)
=== Binary Tree Maze Algorithm
The binary tree maze algorithm is a simple algorithm for generating mazes. It is often used in computer graphics, game development, and robotics.
The binary tree maze algorithm works by iteratively deciding whether to create passages to the east or south, resulting in a maze with paths that resemble a binary tree structure. The randomness adds variability to the generated mazes. This algorithm is relatively easy to understand, making it suitable for beginners exploring maze generation algorithms and basic grid-based world generation.
# Iteration over Cells:
** The code iterates over each cell in the maze, represented by x and y coordinates.
# Corner Cells Handling:
** The algorithm checks if the current cell is at the bottom-right corner of the maze (x == self.width - 1 and y == self.height - 1). If so, it continues to the next iteration, skipping the cell. This ensures that the bottom-right cell remains open.
** If the cell is on the rightmost edge but not the bottom-right corner, a passage is opened to the south (self.set_wall(x, y, "south", False)). This creates a path towards the bottom of the maze.
** If the cell is on the bottom edge but not the bottom-right corner, a passage is opened to the east (self.set_wall(x, y, "east", False)). This creates a path towards the right side of the maze.
# Inner Cells:
** For inner cells (not on the right or bottom edge), a decision is made using random.choice([True, False]). This randomly chooses whether to create a passage to the east or south.
** If True, a passage is opened to the east (self.set_wall(x, y, "east", False)).
** If False, a passage is opened to the south (self.set_wall(x, y, "south", False)).