cell.py
import pygame
from random import choice
class Cell:
def __init__(self, x, y, thickness):
self.x, self.y = x, y
self.thickness = thickness
self.walls = {'top': True, 'right': True, 'bottom': True, 'left': True}
self.visited = False
# draw grid cell walls
def draw(self, sc, tile):
x, y = self.x * tile, self.y * tile
if self.walls['top']:
pygame.draw.line(sc, pygame.Color('darkgreen'), (x, y), (x + tile, y), self.thickness)
if self.walls['right']:
pygame.draw.line(sc, pygame.Color('darkgreen'), (x + tile, y), (x + tile, y + tile), self.thickness)
if self.walls['bottom']:
pygame.draw.line(sc, pygame.Color('darkgreen'), (x + tile, y + tile), (x , y + tile), self.thickness)
if self.walls['left']:
pygame.draw.line(sc, pygame.Color('darkgreen'), (x, y + tile), (x, y), self.thickness)
# checks if cell does exist and returns it if it does
def check_cell(self, x, y, cols, rows, grid_cells):
find_index = lambda x, y: x + y * cols
if x < 0 or x > cols - 1 or y < 0 or y > rows - 1:
return False
return grid_cells[find_index(x, y)]
# checking cell neighbors of current cell if visited (carved) or not
def check_neighbors(self, cols, rows, grid_cells):
neighbors = []
top = self.check_cell(self.x, self.y - 1, cols, rows, grid_cells)
right = self.check_cell(self.x + 1, self.y, cols, rows, grid_cells)
bottom = self.check_cell(self.x, self.y + 1, cols, rows, grid_cells)
left = self.check_cell(self.x - 1, self.y, cols, rows, grid_cells)
if top and not top.visited:
neighbors.append(top)
if right and not right.visited:
neighbors.append(right)
if bottom and not bottom.visited:
neighbors.append(bottom)
if left and not left.visited:
neighbors.append(left)
return choice(neighbors) if neighbors else False
clock.py
import pygame, time
pygame.font.init()
class Clock:
def __init__(self):
self.start_time = None
self.elapsed_time = 0
self.font = pygame.font.SysFont("monospace", 35)
self.message_color = pygame.Color("yellow")
# Start the timer
def start_timer(self):
self.start_time = time.time()
# Update the timer
def update_timer(self):
if self.start_time is not None:
self.elapsed_time = time.time() - self.start_time
# Display the timer
def display_timer(self):
secs = int(self.elapsed_time % 60)
mins = int(self.elapsed_time / 60)
my_time = self.font.render(f"{mins:02}:{secs:02}", True, self.message_color)
return my_time
# Stop the timer
def stop_timer(self):
self.start_time = None
game.py
import pygame
pygame.font.init()
class Game:
def __init__(self, goal_cell, tile):
self.font = pygame.font.SysFont("impact", 35)
self.message_color = pygame.Color("darkorange")
self.goal_cell = goal_cell
self.tile = tile
# add goal point for player to reach
def add_goal_point(self, screen):
# adding gate for the goal point
img_path = 'img/gate.png'
img = pygame.image.load(img_path)
img = pygame.transform.scale(img, (self.tile, self.tile))
screen.blit(img, (self.goal_cell.x * self.tile, self.goal_cell.y * self.tile))
# winning message
def message(self):
msg = self.font.render('You Win!!', True, self.message_color)
return msg
# checks if player reached the goal point
def is_game_over(self, player):
goal_cell_abs_x, goal_cell_abs_y = self.goal_cell.x * self.tile, self.goal_cell.y * self.tile
if player.x >= goal_cell_abs_x and player.y >= goal_cell_abs_y:
return True
else:
return False
main.py
import pygame, sys
from maze import Maze
from player import Player
from game import Game
from clock import Clock
pygame.init()
pygame.font.init()
class Main():
def __init__(self, screen):
self.screen = screen
self.font = pygame.font.SysFont("impact", 30)
self.message_color = pygame.Color("cyan")
self.running = True
self.game_over = False
self.FPS = pygame.time.Clock()
def instructions(self):
instructions1 = self.font.render('Use', True, self.message_color)
instructions2 = self.font.render('Arrow Keys', True, self.message_color)
instructions3 = self.font.render('to Move', True, self.message_color)
self.screen.blit(instructions1,(655,300))
self.screen.blit(instructions2,(610,331))
self.screen.blit(instructions3,(630,362))
# draws all configs; maze, player, instructions, and time
def _draw(self, maze, tile, player, game, clock):
# draw maze
[cell.draw(self.screen, tile) for cell in maze.grid_cells]
# add a goal point to reach
game.add_goal_point(self.screen)
# draw every player movement
player.draw(self.screen)
player.update()
# instructions, clock, winning message
self.instructions()
if self.game_over:
clock.stop_timer()
self.screen.blit(game.message(),(610,120))
else:
clock.update_timer()
self.screen.blit(clock.display_timer(), (625,200))
pygame.display.flip()
# main game loop
def main(self, frame_size, tile):
cols, rows = frame_size[0] // tile, frame_size[-1] // tile
maze = Maze(cols, rows)
game = Game(maze.grid_cells[-1], tile)
player = Player(tile // 3, tile // 3)
clock = Clock()
maze.generate_maze()
clock.start_timer()
while self.running:
self.screen.fill("gray")
self.screen.fill( pygame.Color("darkslategray"), (603, 0, 752, 752))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# if keys were pressed still
if event.type == pygame.KEYDOWN:
if not self.game_over:
if event.key == pygame.K_LEFT:
player.left_pressed = True
if event.key == pygame.K_RIGHT:
player.right_pressed = True
if event.key == pygame.K_UP:
player.up_pressed = True
if event.key == pygame.K_DOWN:
player.down_pressed = True
player.check_move(tile, maze.grid_cells, maze.thickness)
# if pressed key released
if event.type == pygame.KEYUP:
if not self.game_over:
if event.key == pygame.K_LEFT:
player.left_pressed = False
if event.key == pygame.K_RIGHT:
player.right_pressed = False
if event.key == pygame.K_UP:
player.up_pressed = False
if event.key == pygame.K_DOWN:
player.down_pressed = False
player.check_move(tile, maze.grid_cells, maze.thickness)
if game.is_game_over(player):
self.game_over = True
player.left_pressed = False
player.right_pressed = False
player.up_pressed = False
player.down_pressed = False
self._draw(maze, tile, player, game, clock)
self.FPS.tick(60)
if __name__ == "__main__":
window_size = (602, 602)
screen = (window_size[0] + 150, window_size[-1])
tile_size = 30
screen = pygame.display.set_mode(screen)
pygame.display.set_caption("Maze")
game = Main(screen)
game.main(window_size, tile_size)
maze.py
import pygame
from cell import Cell
class Maze:
def __init__(self, cols, rows):
self.cols = cols
self.rows = rows
self.thickness = 4
self.grid_cells = [Cell(col, row, self.thickness) for row in range(self.rows) for col in range(self.cols)]
# carve grid cell walls
def remove_walls(self, current, next):
dx = current.x - next.x
if dx == 1:
current.walls['left'] = False
next.walls['right'] = False
elif dx == -1:
current.walls['right'] = False
next.walls['left'] = False
dy = current.y - next.y
if dy == 1:
current.walls['top'] = False
next.walls['bottom'] = False
elif dy == -1:
current.walls['bottom'] = False
next.walls['top'] = False
# generates maze
def generate_maze(self):
current_cell = self.grid_cells[0]
array = []
break_count = 1
while break_count != len(self.grid_cells):
current_cell.visited = True
next_cell = current_cell.check_neighbors(self.cols, self.rows, self.grid_cells)
if next_cell:
next_cell.visited = True
break_count += 1
array.append(current_cell)
self.remove_walls(current_cell, next_cell)
current_cell = next_cell
elif array:
current_cell = array.pop()
return self.grid_cells
player.py
import pygame
class Player:
def __init__(self, x, y):
self.x = int(x)
self.y = int(y)
self.player_size = 10
self.rect = pygame.Rect(self.x, self.y, self.player_size, self.player_size)
self.color = (250, 120, 60)
self.velX = 0
self.velY = 0
self.left_pressed = False
self.right_pressed = False
self.up_pressed = False
self.down_pressed = False
self.speed = 4
# get current cell position of the player
def get_current_cell(self, x, y, grid_cells):
for cell in grid_cells:
if cell.x == x and cell.y == y:
return cell
# stops player to pass through walls
def check_move(self, tile, grid_cells, thickness):
current_cell_x, current_cell_y = self.x // tile, self.y // tile
current_cell = self.get_current_cell(current_cell_x, current_cell_y, grid_cells)
current_cell_abs_x, current_cell_abs_y = current_cell_x * tile, current_cell_y * tile
if self.left_pressed:
if current_cell.walls['left']:
if self.x <= current_cell_abs_x + thickness:
self.left_pressed = False
if self.right_pressed:
if current_cell.walls['right']:
if self.x >= current_cell_abs_x + tile - (self.player_size + thickness):
self.right_pressed = False
if self.up_pressed:
if current_cell.walls['top']:
if self.y <= current_cell_abs_y + thickness:
self.up_pressed = False
if self.down_pressed:
if current_cell.walls['bottom']:
if self.y >= current_cell_abs_y + tile - (self.player_size + thickness):
self.down_pressed = False
# drawing player to the screen
def draw(self, screen):
pygame.draw.rect(screen, self.color, self.rect)
# updates player position while moving
def update(self):
self.velX = 0
self.velY = 0
if self.left_pressed and not self.right_pressed:
self.velX = -self.speed
if self.right_pressed and not self.left_pressed:
self.velX = self.speed
if self.up_pressed and not self.down_pressed:
self.velY = -self.speed
if self.down_pressed and not self.up_pressed:
self.velY = self.speed
self.x += self.velX
self.y += self.velY
self.rect = pygame.Rect(int(self.x), int(self.y), self.player_size, self.player_size)