Added maze loading. /JL
This commit is contained in:
@@ -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)
|
||||
|
@@ -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):
|
||||
|
@@ -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
|
||||
|
28
labyrinth.py
28
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()
|
||||
|
85
maze.py
Normal file
85
maze.py
Normal file
@@ -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
|
29
maze/pacman_maze.txt
Normal file
29
maze/pacman_maze.txt
Normal file
@@ -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
|
34
pman.py
34
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)
|
||||
|
@@ -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)
|
||||
|
||||
|
Reference in New Issue
Block a user