Search code examples
algorithmpython-3.xpygameartificial-intelligencepacman

Pygame Enemy chasing player while finding passages in mazes without direct path


I am adjusting a simple pacman pygame and setting it so that the ghosts in pacman find and search pacman (Yellow Square) without any random movements. However once eating a power pellet (Bigger squares) then the ghosts actively find the best path to run away. It doesn't matter if all the ghosts follow the exact same path. I just need to modify my code so the ghosts have an algorithm to navigate the entire maze to find pacman

Here is the code right now:

import pygame
from random import *
from time import *

board_dimensions = [350, 390]
pacman_length = 20
wall_length = 5
speed1 = 4
speed2 = 2


class Text:
    def __init__(self, x, y, size):
        self.x = x
        self.y = y
        self.size = size

    def update(self, screen, text):
        cover = pygame.Surface([len(text) * self.size // 2.1, self.size * 0.7])
        cover.fill([0, 0, 30])
        screen.blit(cover, (self.x, self.y))
        myscore = pygame.font.Font(None, self.size)
        label = myscore.render(text, True, (255, 255, 255))
        textrect = (self.x, self.y)
        screen.blit(label, textrect)


class Button(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([10, 10])
        self.image.fill([255, 255, 255])
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y


class Ghost(pygame.sprite.Sprite):
    def __init__(self, x, y, color):
        super().__init__()
        self.color = color
        self.image = pygame.Surface([pacman_length, pacman_length])
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.direction = [0, speed1]
        self.moves = [False, True, True, False]
        self.edible = False
        self.wait = False
        self.waitcount = 0
        self.speed = speed1

    def move(self, level):
        if self.wait:
            self.waitcount += 1
            if self.waitcount == 30 * 3:
                self.rect.x = wall_length + 8 * pacman_length
                self.rect.y = wall_length + 6 * pacman_length
                self.wait = False
                self.change_color(self.color)
                self.edible = False
        else:
            if self.edible:
                if max(abs(self.direction[0]), abs(self.direction[1])) == speed1 and (
                    self.rect.x - wall_length) % pacman_length == 0 and (
                    self.rect.y - wall_length) % pacman_length == 0:
                    self.direction[0] = self.direction[0] * speed2 / speed1
                    self.direction[1] = self.direction[1] * speed2 / speed1
            else:
                if max(abs(self.direction[0]), abs(self.direction[1])) == speed2 and (
                    self.rect.x - wall_length) % pacman_length == 0 and (
                    self.rect.y - wall_length) % pacman_length == 0:
                    self.direction[0] = self.direction[0] * speed1 / speed2
                    self.direction[1] = self.direction[1] * speed1 / speed2
            if (self.rect.x - wall_length) % pacman_length == 0 and (self.rect.y - wall_length) % pacman_length == 0:
                choices = []
                self.rect.x += self.direction[0]
                self.rect.y += self.direction[1]
                hitlist = pygame.sprite.spritecollide(self, level.wall_list, False)
                if not hitlist:
                    choices.append([self.direction[0], self.direction[1]] * 2)
                self.rect.x -= self.direction[0]
                self.rect.y -= self.direction[1]
                self.rect.x += self.direction[1]
                self.rect.y += self.direction[0]
                hitlist = pygame.sprite.spritecollide(self, level.wall_list, False)
                if not hitlist:
                    choices.append([self.direction[1], self.direction[0]] * 2)
                self.rect.x -= self.direction[1]
                self.rect.y -= self.direction[0]
                self.rect.x -= self.direction[1]
                self.rect.y -= self.direction[0]
                hitlist = pygame.sprite.spritecollide(self, level.wall_list, False)
                if not hitlist:
                    choices.append([-self.direction[1], -self.direction[0]] * 2)
                self.rect.x += self.direction[1]
                self.rect.y += self.direction[0]
                a = randrange(0, len(choices))
                self.rect.x += choices[a][0]
                self.rect.y += choices[a][1]
                self.direction = [choices[a][0], choices[a][1]]
            else:
                self.rect.x += self.direction[0]
                self.rect.y += self.direction[1]
            if self.rect.x > board_dimensions[0]:
                self.rect.x = 0 - pacman_length + wall_length
            elif self.rect.x < 0 - pacman_length:
                self.rect.x = board_dimensions[0] - wall_length

    def change_color(self, color):
        self.image.fill(color)


class Food(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface([5, 5])
        self.image.fill([255, 255, 255])
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y


class Wall(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height):
        super().__init__()
        self.image = pygame.Surface([width, height])
        self.image.fill([0, 0, 125])
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y


class Level:
    def __init__(self):
        buttons = [Button(wall_length + pacman_length / 2 - 5, wall_length + 1.5 * pacman_length - 5),
                   Button(wall_length + 16.5 * pacman_length - 5, wall_length + 1.5 * pacman_length - 5),
                   Button(wall_length + pacman_length / 2 - 5, wall_length + 17.5 * pacman_length - 5),
                   Button(wall_length + 16.5 * pacman_length - 5, wall_length + 17.5 * pacman_length - 5)]
        self.button_list = pygame.sprite.Group()
        for i in buttons:
            self.button_list.add(i)
        ghosts = [Ghost(wall_length + 8 * pacman_length, wall_length + 6 * pacman_length, [255, 0, 0]),
                  Ghost(wall_length + 8 * pacman_length, wall_length + 6 * pacman_length, [0, 255, 0]),
                  Ghost(wall_length + 8 * pacman_length, wall_length + 6 * pacman_length, [0, 255, 255]),
                  Ghost(wall_length + 8 * pacman_length, wall_length + 6 * pacman_length, [125, 0, 125])]
        self.ghost_list = pygame.sprite.Group()
        for i in ghosts:
            self.ghost_list.add(i)
        food = [[wall_length + pacman_length / 2 - 2.5 + pacman_length * (i % 17),
                 wall_length + pacman_length / 2 - 2.5 + (i // 17) * pacman_length] for i in range(17 * 19)]
        remove = [[1, 1], [3, 1], [4, 1], [6, 1], [7, 1], [8, 1], [9, 1], [10, 1], [12, 1], [13, 1], [15, 1], [1, 2],
                  [6, 2], [10, 2], [15, 2], [1, 3], [2, 3], [4, 3], [6, 3], [10, 3], [12, 3], [14, 3], [15, 3], [8, 3],
                  [4, 4], [8, 4], [12, 4], [0, 5], [2, 5], [3, 5], [4, 5], [5, 5], [6, 5], [8, 5], [10, 5], [11, 5],
                  [12, 5], [13, 5], [14, 5], [16, 5], [0, 6], [4, 6], [12, 6], [16, 6], [2, 7], [4, 7], [12, 7],
                  [14, 7], [0, 8], [1, 8], [2, 8], [14, 8], [15, 8], [16, 8], [2, 9], [4, 9], [12, 9], [14, 9], [0, 10],
                  [4, 10], [12, 10], [16, 10], [0, 11], [2, 11], [4, 11], [5, 11], [6, 11], [8, 11], [10, 11], [11, 11],
                  [12, 11], [14, 11], [16, 11], [0, 12], [2, 12], [8, 12], [14, 12], [16, 12], [0, 13], [2, 13],
                  [3, 13], [4, 13], [6, 13], [8, 13], [10, 13], [12, 13], [13, 13], [14, 13], [16, 13], [6, 14],
                  [10, 14], [1, 15], [2, 15], [4, 15], [6, 15], [7, 15], [8, 15], [9, 15], [10, 15], [12, 15], [14, 15],
                  [15, 15], [1, 16], [4, 16], [12, 16], [15, 16], [1, 17], [3, 17], [4, 17], [5, 17], [6, 17], [8, 17],
                  [10, 17], [11, 17], [12, 17], [13, 17], [15, 17], [8, 18], [6, 9], [7, 9], [8, 9], [9, 9], [10, 9],
                  [6, 8], [7, 8], [8, 8], [9, 8], [10, 8], [6, 7], [7, 7], [8, 7], [9, 7], [10, 7], [0, 1], [16, 1]]
        for i in remove:
            food[i[0] + 17 * i[1]] = 1
        self.food_list = pygame.sprite.Group()
        for i in food:
            if i != 1:
                item = Food(i[0], i[1])
                self.food_list.add(item)
        walls = [[wall_length + 6 * pacman_length, wall_length + 7 * pacman_length, 5 * pacman_length, wall_length],
                 [wall_length + 6 * pacman_length, wall_length + 7 * pacman_length, wall_length, 3 * pacman_length],
                 [11 * pacman_length, wall_length + 7 * pacman_length, wall_length, 3 * pacman_length],
                 [wall_length + 6 * pacman_length, wall_length + 10 * pacman_length - wall_length, 5 * pacman_length,
                  wall_length], [0, 0, board_dimensions[0], 5], [0, 0, 5, 2 * wall_length + 5 * pacman_length],
                 [0, wall_length + 5 * pacman_length, wall_length + pacman_length, wall_length],
                 [pacman_length, wall_length + 5 * pacman_length, wall_length, 2 * pacman_length],
                 [0, 7 * pacman_length, pacman_length + wall_length, wall_length],
                 [0, board_dimensions[1] - 5, board_dimensions[0], 5],
                 [board_dimensions[0] - 5, 0, 5, 2 * wall_length + 5 * pacman_length],
                 [board_dimensions[0] - wall_length - pacman_length, wall_length + 5 * pacman_length,
                  wall_length + pacman_length, wall_length],
                 [board_dimensions[0] - pacman_length - wall_length, wall_length + 5 * pacman_length, wall_length,
                  2 * pacman_length],
                 [board_dimensions[0] - pacman_length - wall_length, 7 * pacman_length, pacman_length + wall_length,
                  wall_length],
                 [board_dimensions[0] - pacman_length - wall_length, wall_length + 10 * pacman_length, wall_length,
                  4 * pacman_length],
                 [board_dimensions[0] - pacman_length - wall_length, 14 * pacman_length, pacman_length + wall_length,
                  wall_length], [board_dimensions[0] - pacman_length - wall_length, 10 * pacman_length + wall_length,
                                 pacman_length + wall_length, wall_length],
                 [0, wall_length + 10 * pacman_length, wall_length + pacman_length, wall_length],
                 [pacman_length, wall_length + 10 * pacman_length, wall_length, 4 * pacman_length],
                 [0, 14 * pacman_length, pacman_length + wall_length, wall_length],
                 [0, 14 * pacman_length, wall_length, 5 * pacman_length + wall_length],
                 [board_dimensions[0] - wall_length, 14 * pacman_length, wall_length, 5 * pacman_length + wall_length]]
        self.wall_list = pygame.sprite.Group()
        innerWalls = [[wall_length + pacman_length, wall_length + pacman_length, pacman_length, 3 * pacman_length],
                      [wall_length + 3 * pacman_length, wall_length + pacman_length, 2 * pacman_length, pacman_length],
                      [wall_length + 6 * pacman_length, wall_length + pacman_length, 5 * pacman_length, pacman_length],
                      [wall_length + 12 * pacman_length, wall_length + pacman_length, 2 * pacman_length, pacman_length],
                      [wall_length + 15 * pacman_length, wall_length + pacman_length, pacman_length, 3 * pacman_length],
                      [wall_length + pacman_length, wall_length + 3 * pacman_length, 2 * pacman_length, pacman_length],
                      [wall_length + 6 * pacman_length, wall_length + pacman_length, pacman_length, 3 * pacman_length],
                      [wall_length + 10 * pacman_length, wall_length + pacman_length, pacman_length, 3 * pacman_length],
                      [wall_length + 8 * pacman_length, wall_length + 3 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + 4 * pacman_length, wall_length + 3 * pacman_length, pacman_length,
                       5 * pacman_length],
                      [wall_length + 12 * pacman_length, wall_length + 3 * pacman_length, pacman_length,
                       5 * pacman_length],
                      [wall_length + 14 * pacman_length, wall_length + 3 * pacman_length, 2 * pacman_length,
                       pacman_length],
                      [wall_length + 2 * pacman_length, wall_length + 5 * pacman_length, 5 * pacman_length,
                       pacman_length],
                      [wall_length + 10 * pacman_length, wall_length + 5 * pacman_length, 5 * pacman_length,
                       pacman_length],
                      [0, wall_length + 8 * pacman_length, wall_length + 3 * pacman_length, pacman_length],
                      [wall_length + 14 * pacman_length, wall_length + 8 * pacman_length,
                       wall_length + 3 * pacman_length, pacman_length],
                      [wall_length + 2 * pacman_length, wall_length + 7 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + 14 * pacman_length, wall_length + 7 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + 4 * pacman_length, wall_length + 9 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + 12 * pacman_length, wall_length + 9 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + 4 * pacman_length, wall_length + 11 * pacman_length, 3 * pacman_length,
                       pacman_length],
                      [wall_length + 10 * pacman_length, wall_length + 11 * pacman_length, 3 * pacman_length,
                       pacman_length],
                      [wall_length + 8 * pacman_length, wall_length + 11 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + 2 * pacman_length, wall_length + 11 * pacman_length, pacman_length,
                       2 * pacman_length],
                      [wall_length + 14 * pacman_length, wall_length + 11 * pacman_length, pacman_length,
                       2 * pacman_length],
                      [wall_length + 2 * pacman_length, wall_length + 13 * pacman_length, 3 * pacman_length,
                       pacman_length],
                      [wall_length + 12 * pacman_length, wall_length + 13 * pacman_length, 3 * pacman_length,
                       pacman_length],
                      [wall_length + 10 * pacman_length, wall_length + 13 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + 6 * pacman_length, wall_length + 13 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + 6 * pacman_length, wall_length + 15 * pacman_length, 5 * pacman_length,
                       pacman_length],
                      [wall_length + 8 * pacman_length, wall_length + 17 * pacman_length, pacman_length,
                       2 * pacman_length],
                      [wall_length + 3 * pacman_length, wall_length + 17 * pacman_length, 4 * pacman_length,
                       pacman_length],
                      [wall_length + 10 * pacman_length, wall_length + 17 * pacman_length, 4 * pacman_length,
                       pacman_length],
                      [wall_length + 4 * pacman_length, wall_length + 15 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + 12 * pacman_length, wall_length + 15 * pacman_length, pacman_length,
                       3 * pacman_length],
                      [wall_length + pacman_length, wall_length + 15 * pacman_length, 2 * pacman_length, pacman_length],
                      [wall_length + 14 * pacman_length, wall_length + 15 * pacman_length, 2 * pacman_length,
                       pacman_length],
                      [wall_length + pacman_length, wall_length + 15 * pacman_length, pacman_length, 3 * pacman_length],
                      [wall_length + 15 * pacman_length, wall_length + 15 * pacman_length, pacman_length,
                       3 * pacman_length]]
        for i in walls:
            wall = Wall(i[0], i[1], i[2], i[3])
            self.wall_list.add(wall)
        for i in innerWalls:
            wall = Wall(i[0], i[1], i[2], i[3])
            self.wall_list.add(wall)


class PacMan(pygame.sprite.Sprite):
    def __init__(self, x, y, radius):
        super().__init__()
        self.image = pygame.Surface([radius, radius])
        self.image.fill([250, 250, 0])
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.queue = [-speed1, 0]
        self.direction = [-speed1, 0]

    def change_direction(self, x, y):
        self.queue = [x, y]

    def move(self, level):
        self.rect.x += self.queue[0]
        self.rect.y += self.queue[1]
        hitlist = pygame.sprite.spritecollide(self, level.wall_list, False)
        if hitlist:
            self.rect.x -= self.queue[0]
            self.rect.y -= self.queue[1]
            self.rect.x += self.direction[0]
            self.rect.y += self.direction[1]
            seclist = pygame.sprite.spritecollide(self, level.wall_list, False)
            if seclist:
                self.rect.x -= self.direction[0]
                self.rect.y -= self.direction[1]
        else:
            self.direction[0] = self.queue[0]
            self.direction[1] = self.queue[1]

        if self.rect.x < -pacman_length:
            self.rect.x = board_dimensions[0] - wall_length
            self.rect.y = self.rect.y
        if self.rect.x > board_dimensions[0]:
            self.rect.x = wall_length


def main():
    pygame.init()
    screen = pygame.display.set_mode([board_dimensions[0], board_dimensions[1] + 70])
    pygame.display.set_caption("Pacman")
    player = PacMan(wall_length + 8 * pacman_length, wall_length + 14 * pacman_length, pacman_length)
    moving_players = pygame.sprite.Group()
    moving_players.add(player)
    clock = pygame.time.Clock()
    score_count = Text(50, 410, 15)
    score_label = Text(10, 410, 15)
    board = Level()
    score_value = 0
    ghost_value = 200
    for i in range(3):
        done = False
        edible = False
        count = 0
        while not done:
            count += 1
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    return
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        player.change_direction(-4, 0)
                    elif event.key == pygame.K_RIGHT:
                        player.change_direction(4, 0)
                    elif event.key == pygame.K_DOWN:
                        player.change_direction(0, 4)
                    elif event.key == pygame.K_UP:
                        player.change_direction(0, -4)
                    elif event.key == pygame.K_SPACE:
                        unpause = False
                        while not unpause:
                            for event in pygame.event.get():
                                if event.type == pygame.KEYDOWN:
                                    if event.key == pygame.K_SPACE:
                                        unpause = True
            for i in board.ghost_list:
                i.move(board)
            player.move(board)
            screen.fill([0, 0, 30])
            score_label.update(screen, "Score: ")
            score_count.update(screen, str(score_value))
            board.button_list.draw(screen)
            board.wall_list.draw(screen)
            board.food_list.draw(screen)
            board.ghost_list.draw(screen)
            moving_players.draw(screen)
            foodlist = pygame.sprite.spritecollide(player, board.food_list, False)
            if edible:
                if count == 30 * 6:
                    ghost_value = 200
                    edible = False
                    for i in board.ghost_list:
                        i.change_color(i.color)
                        i.edible = False
                elif count > 30 * 3:
                    for i in board.ghost_list:
                        if i.edible:
                            if count % 15 == 0 or count % 15 == 1:
                                i.change_color([255, 255, 255])
                            elif count % 15 == 2:
                                i.change_color([60, 60, 255])
            for i in foodlist:
                score_value += 10
                board.food_list.remove(i)
            buttonlist = pygame.sprite.spritecollide(player, board.button_list, False)
            if buttonlist:
                edible = True
                count = 0
                for i in buttonlist:
                    board.button_list.remove(i)
                for i in board.ghost_list:
                    i.change_color([60, 60, 255])
                    i.edible = True
            ghostlist = pygame.sprite.spritecollide(player, board.ghost_list, False)
            if ghostlist:
                if ghostlist[0].edible:
                    score_value += ghost_value
                    ghost_value *= 2
                    ghostlist[0].waitcount = 0
                    ghostlist[0].wait = True
                    ghostlist[0].rect.y = wall_length + 8 * pacman_length
                    ghostlist[0].rect.x = wall_length + 8 * pacman_length
                    ghostlist[0].change_color(ghostlist[0].color)
                    ghostlist[0].edible = False
                else:
                    done = True
            pygame.display.flip()
            clock.tick(30)
        player.rect.x = wall_length + 8 * pacman_length
        player.rect.y = wall_length + 14 * pacman_length
        for i in board.ghost_list:
            i.change_color(i.color)
            i.rect.x = wall_length + 8 * pacman_length
            i.rect.y = wall_length + 6 * pacman_length
        ghost_value = 200
        clock.tick(1 / 3)
    pygame.quit()


main()

Solution

  • You could try an implementation of Dijkstra's algorithm to find the minimum spanning tree between the ghosts and pac man. You can make nodes at every turn and have the start node be the ghost and the end node be pac man. It is essentially a maze solving algorithm. If you do not care about execution time or the shortest path, you can run a "left turning" mouse algorithm to get the path that will eventually lead to pac man.