Skip to content

Commit 15d993b

Browse files
committed
add maze game tutorial
1 parent 842c3b5 commit 15d993b

File tree

10 files changed

+333
-0
lines changed

10 files changed

+333
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ This is a repository of all the tutorials of [The Python Code](https://www.thepy
292292
- [How to Make a Checkers Game with Pygame in Python](https://www.thepythoncode.com/article/make-a-checkers-game-with-pygame-in-python). ([code](gui-programming/checkers-game))
293293
- [How to Make a Snake Game in Python](https://www.thepythoncode.com/article/make-a-snake-game-with-pygame-in-python). ([code](gui-programming/snake-game))
294294
- [How to Create a Slide Puzzle Game in Python](https://www.thepythoncode.com/article/slide-puzzle-game-in-python). ([code](gui-programming/slide-puzzle))
295+
- [How to Make a Maze Game in Python](https://www.thepythoncode.com/article/build-a-maze-game-in-python). ([code](gui-programming/maze-game))
295296

296297

297298
For any feedback, please consider pulling requests.

gui-programming/maze-game/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# [How to Make a Maze Game in Python](https://www.thepythoncode.com/article/build-a-maze-game-in-python)

gui-programming/maze-game/cell.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import pygame
2+
from random import choice
3+
4+
class Cell:
5+
def __init__(self, x, y, thickness):
6+
self.x, self.y = x, y
7+
self.thickness = thickness
8+
self.walls = {'top': True, 'right': True, 'bottom': True, 'left': True}
9+
self.visited = False
10+
11+
# draw grid cell walls
12+
def draw(self, sc, tile):
13+
x, y = self.x * tile, self.y * tile
14+
if self.walls['top']:
15+
pygame.draw.line(sc, pygame.Color('darkgreen'), (x, y), (x + tile, y), self.thickness)
16+
if self.walls['right']:
17+
pygame.draw.line(sc, pygame.Color('darkgreen'), (x + tile, y), (x + tile, y + tile), self.thickness)
18+
if self.walls['bottom']:
19+
pygame.draw.line(sc, pygame.Color('darkgreen'), (x + tile, y + tile), (x , y + tile), self.thickness)
20+
if self.walls['left']:
21+
pygame.draw.line(sc, pygame.Color('darkgreen'), (x, y + tile), (x, y), self.thickness)
22+
23+
# checks if cell does exist and returns it if it does
24+
def check_cell(self, x, y, cols, rows, grid_cells):
25+
find_index = lambda x, y: x + y * cols
26+
if x < 0 or x > cols - 1 or y < 0 or y > rows - 1:
27+
return False
28+
return grid_cells[find_index(x, y)]
29+
30+
# checking cell neighbors of current cell if visited (carved) or not
31+
def check_neighbors(self, cols, rows, grid_cells):
32+
neighbors = []
33+
top = self.check_cell(self.x, self.y - 1, cols, rows, grid_cells)
34+
right = self.check_cell(self.x + 1, self.y, cols, rows, grid_cells)
35+
bottom = self.check_cell(self.x, self.y + 1, cols, rows, grid_cells)
36+
left = self.check_cell(self.x - 1, self.y, cols, rows, grid_cells)
37+
if top and not top.visited:
38+
neighbors.append(top)
39+
if right and not right.visited:
40+
neighbors.append(right)
41+
if bottom and not bottom.visited:
42+
neighbors.append(bottom)
43+
if left and not left.visited:
44+
neighbors.append(left)
45+
return choice(neighbors) if neighbors else False

gui-programming/maze-game/clock.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import pygame, time
2+
3+
pygame.font.init()
4+
5+
class Clock:
6+
def __init__(self):
7+
self.start_time = None
8+
self.elapsed_time = 0
9+
self.font = pygame.font.SysFont("monospace", 35)
10+
self.message_color = pygame.Color("yellow")
11+
12+
# Start the timer
13+
def start_timer(self):
14+
self.start_time = time.time()
15+
16+
# Update the timer
17+
def update_timer(self):
18+
if self.start_time is not None:
19+
self.elapsed_time = time.time() - self.start_time
20+
21+
# Display the timer
22+
def display_timer(self):
23+
secs = int(self.elapsed_time % 60)
24+
mins = int(self.elapsed_time / 60)
25+
my_time = self.font.render(f"{mins:02}:{secs:02}", True, self.message_color)
26+
return my_time
27+
28+
# Stop the timer
29+
def stop_timer(self):
30+
self.start_time = None

gui-programming/maze-game/game.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import pygame
2+
3+
pygame.font.init()
4+
5+
class Game:
6+
def __init__(self, goal_cell, tile):
7+
self.font = pygame.font.SysFont("impact", 35)
8+
self.message_color = pygame.Color("darkorange")
9+
self.goal_cell = goal_cell
10+
self.tile = tile
11+
12+
# add goal point for player to reach
13+
def add_goal_point(self, screen):
14+
# adding gate for the goal point
15+
img_path = 'img/gate.png'
16+
img = pygame.image.load(img_path)
17+
img = pygame.transform.scale(img, (self.tile, self.tile))
18+
screen.blit(img, (self.goal_cell.x * self.tile, self.goal_cell.y * self.tile))
19+
20+
# winning message
21+
def message(self):
22+
msg = self.font.render('You Win!!', True, self.message_color)
23+
return msg
24+
25+
# checks if player reached the goal point
26+
def is_game_over(self, player):
27+
goal_cell_abs_x, goal_cell_abs_y = self.goal_cell.x * self.tile, self.goal_cell.y * self.tile
28+
if player.x >= goal_cell_abs_x and player.y >= goal_cell_abs_y:
29+
return True
30+
else:
31+
return False
48.6 KB
Loading

gui-programming/maze-game/main.py

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import pygame, sys
2+
from maze import Maze
3+
from player import Player
4+
from game import Game
5+
from clock import Clock
6+
7+
pygame.init()
8+
pygame.font.init()
9+
10+
class Main():
11+
def __init__(self, screen):
12+
self.screen = screen
13+
self.font = pygame.font.SysFont("impact", 30)
14+
self.message_color = pygame.Color("cyan")
15+
self.running = True
16+
self.game_over = False
17+
self.FPS = pygame.time.Clock()
18+
19+
def instructions(self):
20+
instructions1 = self.font.render('Use', True, self.message_color)
21+
instructions2 = self.font.render('Arrow Keys', True, self.message_color)
22+
instructions3 = self.font.render('to Move', True, self.message_color)
23+
self.screen.blit(instructions1,(655,300))
24+
self.screen.blit(instructions2,(610,331))
25+
self.screen.blit(instructions3,(630,362))
26+
27+
# draws all configs; maze, player, instructions, and time
28+
def _draw(self, maze, tile, player, game, clock):
29+
# draw maze
30+
[cell.draw(self.screen, tile) for cell in maze.grid_cells]
31+
32+
# add a goal point to reach
33+
game.add_goal_point(self.screen)
34+
35+
# draw every player movement
36+
player.draw(self.screen)
37+
player.update()
38+
39+
# instructions, clock, winning message
40+
self.instructions()
41+
if self.game_over:
42+
clock.stop_timer()
43+
self.screen.blit(game.message(),(610,120))
44+
else:
45+
clock.update_timer()
46+
self.screen.blit(clock.display_timer(), (625,200))
47+
48+
pygame.display.flip()
49+
50+
# main game loop
51+
def main(self, frame_size, tile):
52+
cols, rows = frame_size[0] // tile, frame_size[-1] // tile
53+
maze = Maze(cols, rows)
54+
game = Game(maze.grid_cells[-1], tile)
55+
player = Player(tile // 3, tile // 3)
56+
clock = Clock()
57+
58+
maze.generate_maze()
59+
clock.start_timer()
60+
while self.running:
61+
self.screen.fill("gray")
62+
self.screen.fill( pygame.Color("darkslategray"), (603, 0, 752, 752))
63+
64+
for event in pygame.event.get():
65+
if event.type == pygame.QUIT:
66+
pygame.quit()
67+
sys.exit()
68+
69+
# if keys were pressed still
70+
if event.type == pygame.KEYDOWN:
71+
if not self.game_over:
72+
if event.key == pygame.K_LEFT:
73+
player.left_pressed = True
74+
if event.key == pygame.K_RIGHT:
75+
player.right_pressed = True
76+
if event.key == pygame.K_UP:
77+
player.up_pressed = True
78+
if event.key == pygame.K_DOWN:
79+
player.down_pressed = True
80+
player.check_move(tile, maze.grid_cells, maze.thickness)
81+
82+
# if pressed key released
83+
if event.type == pygame.KEYUP:
84+
if not self.game_over:
85+
if event.key == pygame.K_LEFT:
86+
player.left_pressed = False
87+
if event.key == pygame.K_RIGHT:
88+
player.right_pressed = False
89+
if event.key == pygame.K_UP:
90+
player.up_pressed = False
91+
if event.key == pygame.K_DOWN:
92+
player.down_pressed = False
93+
player.check_move(tile, maze.grid_cells, maze.thickness)
94+
95+
if game.is_game_over(player):
96+
self.game_over = True
97+
player.left_pressed = False
98+
player.right_pressed = False
99+
player.up_pressed = False
100+
player.down_pressed = False
101+
102+
self._draw(maze, tile, player, game, clock)
103+
self.FPS.tick(60)
104+
105+
106+
if __name__ == "__main__":
107+
window_size = (602, 602)
108+
screen = (window_size[0] + 150, window_size[-1])
109+
tile_size = 30
110+
screen = pygame.display.set_mode(screen)
111+
pygame.display.set_caption("Maze")
112+
113+
game = Main(screen)
114+
game.main(window_size, tile_size)

gui-programming/maze-game/maze.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import pygame
2+
from cell import Cell
3+
4+
class Maze:
5+
def __init__(self, cols, rows):
6+
self.cols = cols
7+
self.rows = rows
8+
self.thickness = 4
9+
self.grid_cells = [Cell(col, row, self.thickness) for row in range(self.rows) for col in range(self.cols)]
10+
11+
# carve grid cell walls
12+
def remove_walls(self, current, next):
13+
dx = current.x - next.x
14+
if dx == 1:
15+
current.walls['left'] = False
16+
next.walls['right'] = False
17+
elif dx == -1:
18+
current.walls['right'] = False
19+
next.walls['left'] = False
20+
dy = current.y - next.y
21+
if dy == 1:
22+
current.walls['top'] = False
23+
next.walls['bottom'] = False
24+
elif dy == -1:
25+
current.walls['bottom'] = False
26+
next.walls['top'] = False
27+
28+
# generates maze
29+
def generate_maze(self):
30+
current_cell = self.grid_cells[0]
31+
array = []
32+
break_count = 1
33+
while break_count != len(self.grid_cells):
34+
current_cell.visited = True
35+
next_cell = current_cell.check_neighbors(self.cols, self.rows, self.grid_cells)
36+
if next_cell:
37+
next_cell.visited = True
38+
break_count += 1
39+
array.append(current_cell)
40+
self.remove_walls(current_cell, next_cell)
41+
current_cell = next_cell
42+
elif array:
43+
current_cell = array.pop()
44+
return self.grid_cells

gui-programming/maze-game/player.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import pygame
2+
3+
class Player:
4+
def __init__(self, x, y):
5+
self.x = int(x)
6+
self.y = int(y)
7+
self.player_size = 10
8+
self.rect = pygame.Rect(self.x, self.y, self.player_size, self.player_size)
9+
self.color = (250, 120, 60)
10+
self.velX = 0
11+
self.velY = 0
12+
self.left_pressed = False
13+
self.right_pressed = False
14+
self.up_pressed = False
15+
self.down_pressed = False
16+
self.speed = 4
17+
18+
# get current cell position of the player
19+
def get_current_cell(self, x, y, grid_cells):
20+
for cell in grid_cells:
21+
if cell.x == x and cell.y == y:
22+
return cell
23+
24+
# stops player to pass through walls
25+
def check_move(self, tile, grid_cells, thickness):
26+
current_cell_x, current_cell_y = self.x // tile, self.y // tile
27+
current_cell = self.get_current_cell(current_cell_x, current_cell_y, grid_cells)
28+
current_cell_abs_x, current_cell_abs_y = current_cell_x * tile, current_cell_y * tile
29+
if self.left_pressed:
30+
if current_cell.walls['left']:
31+
if self.x <= current_cell_abs_x + thickness:
32+
self.left_pressed = False
33+
if self.right_pressed:
34+
if current_cell.walls['right']:
35+
if self.x >= current_cell_abs_x + tile - (self.player_size + thickness):
36+
self.right_pressed = False
37+
if self.up_pressed:
38+
if current_cell.walls['top']:
39+
if self.y <= current_cell_abs_y + thickness:
40+
self.up_pressed = False
41+
if self.down_pressed:
42+
if current_cell.walls['bottom']:
43+
if self.y >= current_cell_abs_y + tile - (self.player_size + thickness):
44+
self.down_pressed = False
45+
46+
# drawing player to the screen
47+
def draw(self, screen):
48+
pygame.draw.rect(screen, self.color, self.rect)
49+
50+
# updates player position while moving
51+
def update(self):
52+
self.velX = 0
53+
self.velY = 0
54+
if self.left_pressed and not self.right_pressed:
55+
self.velX = -self.speed
56+
if self.right_pressed and not self.left_pressed:
57+
self.velX = self.speed
58+
if self.up_pressed and not self.down_pressed:
59+
self.velY = -self.speed
60+
if self.down_pressed and not self.up_pressed:
61+
self.velY = self.speed
62+
63+
self.x += self.velX
64+
self.y += self.velY
65+
66+
self.rect = pygame.Rect(int(self.x), int(self.y), self.player_size, self.player_size)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pygame

0 commit comments

Comments
 (0)