diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/blinky.py | 4 | ||||
| -rw-r--r-- | src/clyde.py | 86 | ||||
| -rw-r--r-- | src/game.py | 246 | ||||
| -rw-r--r-- | src/game_state.py | 50 | ||||
| -rw-r--r-- | src/ghost.py | 120 | ||||
| -rw-r--r-- | src/inky.py | 179 | ||||
| -rw-r--r-- | src/map.py | 29 | ||||
| -rw-r--r-- | src/mode.py | 8 | ||||
| -rw-r--r-- | src/pinky.py | 68 | ||||
| -rw-r--r-- | src/player.py | 62 | ||||
| -rw-r--r-- | src/settings.py | 3 | ||||
| -rw-r--r-- | src/timer.py | 7 |
12 files changed, 636 insertions, 226 deletions
diff --git a/src/blinky.py b/src/blinky.py index eee44a8..4c45644 100644 --- a/src/blinky.py +++ b/src/blinky.py @@ -1,8 +1,6 @@ from ghost import Ghost + class Blinky(Ghost): def __init__(self, sprite_sheet, x, y): super().__init__(sprite_sheet, "red", x, y) - - - diff --git a/src/clyde.py b/src/clyde.py index 117f71a..2fd4aee 100644 --- a/src/clyde.py +++ b/src/clyde.py @@ -1,9 +1,93 @@ from ghost import Ghost +import random +import pygame +from settings import settings +from mode import MODE +from typing_extensions import override +import math + class Clyde(Ghost): def __init__(self, sprite_sheet, x, y): - super().__init__(sprite_sheet, "orange", x, y) + super().__init__(sprite_sheet, "cyan", x, y) + + def is_eight_tiles_away(self, pacman): + tile_width = 30 + dx = self.x - pacman.x + dy = self.y - pacman.y + return math.sqrt(dx * dx + dy * dy) <= tile_width * 8 + + @override + def get_default_tile(self): + return (2 * 30 + 15, 30 * 30 + 15) + + @override + def get_intial_tile(self): + return (14 * 30 + 15, 12 * 30 + 15) + + @override + def get_next_move(self, game_state, screen): + default_tile = self.get_default_tile() + + dx = [1, 0, -1, 0] # right, down, left, up + dy = [0, 1, 0, -1] + + inv_dir = [2, 3, 0, 1] + + ret = len(dx) * [math.inf] + bottom_left_corner = ( + 2.5 * 30, (len(game_state.map.maze) - 1 - 1 - 0.5) * 30) + + forbidden = inv_dir[self.last_move] + + rand_pos = (0, 0) + + if game_state.pacman.powerup and self.mode != MODE.EATEN: + self.mode = MODE.FRIGHETENED + rand_pos = random.randint(0, 900), random.randint(0, 990) + if game_state.pacman.powerup is False and self.mode == MODE.FRIGHETENED: + self.mode = MODE.CHASING + if settings.debug: + pygame.draw.line(screen, self.color, (game_state.pacman.x, game_state.pacman.y), + (self.x, self.y), 1) + for i in range(len(dx)): + nx = self.x + dx[i] * self.speed + ny = self.y + dy[i] * self.speed + if self.check_collision(nx, ny, 30, 30, game_state.map.maze): + if i != forbidden: + if self.mode == MODE.SCATTERED: + ret[i] = self.heuristic( + (nx, ny), default_tile[0], default_tile[1]) + elif self.mode == MODE.FRIGHETENED: + ret[i] = self.heuristic( + (nx, ny), rand_pos[0], rand_pos[1]) + elif self.mode == MODE.CHASING: + if self.is_eight_tiles_away(game_state.pacman): + ret[i] = self.heuristic( + (nx, ny), bottom_left_corner[0], bottom_left_corner[1]) + if settings.debug: + pygame.draw.line(screen, self.color, (bottom_left_corner), + (self.x, self.y), 1) + else: + ret[i] = self.heuristic( + (nx, ny), game_state.pacman.x, game_state.pacman.y) + if settings.debug: + pygame.draw.line(screen, self.color, (game_state.pacman.x, game_state.pacman.y), + (self.x, self.y), 1) + elif self.mode == MODE.EATEN: + pos = self.get_intial_tile() + self.x = pos[0] + self.y = pos[1] + min_h = min(ret) + # Favour going up when there is a conflict + if min_h == ret[3] and min_h != math.inf: + return 3 + # Favour going down than sideways when there is a conflict + if min_h == ret[1] and min_h != math.inf: + return 1 + min_idx = ret.index(min_h) + return min_idx diff --git a/src/game.py b/src/game.py index 37739e3..a7efb67 100644 --- a/src/game.py +++ b/src/game.py @@ -1,21 +1,102 @@ -from blinky import Blinky -from clyde import Clyde from direction import DIRECTION -from inky import Inky -from pinky import Pinky -from player import Player +from game_state import GameState +from mode import MODE from settings import settings -import map as Map +from game_state import WIDTH, HEIGHT, TILE_WIDTH, TILE_HEIGHT import pygame -WIDTH = settings.width -HEIGHT = settings.height - class Game(): def __init__(self): self.settings = settings + def show_gameover_screen(self, screen, game_state, sprites): + font = pygame.font.SysFont(None, 64) + + # Render the "Game Over" text to a surface + game_over_text_1 = font.render( + "Game Over", True, (255, 255, 255)) + game_over_text_2 = font.render( + "Press R to try again or Q to quit.", True, (255, 255, 255)) + + # Blit the "Game Over" text onto the screen + text_rect_1 = game_over_text_1.get_rect( + center=(WIDTH/2, HEIGHT/2 - 75)) + text_rect_2 = game_over_text_2.get_rect( + center=(WIDTH/2, HEIGHT/2)) + + screen.blit(game_over_text_1, text_rect_1) + screen.blit(game_over_text_2, text_rect_2) + + # Update the display + pygame.display.flip() + + quit_game = False # Initialize the flag variable + while not quit_game: + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN and event.key == pygame.K_r: + # Reset the game and start again + # Add your own code here to reset the game state + self.reset_game(game_state, sprites) + game_state.game_over = False + quit_game = True # Set the flag to True to break out of both loops + break + elif event.type == pygame.KEYDOWN and event.key == pygame.K_q: + game_state.game_over = True + quit_game = True + break + elif event.type == pygame.QUIT: + game_state.game_over = True + quit_game = True # Set the flag to True to break out of both loops + break + + def show_wining_screen(self, screen, game_state, sprites): + font = pygame.font.SysFont(None, 64) + + # Render the "Game Over" text to a surface + wining_text_1 = font.render( + "Congratulation You Won!!", True, (255, 255, 255)) + wining_text_2 = font.render( + "Press R to play again or Q to quit", True, (255, 255, 255)) + + text_rect_1 = wining_text_1.get_rect( + center=(WIDTH/2, HEIGHT/2 - 75)) + text_rect_2 = wining_text_2.get_rect( + center=(WIDTH/2, HEIGHT/2)) + + # Blit the "Game Over" text onto the screen + text_rect_1 = wining_text_1.get_rect( + center=(WIDTH/2, HEIGHT/2)) + text_rect_2 = wining_text_2.get_rect( + center=(WIDTH/2, HEIGHT/2 + 100)) + screen.blit(wining_text_1, text_rect_1) + screen.blit(wining_text_2, text_rect_2) + + # Update the display + pygame.display.flip() + + quit_game = False # Initialize the flag variable + while not quit_game: + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN and event.key == pygame.K_r: + # Reset the game and start again + # Add your own code here to reset the game state + self.reset_game(game_state, sprites) + game_state.game_over = False + quit_game = True # Set the flag to True to break out of both loops + break + elif event.type == pygame.KEYDOWN and event.key == pygame.K_q: + game_state.game_over = True + quit_game = True + break + elif event.type == pygame.QUIT: + game_state.game_over = True + quit_game = True # Set the flag to True to break out of both loops + break + + def reset_game(self, game_state, sprites): + game_state.reset(sprites) + def run(self): # Initialize Pygame pygame.init() @@ -24,27 +105,24 @@ class Game(): screen = pygame.display.set_mode((WIDTH, HEIGHT)) # Sprite sheet for pacman - sprite_sheet = pygame.image.load( '../assets/pacman_left_sprite.png').convert_alpha() + sprite_sheet = pygame.image.load( + '../assets/pacman_left_sprite.png').convert_alpha() # Sprite sheets for the ghosts - blinky_sprite = pygame.image.load('../assets/blinky.png').convert_alpha() - pinky_sprite = pygame.image.load( '../assets/pinky.png').convert_alpha() - clyde_sprite = pygame.image.load( '../assets/clyde.png').convert_alpha() - inky_sprite = pygame.image.load( '../assets/inky.png').convert_alpha() + blinky_sprite = pygame.image.load( + '../assets/blinky.png').convert_alpha() + pinky_sprite = pygame.image.load('../assets/pinky.png').convert_alpha() + clyde_sprite = pygame.image.load('../assets/clyde.png').convert_alpha() + inky_sprite = pygame.image.load('../assets/inky.png').convert_alpha() - # our beautiful maze - maze = Map.Map() + sprites = [sprite_sheet, blinky_sprite, + pinky_sprite, inky_sprite, clyde_sprite] - # length of the map grid size - TILE_WIDTH = WIDTH // len(maze.maze[0]) - TILE_HEIGHT = HEIGHT // len(maze.maze) + # Set the timer to trigger after 10,000 milliseconds (10 seconds) + timer_event = pygame.USEREVENT + 1 + pygame.time.set_timer(timer_event, 1000 * 10, 1) - # Initialize the player and the ghosts - player = Player(sprite_sheet) - blinky = Blinky(blinky_sprite,75, 75) - pinky = Pinky(pinky_sprite, 27 * 30, 30 * 30 + 15) - inky = Inky(inky_sprite, 75, 30 * 30 + 15) - clyde = Clyde(clyde_sprite, 27 * 30 + 15, 75) + game_state = GameState(sprites) # Set the pacman velocity dx = 0 @@ -57,15 +135,13 @@ class Game(): pygame.mixer.music.load('../assets/sfx/game_start.wav') siren_sound = pygame.mixer.Sound('../assets/sfx/siren_1.wav') - munch_sound = pygame.mixer.Sound('../assets/sfx/munch_1.wav') - - pygame.mixer.music.play() - siren_sound.play(-1) - is_game_over = [False] + if settings.sound: + pygame.mixer.music.play() + siren_sound.play(-1) # Main game loop - while not is_game_over[0]: + while not game_state.game_over: # setting game fps clock.tick(settings.fps) @@ -84,95 +160,101 @@ class Game(): # Handling events for event in pygame.event.get(): if event.type == pygame.QUIT: - is_game_over = False + game_state.game_over = True elif event.type == pygame.KEYDOWN: # Move the circle based on the pressed key if event.key == pygame.K_w: - player.direction = DIRECTION.UP - ty = -player.speed + game_state.pacman.direction = DIRECTION.UP + ty = -game_state.pacman.speed tx = 0 # Necssarry to move only horizontal or vertical elif event.key == pygame.K_s: - player.direction = DIRECTION.DOWN - ty = player.speed + game_state.pacman.direction = DIRECTION.DOWN + ty = game_state.pacman.speed tx = 0 # Necssarry to move only horizontal or vertical elif event.key == pygame.K_a: - player.direction = DIRECTION.LEFT - tx = -player.speed + game_state.pacman.direction = DIRECTION.LEFT + tx = -game_state.pacman.speed ty = 0 # Necssarry to move only horizontal or vertical elif event.key == pygame.K_d: - player.direction = DIRECTION.RIGHT - tx = player.speed + game_state.pacman.direction = DIRECTION.RIGHT + tx = game_state.pacman.speed ty = 0 # Necssarry to move only horizontal or vertical + # Check for the timer event + if event.type == timer_event: + game_state.pinky.mode = MODE.CHASING + game_state.inky.mode = MODE.CHASING + game_state.blinky.mode = MODE.CHASING + game_state.clyde.mode = MODE.CHASING keys = pygame.key.get_pressed() # Simulates holding the key which adds better playability for pacman if keys[pygame.K_w]: - player.direction = DIRECTION.UP - ty = -player.speed + game_state.pacman.direction = DIRECTION.UP + ty = -game_state.pacman.speed tx = 0 elif keys[pygame.K_s]: - player.direction = DIRECTION.DOWN - ty = player.speed + game_state.pacman.direction = DIRECTION.DOWN + ty = game_state.pacman.speed tx = 0 elif keys[pygame.K_a]: - player.direction = DIRECTION.LEFT - tx = -player.speed + game_state.pacman.direction = DIRECTION.LEFT + tx = -game_state.pacman.speed ty = 0 elif keys[pygame.K_d]: - player.direction = DIRECTION.RIGHT - tx = player.speed + game_state.pacman.direction = DIRECTION.RIGHT + tx = game_state.pacman.speed ty = 0 - - # if tx and ty doesn't lead to colliding change the current dx and dy to them and other wise + # if tx and ty doesn't lead to colliding change the current + # dx and dy to them and other wise # let pacman move in his previous direction - if player.check_collision(maze, tx, ty, TILE_WIDTH, TILE_HEIGHT): + if game_state.pacman.check_collision(game_state, tx, ty, TILE_WIDTH, TILE_HEIGHT): dx = tx dy = ty if dx < 0: - player.direction = DIRECTION.LEFT + game_state.pacman.direction = DIRECTION.LEFT elif dx > 0: - player.direction = DIRECTION.RIGHT + game_state.pacman.direction = DIRECTION.RIGHT elif dy < 0: - player.direction = DIRECTION.UP + game_state.pacman.direction = DIRECTION.UP elif dy > 0: - player.direction = DIRECTION.DOWN - - if player.check_collision(maze, dx, dy, TILE_WIDTH, TILE_HEIGHT): - player.x += dx - player.y += dy - player.x %= 900 + game_state.pacman.direction = DIRECTION.DOWN + if game_state.pacman.check_collision(game_state, dx, dy, TILE_WIDTH, TILE_HEIGHT): + game_state.pacman.x += dx + game_state.pacman.y += dy + game_state.pacman.x %= 900 # logic for portal # Move ghosts - blinky.move(maze.maze, player, screen, is_game_over) - pinky.move(maze.maze, player, screen, is_game_over) - inky.move(maze.maze, player, screen, is_game_over) - clyde.move(maze.maze, player, screen, is_game_over) + game_state.blinky.move(game_state, screen) + game_state.pinky.move(game_state, screen) + game_state.inky.move(game_state, screen) + game_state.clyde.move(game_state, screen) # Draw the map on each frame - maze.draw_map(screen) - - # Draw the player and the ghosts - player.draw(screen, counter) - blinky.draw(screen) - pinky.draw(screen) - inky.draw(screen) - clyde.draw(screen) + game_state.map.draw_map(screen) + # Draw the game_state.pacman and the ghosts + game_state.pacman.draw(screen, counter) + game_state.blinky.draw(screen, game_state.pacman.powerup, counter) + game_state.pinky.draw(screen, game_state.pacman.powerup, counter) + game_state.inky.draw(screen, game_state.pacman.powerup, counter) + game_state.clyde.draw(screen, game_state.pacman.powerup, counter) - # Update the screen - pygame.display.flip() - - # Quit Pygame - - #pygame.quit() - screen = pygame.display.set_mode((1280, 720)) - pygame.mixer.music.stop() - siren_sound.stop() - + if game_state.food == 246: + self.show_wining_screen(screen, game_state, sprites) + if not game_state.is_pacman_alive: + self.show_gameover_screen( + screen, game_state, sprites) + game_state.is_pacman_alive = True + else: + # Update the screen + pygame.display.flip() + # Quit Pygame + print(game_state.score) + pygame.quit() diff --git a/src/game_state.py b/src/game_state.py new file mode 100644 index 0000000..5ed98c0 --- /dev/null +++ b/src/game_state.py @@ -0,0 +1,50 @@ +from blinky import Blinky +from clyde import Clyde +from inky import Inky +from pinky import Pinky +from player import Player +from settings import settings +import map as Map +import pygame + +WIDTH = settings.width +HEIGHT = settings.height +maze = Map.Map() +TILE_WIDTH = WIDTH // len(maze.maze[0]) +TILE_HEIGHT = HEIGHT // len(maze.maze) + + +class GameState(): + def __init__(self, sprites): + self.pacman = Player(sprites[0]) + self.blinky = Blinky(sprites[1], 12 * TILE_WIDTH + + 15, 12 * TILE_HEIGHT + 15) + self.pinky = Pinky(sprites[2], 11 * TILE_WIDTH + + 15, 12 * TILE_HEIGHT + 15) + self.inky = Inky(sprites[3], 13 * TILE_WIDTH + + 15, 12 * TILE_HEIGHT + 15) + self.clyde = Clyde(sprites[4], 14 * TILE_WIDTH + + 15, 12 * TILE_HEIGHT + 15) + self.map = Map.Map() + self.food = 0 + self.game_over = False + self.score = 0 + self.is_pacman_alive = True + + def reset(self, sprites): + self.pacman = Player(sprites[0]) + self.blinky = Blinky(sprites[1], 12 * TILE_WIDTH + + 15, 12 * TILE_HEIGHT + 15) + self.pinky = Pinky(sprites[2], 11 * TILE_WIDTH + + 15, 12 * TILE_HEIGHT + 15) + self.inky = Inky(sprites[3], 13 * TILE_WIDTH + + 15, 12 * TILE_HEIGHT + 15) + self.clyde = Clyde(sprites[4], 14 * TILE_WIDTH + + 15, 12 * TILE_HEIGHT + 15) + self.map = Map.Map() + self.food = 0 + self.game_over = False + self.is_pacman_alive = True + self.score = 0 + timer_event = pygame.USEREVENT + 1 + pygame.time.set_timer(timer_event, 1000 * 10, 1) diff --git a/src/ghost.py b/src/ghost.py index e810bfe..8568ff4 100644 --- a/src/ghost.py +++ b/src/ghost.py @@ -1,23 +1,33 @@ import pygame +import time +import random import math from util import get_sprites +from timer import Timer from settings import settings +from mode import MODE +from direction import DIRECTION import map as Map -dx = [1, 0, -1, 0] # right, down, left, up +dx = [1, 0, -1, 0] # right, down, left, up dy = [0, 1, 0, -1] inv_dir = [2, 3, 0, 1] sprite_sheet = [2, 0, 3, 1] + class Ghost(): - def __init__(self,sprite_sheet, color, x, y): + def __init__(self, sprite_sheet, color, x, y): self.x = x self.y = y + self.sprite_sheet = sprite_sheet + self.name = "blinky" self.sprite = get_sprites(sprite_sheet) self.color = color - self.last_move = 3 # this represents the direction based on the dx, dy arrays + self.last_move = 3 # this represents the direction based on the dx, dy arrays self.speed = 3 + self.timer = None + self.mode = MODE.SCATTERED def in_bounds(self, pos): (x, y) = pos @@ -26,67 +36,133 @@ class Ghost(): def heuristic(self, next_pos, tx, ty): return abs(next_pos[0] - tx) + abs(next_pos[1] - ty) - # checks if the current position of pacman is either a dot, big dot or free def is_valid(self, maze, x, y): - if x >= 0 and x < 30: + if x >= 0 and x < 30: # Necessary to make portals work is_dot = maze[y][x] == Map.D is_big_dot = maze[y][x] == Map.BD is_free = maze[y][x] == 0 return (is_dot or is_free or is_big_dot) return True + def get_default_tile(self): + return (75, 75) + + def get_intial_tile(self): + return (12 * 30 + 15, 12 * 30 + 15) + + # checks collision with pacman and obstacles returns false if there is + # a collision and true otherwise - # checks collision with pacman and obstacles returns false if there is a collision and true otherwise def check_collision(self, nx, ny, gx, gy, maze): direct_x = [1, 0, -1, 0, 1, 1, -1, -1] direct_y = [0, 1, 0, -1, -1, 1, -1, 1] - for i in range(len(direct_x)): px = nx + direct_x[i] * 14 py = ny + direct_y[i] * 14 x = px // gx y = py // gy - if not self.is_valid(maze, x, y): + if not self.is_valid(maze, x, y): return False return True + def check_pacman_collision(self, game_state): + if game_state.pacman.powerup and abs(game_state.pacman.x - self.x) <= 30 and abs(game_state.pacman.y - self.y) <= 30: + initial_position = self.get_intial_tile() + self.mode = MODE.EATEN + self.timer = Timer(2 * 1000) + time.sleep(1) + game_state.score += 200 + self.x = initial_position[0] + self.y = initial_position[1] + elif not game_state.pacman.powerup and abs(game_state.pacman.x - self.x) <= 30 and abs(game_state.pacman.y - self.y) <= 30: + if abs(game_state.pacman.x - self.x) <= 30 and abs(game_state.pacman.y - self.y) <= 30: + game_state.is_pacman_alive = False + + def get_next_move(self, game_state, screen): - def get_next_move(self, pacman, maze, screen): + default_tile = self.get_default_tile() ret = len(dx) * [math.inf] forbidden = inv_dir[self.last_move] - + + rand_pos = (0, 0) + + if game_state.pacman.powerup and self.mode != MODE.EATEN: + self.mode = MODE.FRIGHETENED + rand_pos = random.randint(0, 900), random.randint(0, 990) + + if game_state.pacman.powerup is False and self.mode == MODE.FRIGHETENED: + self.mode = MODE.CHASING + for i in range(len(dx)): nx = self.x + dx[i] * self.speed ny = self.y + dy[i] * self.speed - if self.check_collision(nx, ny, 30, 30, maze): + if self.check_collision(nx, ny, 30, 30, game_state.map.maze): if i != forbidden: - ret[i] = self.heuristic((nx, ny), pacman.x, pacman.y) + if self.mode == MODE.SCATTERED: + ret[i] = self.heuristic( + (nx, ny), default_tile[0], default_tile[1]) + elif self.mode == MODE.CHASING: + ret[i] = self.heuristic( + (nx, ny), game_state.pacman.x, game_state.pacman.y) + elif self.mode == MODE.FRIGHETENED: + ret[i] = self.heuristic( + (nx, ny), rand_pos[0], rand_pos[1]) + elif self.mode == MODE.EATEN: + pos = self.get_intial_tile() + self.x = pos[0] + self.y = pos[1] + if settings.debug: + pygame.draw.line(screen, self.color, (game_state.pacman.x, game_state.pacman.y), + (self.x, self.y), 1) min_h = min(ret) + + # Favour going up when there is a conflict if min_h == ret[3] and min_h != math.inf: return 3 + # Favour going down than sideways when there is a conflict + if min_h == ret[1] and min_h != math.inf: + return 1 min_idx = ret.index(min_h) return min_idx - - def move(self, maze, pacman, screen, game_over): - if abs(pacman.x - self.x) <= 15 and abs(pacman.y - self.y) <= 15: - game_over[0] = True - min_idx = self.get_next_move(pacman, maze, screen) + def move(self, game_state, screen): + self.check_pacman_collision(game_state) + min_idx = self.get_next_move(game_state, screen) new_dx = dx[min_idx] * self.speed new_dy = dy[min_idx] * self.speed self.x += new_dx self.y += new_dy - self.x %= 900 + self.x %= 900 # The logic of the portal self.last_move = min_idx - def draw(self, screen): + def draw(self, screen, powerup, counter): + if self.timer is not None: + elapsed_time = pygame.time.get_ticks() - self.timer.start + if elapsed_time > self.timer.duration: + self.mode = MODE.CHASING + radius = 30 // 2 - pos = (self.x - radius , self.y - radius) - image = pygame.transform.scale(self.sprite[sprite_sheet[self.last_move]], (40, 40)) - screen.blit(image, pos) + pos = (self.x - radius, self.y - radius) + if powerup: + self.sprite = get_sprites(pygame.image.load( + f'../assets/pacman_{self.color}.png').convert_alpha()) + image = pygame.transform.scale(self.sprite[counter // 5], (35, 35)) + if self.last_move == DIRECTION.UP.value: + screen.blit(pygame.transform.rotate(image, 270), pos) + elif self.last_move == DIRECTION.DOWN.value: + screen.blit(pygame.transform.rotate(image, 90), pos) + elif self.last_move == DIRECTION.RIGHT.value: + screen.blit(pygame.transform.flip(image, True, False), pos) + elif self.last_move == DIRECTION.LEFT.value: + screen.blit(image, pos) + else: + self.sprite = get_sprites(self.sprite_sheet) + image = pygame.transform.scale( + self.sprite[sprite_sheet[self.last_move]], (40, 40)) + screen.blit(image, pos) diff --git a/src/inky.py b/src/inky.py index 2f886a6..be2e62c 100644 --- a/src/inky.py +++ b/src/inky.py @@ -1,73 +1,118 @@ import math +import random +from typing_extensions import override from direction import DIRECTION +from mode import MODE +from settings import settings +import pygame from ghost import Ghost + class Inky(Ghost): def __init__(self, sprite_sheet, x, y): - super().__init__(sprite_sheet, "cyan", x, y) - - - # def get_intermediate_tile(self, pacman): - # if pacman.direction == DIRECTION.UP: - # new_target = (pacman.x - 30 * 2, pacman.y - 30 * 4) - # if self.in_bounds(new_target): - # return new_target - # else: - # return (pacman.x, pacman.y) - # elif pacman.direction == DIRECTION.DOWN: - # new_target = (pacman.x, pacman.y + 30 * 2) - # if self.in_bounds(new_target): - # return new_target - # else: - # return (pacman.x, pacman.y) - # elif pacman.direction == DIRECTION.RIGHT: - # new_target = (pacman.x + 30 * 2, pacman.y) - # if self.in_bounds(new_target): - # return new_target - # else: - # return (pacman.x, pacman.y) - # elif pacman.direction == DIRECTION.LEFT: - # new_target = (pacman.x - 30 * 2, pacman.y) - # if self.in_bounds(new_target): - # return new_target - # else: - # return (pacman.x, pacman.y) - # - # def get_vector_blinky_it(self, blinky, pacman): - # it = self.get_intermediate_tile(pacman) - # return (it[0] - blinky.x, it[1], blinky.y) - # - # @override - # def get_next_move(self, target, maze, screen): - # dx = [1, 0, -1, 0] - # dy = [0, 1, 0, -1] - # - # ret = len(dx) * [math.inf] - # - # forbidden = 0 - # - # if self.last_move == 0: - # forbidden = 2 - # if self.last_move == 1: - # forbidden = 3 - # if self.last_move == 2: - # forbidden = 0 - # if self.last_move == 3: - # forbidden = 1 - # - # new_target = self.get_intermediate_tile(target) - # pygame.draw.circle(screen, self.color, (new_target[0], new_target[1]), 15) - # - # for i in range(len(dx)): - # if i != forbidden: - # nx = self.x + dx[i] * self.speed - # ny = self.y + dy[i] * self.speed - # if self.check_collision(nx, ny, 30, 30, maze): - # ret[i] = self.heuristic((nx, ny), new_target[0], new_target[1]) - # - # min_idx = ret.index(min(ret)) - # return min_idx - # - # - # - # + super().__init__(sprite_sheet, "orange", x, y) + + def get_intermediate_tile(self, pacman): + if pacman.direction == DIRECTION.UP: + new_target = (pacman.x - 30 * 2, pacman.y - 30 * 2) + if self.in_bounds(new_target): + return new_target + else: + return (pacman.x, pacman.y) + elif pacman.direction == DIRECTION.DOWN: + new_target = (pacman.x, pacman.y + 30 * 2) + if self.in_bounds(new_target): + return new_target + else: + return (pacman.x, pacman.y) + elif pacman.direction == DIRECTION.RIGHT: + new_target = (pacman.x + 30 * 2, pacman.y) + if self.in_bounds(new_target): + return new_target + else: + return (pacman.x, pacman.y) + elif pacman.direction == DIRECTION.LEFT: + new_target = (pacman.x - 30 * 2, pacman.y) + if self.in_bounds(new_target): + return new_target + else: + return (pacman.x, pacman.y) + + @override + def get_default_tile(self): + return (27 * 30 + 15, 2 * 30 + 15) + + @override + def get_intial_tile(self): + return (13 * 30 + 15, 12 * 30 + 15) + + def get_target(self, inter_tile, blinky): + target = (max(inter_tile[0] - (blinky.x - inter_tile[0]) % 900, 0), + max(inter_tile[1] - (blinky.y - inter_tile[1]) % 990, 0)) + return target + + @override + def get_next_move(self, game_state, screen): + default_tile = self.get_default_tile() + + dx = [1, 0, -1, 0] + dy = [0, 1, 0, -1] + + ret = len(dx) * [math.inf] + + forbidden = 0 + + if self.last_move == 0: + forbidden = 2 + if self.last_move == 1: + forbidden = 3 + if self.last_move == 2: + forbidden = 0 + if self.last_move == 3: + forbidden = 1 + + inter_tile = self.get_intermediate_tile(game_state.pacman) + target = self.get_target(inter_tile, game_state.blinky) + + rand_pos = (0, 0) + + if game_state.pacman.powerup and self.mode != MODE.EATEN: + self.mode = MODE.FRIGHETENED + rand_pos = random.randint(0, 900), random.randint(0, 990) + + if game_state.pacman.powerup is False and self.mode == MODE.FRIGHETENED: + self.mode = MODE.CHASING + + if settings.debug: + pygame.draw.line(screen, self.color, (target), + (self.x, self.y), 1) + + for i in range(len(dx)): + if i != forbidden: + nx = self.x + dx[i] * self.speed + ny = self.y + dy[i] * self.speed + if self.check_collision(nx, ny, 30, 30, game_state.map.maze): + if self.mode == MODE.SCATTERED: + ret[i] = self.heuristic( + (nx, ny), default_tile[0], default_tile[1]) + elif self.mode == MODE.FRIGHETENED: + ret[i] = self.heuristic( + (nx, ny), rand_pos[0], rand_pos[1]) + elif self.mode == MODE.CHASING: + ret[i] = self.heuristic( + (nx, ny), target[0], target[1]) + elif self.mode == MODE.EATEN: + pos = self.get_intial_tile() + self.x = pos[0] + self.y = pos[1] + + min_h = min(ret) + + # Favour going up when there is a conflict + if min_h == ret[3] and min_h != math.inf: + return 3 + # Favour going down than sideways when there is a conflict + if min_h == ret[1] and min_h != math.inf: + return 1 + min_idx = ret.index(min_h) + return min_idx @@ -3,17 +3,15 @@ import math import settings as Settings -H = 1 -V = 2 -D = 4 -BD = 8 -TR = 16 -TL = 32 -BL = 64 -BR = 128 -G = 256 -LP = 512 -RP = 1024 +H = 1 # Horitoznal wall +V = 2 # Vertical wall +D = 4 # Dot +BD = 8 # Big Dot +TR = 16 # TopRight wall +TL = 32 # TopLeft wall +BL = 64 # BottomLeft wall +BR = 128 # BottomrRight wall +G = 256 # Ghost PI = math.pi @@ -21,7 +19,7 @@ PI = math.pi class Map(): def __init__(self): self.maze = [ - [TL, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H,H, H, H, H, H, H, H, H, H, H, H, H, H, TR], + [TL, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, TR], [V, TL, H, H, H, H, H, H, H, H, H, H, H, H, TR, TL, H, H, H, H, H, H, H, H, H, H, H, H, TR, V], [V, V, D, D, D, D, D, D, D, D, D, D, D, D, V, V, D, D, D, D, D, D, D, D, D, D, D, D, V, V], [V, V, D, TL, H, H, TR, D, TL, H, H, H, TR, D, V, V, D, TL, H, H, H, TR, D, TL, H, H, TR, D, V, V], @@ -54,7 +52,7 @@ class Map(): [V, V, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, D, V, V], [V, BL, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, BR, V], [BL, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, H, BR] - ] + |
