From 21870930ef59bf510ec0405a4ca944f177b04360 Mon Sep 17 00:00:00 2001 From: Jan Lerking Date: Thu, 17 Apr 2025 21:40:49 +0200 Subject: [PATCH] Added maze loading. /JL --- actors/enums.py | 6 ++-- actors/ghost.py | 3 +- actors/pacman.py | 6 ++-- labyrinth.py | 28 +++++++-------- maze.py | 85 ++++++++++++++++++++++++++++++++++++++++++++ maze/pacman_maze.txt | 29 +++++++++++++++ pman.py | 34 +++++++++++------- scoreboard.py | 4 +-- 8 files changed, 158 insertions(+), 37 deletions(-) create mode 100644 maze.py create mode 100644 maze/pacman_maze.txt diff --git a/actors/enums.py b/actors/enums.py index 4bad0a9..9cfb5f9 100644 --- a/actors/enums.py +++ b/actors/enums.py @@ -1,9 +1,5 @@ from enum import Enum, auto -class PillType(Enum): - NORMAL = 1 - POWER = 2 - class PlayerDirection(Enum): DirectionRight = 0 DirectionLeft = 180 @@ -29,6 +25,8 @@ class Colors(Enum): WHITE = (255, 255, 255) RED = (255, 0, 0) # add others as needed + def __getitem__(self): + return self.value class GhostColor(Enum): BLINKY = (255, 0, 0) diff --git a/actors/ghost.py b/actors/ghost.py index 6949464..f8576d1 100644 --- a/actors/ghost.py +++ b/actors/ghost.py @@ -1,5 +1,6 @@ from .enums import GhostColor, GhostMode, GhostBehavior -from .behaviors import path_toward # required if you want a fallback +from .ghost_behaviors import path_toward # required if you want a fallback +import pygame class Ghost(pygame.sprite.Sprite): def __init__(self, name, color_enum, behavior_enum, position, speed): diff --git a/actors/pacman.py b/actors/pacman.py index 64c9742..123c47e 100644 --- a/actors/pacman.py +++ b/actors/pacman.py @@ -8,7 +8,7 @@ class ActorPacman: self.direction = PlayerDirection.DirectionRight self.speed = 3 self.x, self.y = center - self.radius = 100 + self.radius = 10 self.mouth_angle_deg = 45 self.min_mouth_deg = 5 self.max_mouth_deg = 45 @@ -42,7 +42,7 @@ class ActorPacman: def draw(self): mouth_angle = math.radians(self.mouth_angle_deg) center = (self.x, self.y) - rotation_offset = math.radians(self.direction, 0) + rotation_offset = math.radians(self.direction.value) start_angle = mouth_angle / 2 + rotation_offset end_angle = 2 * math.pi - mouth_angle / 2 + rotation_offset points = [center] @@ -52,7 +52,7 @@ class ActorPacman: y = center[1] + self.radius * math.sin(angle) points.append((x, y)) angle += math.radians(1) - pygame.draw.polygon(self.screen, Colors.Yellow, points) + pygame.draw.polygon(self.screen, Colors.Yellow.value, points) def move_right(self): pass diff --git a/labyrinth.py b/labyrinth.py index 64dbbd8..0251e4c 100644 --- a/labyrinth.py +++ b/labyrinth.py @@ -23,7 +23,7 @@ class PivotWall: (x + dx - dy * self.thickness / self.length, y + dy + dx * self.thickness / self.length), ] - pygame.draw.polygon(self.screen, Colors.Cyan, points) + pygame.draw.polygon(self.screen, Colors.Cyan.value, points) def rotate(self, delta_angle): self.angle = (self.angle + delta_angle) % 360 @@ -42,15 +42,15 @@ class GhostHome: r = self.rect t = self.wall_thickness - pygame.draw.rect(self.screen, Colors.Magenta, (r.left, r.top + t, t, r.height -t)) - pygame.draw.rect(self.screen, Colors.Magenta, (r.right - t, r.top + t, t, r.height -t)) - pygame.draw.rect(self.screen, Colors.Magenta, (r.left, r.bottom - t, r.width, t)) + pygame.draw.rect(self.screen, Colors.Magenta.value, (r.left, r.top + t, t, r.height -t)) + pygame.draw.rect(self.screen, Colors.Magenta.value, (r.right - t, r.top + t, t, r.height -t)) + pygame.draw.rect(self.screen, Colors.Magenta.value, (r.left, r.bottom - t, r.width, t)) gap = 40 gap_x1 = r.centerx - gap // 2 gap_x2 = r.centerx + gap // 2 - pygame.draw.rect(self.screen, Colors.Magenta, (r.left, r.top, gap_x1 - r.left, t)) - pygame.draw.rect(self.screen, Colors.Magenta, (gap_x2, r.top, r.right - gap_x2, t)) + pygame.draw.rect(self.screen, Colors.Magenta.value, (r.left, r.top, gap_x1 - r.left, t)) + pygame.draw.rect(self.screen, Colors.Magenta.value, (gap_x2, r.top, r.right - gap_x2, t)) class Labyrinth: def __init__(self, screen, width, height, wall_thickness=20): @@ -72,15 +72,15 @@ class Labyrinth: mid_x = self.width // 2 mid_y = self.height // 2 - pygame.draw.rect(self.screen, Colors.Blue, (0, 0, mid_x - 50, w)) - pygame.draw.rect(self.screen, Colors.Blue, (mid_x + 50, self.width - (mid_x + 50), w)) - pygame.draw.rect(self.screen, Colors.Blue, (0, self.height - w, mid_x - 50, w)) - pygame.draw.rect(self.screen, Colors.Blue, (mid_x + 50, self.height - w, self.width - (mid_x + 50), w)) + pygame.draw.rect(self.screen, Colors.Blue.value, (0, 0, mid_x - 50, w)) + pygame.draw.rect(self.screen, Colors.Blue.value, (mid_x + 50, 0, self.width - (mid_x + 50), w)) + pygame.draw.rect(self.screen, Colors.Blue.value, (0, self.height - w, mid_x - 50, w)) + pygame.draw.rect(self.screen, Colors.Blue.value, (mid_x + 50, self.height - w, self.width - (mid_x + 50), w)) - pygame.draw.rect(self.screen, Colors.Blue, (0, 0, w, mid_y -50)) - pygame.draw.rect(self.screen, Colors.Blue, (0, mid_y + 50, w, self.height - (mid_y + 50))) - pygame.draw.rect(self.screen, Colors.Blue, (self.width - w, 0, w, mid_y - 50)) - pygame.draw.rect(self.screen, Colors.Blue, (self.width - w, mid_y + 50, w, self.height - (mid_y + 50))) + pygame.draw.rect(self.screen, Colors.Blue.value, (0, 0, w, mid_y -50)) + pygame.draw.rect(self.screen, Colors.Blue.value, (0, mid_y + 50, w, self.height - (mid_y + 50))) + pygame.draw.rect(self.screen, Colors.Blue.value, (self.width - w, 0, w, mid_y - 50)) + pygame.draw.rect(self.screen, Colors.Blue.value, (self.width - w, mid_y + 50, w, self.height - (mid_y + 50))) for pivot in self.pivot_walls: pivot.draw() diff --git a/maze.py b/maze.py new file mode 100644 index 0000000..76fa5d3 --- /dev/null +++ b/maze.py @@ -0,0 +1,85 @@ +import pygame + +TILE_SIZE = 32 + +# Color definitions +COLOR_BG = (0, 0, 0) +COLOR_WALL = (0, 0, 255) +COLOR_DOT = (255, 255, 255) +COLOR_POWER = (255, 255, 255) + +class Maze: + def __init__(self, file_path): + self.file_path = file_path + self.layout = [] + self.dots = set() + self.power_pellets = set() + self.load_maze() + + def load_maze(self): + with open(self.file_path, "r") as f: + self.layout = [line.strip() for line in f.readlines()] + + self.rows = len(self.layout) + self.cols = len(self.layout[0]) + self.width = self.cols * TILE_SIZE + self.height = self.rows * TILE_SIZE + + # Track collectible positions + self.dots.clear() + self.power_pellets.clear() + for y, row in enumerate(self.layout): + for x, char in enumerate(row): + if char == ".": + self.dots.add((x, y)) + elif char == "*": + self.power_pellets.add((x, y)) + elif char == "P": + self.pacman_start = (x * TILE_SIZE + TILE_SIZE // 2, y * TILE_SIZE + TILE_SIZE // 2) + + def draw(self, screen): + for y, row in enumerate(self.layout): + for x, char in enumerate(row): + rect = pygame.Rect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE) + if char == "W": + pygame.draw.rect(screen, COLOR_WALL, rect) + # Draw dots + for (x, y) in self.dots: + center = (x * TILE_SIZE + TILE_SIZE // 2, y * TILE_SIZE + TILE_SIZE // 2) + pygame.draw.circle(screen, COLOR_DOT, center, 2) + # Draw power pellets + for (x, y) in self.power_pellets: + center = (x * TILE_SIZE + TILE_SIZE // 2, y * TILE_SIZE + TILE_SIZE // 2) + pygame.draw.circle(screen, COLOR_POWER, center, 5) + + def tile_at(self, x, y): + """Return the tile character at a specific (x, y) tile coordinate.""" + if 0 <= y < self.rows and 0 <= x < self.cols: + return self.layout[y][x] + return " " # Treat out-of-bounds as path + + def is_wall(self, x, y): + return self.tile_at(x, y) == "W" + + def collect_dot(self, x, y): + """Mark a dot as collected. Return True if there was a dot or power pellet.""" + pos = (x, y) + if pos in self.dots: + self.dots.remove(pos) + return "dot" + elif pos in self.power_pellets: + self.power_pellets.remove(pos) + return "power" + return None + + def reset_collectibles(self): + """Reloads only dot/pellet positions from the layout.""" + self.load_maze() + + def pixel_to_tile(self, px, py): + """Convert pixel coordinates to tile (grid) coordinates.""" + return px // TILE_SIZE, py // TILE_SIZE + + def tile_to_pixel(self, tx, ty): + """Convert tile (grid) coordinates to top-left pixel position.""" + return tx * TILE_SIZE, ty * TILE_SIZE diff --git a/maze/pacman_maze.txt b/maze/pacman_maze.txt new file mode 100644 index 0000000..729633f --- /dev/null +++ b/maze/pacman_maze.txt @@ -0,0 +1,29 @@ +WWWWWWWWWWWWWWWWWWWWWWWWWWWWW +W.............W.............W +W.WWWWW.WWWWW.W.WWWWW.WWWWW.W +W*W W.W W.W.W W.W W*W +W.WWWWW.WWWWW.W.WWWWW.WWWWW.W +W...........................W +W.WWWWW.W.WWWWWWWWW.W.WWWWW.W +W.WWWWW.W.WWWWWWWWW.W.WWWWW.W +W.......W.....W.....W.......W +WWWWWWW.WWWWW.W.WWWWW.WWWWWWW +W W.WWWWW.W.WWWWW.W W +W W...............W W +WWWWWWW.W.WWW---WWW.W.WWWWWWW +........W.W W.W........ +WWWWWWW.W.W W.W.WWWWWWW +W W.W.W W.W.W W +W W.W.WWWWWWWWW.W.W W +WWWWWWW.......P.......WWWWWWW +W.......WWWWWWWWWWWWW.......W +W.WWWWW.W W...W W.WWWWW.W +W*WWWWW.WWWWW.W.WWWWW.WWWWW*W +W...WW........W........WW...W +WWW.WW.WW.WWW...WWW.WW.WW.WWW +WWW.WW.WW.WWWWWWWWW.WW.WW.WWW +W......WW.....W.....WW......W +W.WWWWWWWWWWW.W.WWWWWWWWWWW.W +W.WWWWWWWWWWW.W.WWWWWWWWWWW.W +W...........................W +WWWWWWWWWWWWWWWWWWWWWWWWWWWWW diff --git a/pman.py b/pman.py index 72c90fc..734dc3d 100644 --- a/pman.py +++ b/pman.py @@ -1,11 +1,13 @@ import pygame -from actors.enums import Colors, PlayerDirection +from actors.enums import Colors, PlayerDirection, PillType from actors.pacman import ActorPacman from labyrinth import Labyrinth -from actors.ghosts import Blinky, Pinky, Inky, Clyde # adjust import path as needed +from actors.ghost import Blinky, Pinky, Inky, Clyde # adjust import path as needed +from actors.ghost_mode_controller import GhostModeController from scoreboard import Scoreboard +from maze import Maze -__version__ = "0.2.1" +__version__ = "0.2.3" def spawn_ghosts(center_position): @@ -37,7 +39,7 @@ def place_pills(maze, spacing=16): for x in range(width): if maze[y][x] == 0: # assuming 0 is path if x % spacing == 0 and y % spacing == 0: - pills.add(NormalPill(x * spacing + spacing // 2, y * spacing + spacing // 2)) + pills.add(PillType.PillTypeRegular(x * spacing + spacing // 2, y * spacing + spacing // 2)) # Power pills in 4 corners corners = [ @@ -47,26 +49,29 @@ def place_pills(maze, spacing=16): (height - 2, width - 2) ] for cy, cx in corners: - pills.add(PowerPill(cx * spacing + spacing // 2, cy * spacing + spacing // 2)) + pills.add(PillType.PillTypePower(cx * spacing + spacing // 2, cy * spacing + spacing // 2)) return pills def main() -> None: pygame.init() - screen_width, screen_height = 800, 800 + screen_width, screen_height = (32*29), (32*30) screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("Pac-Man " + __version__) scoreboard = Scoreboard() - labyrinth = Labyrinth(screen, width=screen_width, height=screen_height) - player = ActorPacman(screen, center=(200, 200)) - ghost_home_center = (maze_width // 2, maze_height // 2) + #labyrinth = Labyrinth(screen, width=screen_width, height=screen_height) + maze = Maze("maze/pacman_maze.txt") + + player = ActorPacman(screen, center=maze.pacman_start) + ghost_mode_controller = GhostModeController() + ghost_home_center = (screen_width // 2, screen_height // 2) ghosts = spawn_ghosts(ghost_home_center) clock = pygame.time.Clock() running = True while running: - screen.fill(Colors.Black) + screen.fill(Colors.Black.value) for event in pygame.event.get(): match event.type: @@ -83,7 +88,8 @@ def main() -> None: case pygame.K_DOWN: player.direction = PlayerDirection.DirectionDown case pygame.JOYHATMOTION: - hat_x, hat_y =event.value + print("Hat has been pressed.") + hat_x, hat_y = event.value if hat_x == 1: player.direction = PlayerDirection.DirectionRight elif hat_x == -1: @@ -93,7 +99,9 @@ def main() -> None: elif hat_y == -1: player.direction = PlayerDirection.DirectionDown - labyrinth.draw() + #labyrinth.draw() + # In your main game loop: + maze.draw(screen) player.animate() player.draw() ghost_mode_controller.update() @@ -101,7 +109,7 @@ def main() -> None: for ghost in ghosts: ghost.set_mode(current_mode) - ghost.update(maze, pacman) + #ghost.update(maze, pacman) ghosts.draw(screen) scoreboard.draw(screen) diff --git a/scoreboard.py b/scoreboard.py index 1304553..798c0f4 100644 --- a/scoreboard.py +++ b/scoreboard.py @@ -1,6 +1,6 @@ import pygame import os -from enums import Color +from actors.enums import Colors class Scoreboard: def __init__(self, font_size=24, highscore_file="highscore.txt"): @@ -9,7 +9,7 @@ class Scoreboard: self.highscore = self.load_highscore() self.font = pygame.font.Font(None, font_size) - self.color = Color.WHITE.value + self.color = Colors.WHITE.value self.score_pos = (10, 10) self.highscore_pos = (10, 40)