top of page

Pacman 

unnamed_edited.jpg

The program Python 

​

###
#Projet PACMAN :Eden et Lyna
###
import pyxel
import random

class PacmanGame:
###################################################
#  Initialiser toutes les valeurs
###################################################
    def __init__(self):
        self.width = 160
        self.height = 120
        self.cell_size = 10
        self.grid_width = self.width // self.cell_size
        self.grid_height = self.height // self.cell_size
        self.pacman_x = 80
        self.pacman_y = 80
        self.pacman_radius = 5
        self.velocity = 2
        self.direction = None
        self.score = 0
        self.game_state = "playing"  # alternarive :"game_over"
        # PREMMIER FANTOME
        self.ghost_x = 80
        self.ghost_y = 100
        self.ghost_velocity = 1
        self.ghost_direction = random.choice(['left', 'right', 'up', 'down'])
        # SECOND FANTOME
        self.following_ghost_x = 40
        self.following_ghost_y = 40
        self.following_ghost_direction = 'left'
        # TROISIEM FANTOME
        self.Dernier_ghost_x = 100
        self.Dernier_ghost_y = 30
        self.Dernier_ghost_direction = 'left'  # Initial direction
        self.Dernier_ghost_velocity = 1.5  # The ghost's movement speed

        # Compte 60 images a 30 par secondes = 2s
        self.ghost_active = False  # Initial state for the first ghost
        self.following_ghost_active = False  # Initial state for the second ghost
        self.Dernier_ghost_active = False  # Initial state for the second ghost

        # 3 vies POUR COMMENCER
        self.lives = 3

        # Définir restTime
        self.restTime = 0

 

######################################################
# CREATION DE LA GRILLE "ON THE FLY" (PAS DE FICHIER) après avoir essayé de supperposé des matrices pour donner à chaque case un statut différent
# en fonction de la position de PACMAN et celle des FANTOMES nous avons décider d'utiliser une bibliothèque.
######################################################
        self.grid = {x: {y: {'isWall': False, 'containsPill': False} for y in range(self.grid_height)} for x in range(self.grid_width)}
        for x in range(self.grid_width):
            for y in range(self.grid_height):
                if not self.grid[x][y]['isWall']:  # Si isWall est False
                    self.grid[x][y]['containsPill'] = True  # Alors containsPill devient True (Cela nous évite d'avoir à placer les pionts un à un)

        # Ajouter le murs
        #Horizintal
        for xl in range(0,17):
            self.add_wall(xl, 0)
            self.add_wall(xl, 11)
            if not xl == 7 and not xl == 1 and not xl == 14 and not xl == 8:  # Exclut à la fois 1 et 5
                self.add_wall(xl, 6)
            if not xl == 3 and not xl == 1 and not xl == 14 and not xl == 12:  # Exclut à la fois 1 et 5
                self.add_wall(xl, 2)
        #Vertical
        for xl in range(0,13):
            self.add_wall(0, xl)
            self.add_wall(15,xl)
            if not xl == 1 and not xl == 10 and not xl == 5 and not xl == 7:  # Exclut à la fois 1 et 5
                self.add_wall(2, xl)
            if not xl == 1 and not xl == 10 and not xl == 5 and not xl == 7:  # Exclut à la fois 1 et 5
                self.add_wall(13, xl)
            #Point d'apparition des fantomes
            if not xl == 1 and not xl == 2 and not xl == 5 and not xl == 7 and not xl == 3 and not xl == 4:  # Exclut à la fois 1 et 5
                self.add_wall(5, xl)
            if not xl == 1 and not xl == 2 and not xl == 5 and not xl == 7 and not xl == 3 and not xl == 4:  # Exclut à la fois 1 et 5
                self.add_wall(10, xl)

        # On ajoute "manuellement" des cube et une bille qui nous a servi de test
        self.add_pill(7, 7)
        self.add_wall(6, 5)
        self.add_wall(9, 4)
        self.add_wall(6, 4)
        self.add_wall(9, 5)

        pyxel.init(self.width, self.height)
        pyxel.run(self.update, self.draw)

    def add_wall(self, x, y):
        if 0 <= x < self.grid_width and 0 <= y < self.grid_height:
            self.grid[x][y]['isWall'] = True

    def add_pill(self, x, y):
        if 0 <= x < self.grid_width and 0 <= y < self.grid_height:
            self.grid[x][y]['containsPill'] = True

    def update_ghost(self):
        # On propose aux fantome une nouvelle direction
        next_x, next_y = self.ghost_x, self.ghost_y
        if self.ghost_direction == 'left':
            next_x -= self.ghost_velocity
        elif self.ghost_direction == 'right':
            next_x += self.ghost_velocity
        elif self.ghost_direction == 'up':
            next_y -= self.ghost_velocity
        elif self.ghost_direction == 'down':
            next_y += self.ghost_velocity
        # La prochaine position est ensuite convertie en coordonnées (liées à la grille)
        grid_x, grid_y = next_x // self.cell_size, next_y // self.cell_size
        # On check la collision des parois
        if 0 <= grid_x < self.grid_width and 0 <= grid_y < self.grid_height and not self.grid[grid_x][grid_y]['isWall']:
            # Si aucune collision alors changer position fantome
            self.ghost_x, self.ghost_y = next_x, next_y
        else:
            # Si collision alors choisir une nouvelle direction
            self.choose_new_direction()
    #On va maintenant coder un le  following ghost, un fantome qui suit les déplacement de PACMAN
    def update_following_ghost(self):
        directions = ['left', 'right', 'up', 'down']
        min_distance = float('inf')
        selected_direction = self.following_ghost_direction
        for direction in directions:
            dx, dy = self.following_ghost_x, self.following_ghost_y
            if direction == 'left':
                dx -= self.ghost_velocity
            elif direction == 'right':
                dx += self.ghost_velocity
            elif direction == 'up':
                dy -= self.ghost_velocity
            elif direction == 'down':
                dy += self.ghost_velocity
            grid_x, grid_y = dx // self.cell_size, dy // self.cell_size
            if 0 <= grid_x < self.grid_width and 0 <= grid_y < self.grid_height and not self.grid[grid_x][grid_y]['isWall']:
                distance = abs(dx - self.pacman_x) + abs(dy - self.pacman_y)  # Manhattan distance
                if distance < min_distance:
                    min_distance = distance
                    selected_direction = direction
        self.following_ghost_direction = selected_direction
        # Pour bouger le fantome vers PM
        if self.following_ghost_direction == 'left':
            self.following_ghost_x -= self.ghost_velocity
        elif self.following_ghost_direction == 'right':
            self.following_ghost_x += self.ghost_velocity
        elif self.following_ghost_direction == 'up':
            self.following_ghost_y -= self.ghost_velocity
        elif self.following_ghost_direction == 'down':
            self.following_ghost_y += self.ghost_velocity
    #On crée maintenant un dernier fantome, le meme que le précédent sauf que ce dernier est plus rapide (une velocity 50% supperieure)
    def update_Dernier_ghost(self):
        directions = ['left', 'right', 'up', 'down']
        min_distance = float('inf')
        selected_direction = self.Dernier_ghost_direction
        for direction in directions:
            dx, dy = self.Dernier_ghost_x, self.Dernier_ghost_y
            if direction == 'left':
                dx -= self.Dernier_ghost_velocity
            elif direction == 'right':
                dx += self.Dernier_ghost_velocity
            elif direction == 'up':
                dy -= self.Dernier_ghost_velocity
            elif direction == 'down':
                dy += self.Dernier_ghost_velocity
            grid_x, grid_y = dx // self.cell_size, dy // self.cell_size
            if 0 <= grid_x < self.grid_width and 0 <= grid_y < self.grid_height and not self.grid[grid_x][grid_y]['isWall']:
                distance = abs(dx - self.pacman_x) + abs(dy - self.pacman_y)  # Manhattan distance
                if distance < min_distance:
                    min_distance = distance
                    selected_direction = direction
        self.Dernier_ghost_direction = selected_direction
        # Pour bouger le fantome vers PM
        if self.Dernier_ghost_direction == 'left':
            self.Dernier_ghost_x -= self.Dernier_ghost_velocity
        elif self.Dernier_ghost_direction == 'right':
            self.Dernier_ghost_x += self.Dernier_ghost_velocity
        elif self.Dernier_ghost_direction == 'up':
            self.Dernier_ghost_y -= self.Dernier_ghost_velocity
        elif self.Dernier_ghost_direction == 'down':
            self.Dernier_ghost_y += self.Dernier_ghost_velocity

    def choose_new_direction(self):
        directions = ['left', 'right', 'up', 'down']
        opposite_direction = {'left': 'right', 'right': 'left', 'up': 'down', 'down': 'up'}
        directions.remove(opposite_direction[self.ghost_direction])  # Enlever la direction opposée
        self.ghost_direction = random.choice(directions)

    def update(self):
        if self.game_state == "game_over":
            if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
                mouse_x, mouse_y = pyxel.mouse_x, pyxel.mouse_y
                if (self.replay_button_x <= mouse_x <= self.replay_button_x + self.replay_button_width and
                    self.replay_button_y <= mouse_y <= self.replay_button_y + self.replay_button_height):
                    self.reset_game()  # On réinitialise le jeu
        elif self.game_state == "playing":
            next_x = self.pacman_x + {'left': -self.velocity, 'right': self.velocity}.get(self.direction, 0)
            next_y = self.pacman_y + {'up': -self.velocity, 'down': self.velocity}.get(self.direction, 0)
            grid_x, grid_y = next_x // self.cell_size, next_y // self.cell_size
            if 0 <= grid_x < self.grid_width and 0 <= grid_y < self.grid_height and not self.grid[grid_x][grid_y]['isWall']:
                self.pacman_x, self.pacman_y = next_x, next_y
                if self.grid[grid_x][grid_y].get('containsPill', False):
                    self.grid[grid_x][grid_y]['containsPill'] = False
                    self.score += 10  # Incrémentation du score lorsque PM mange une bille
            if pyxel.btnp(pyxel.KEY_LEFT): self.direction = 'left'
            elif pyxel.btnp(pyxel.KEY_RIGHT): self.direction = 'right'
            elif pyxel.btnp(pyxel.KEY_UP): self.direction = 'up'
            elif pyxel.btnp(pyxel.KEY_DOWN): self.direction = 'down'

            # Activer chaque fantome toutes les 2 seconds (30 frames * 2)
            # Update/Activer le premier fantome
            if pyxel.frame_count > (self.restTime+60):
                self.ghost_active = True
            if self.ghost_active: self.update_ghost()  # Premier fantome

            # Update/Activer le deuxième fantome
            if pyxel.frame_count > (self.restTime+120):
                self.following_ghost_active = True
            if self.following_ghost_active: self.update_following_ghost()

            # Update/Activer le troisieme fantome
            if pyxel.frame_count > (self.restTime+180):
                self.Dernier_ghost_active = True
            if self.Dernier_ghost_active: self.update_Dernier_ghost()

            # Ici on améliore la colision des fantomes avec les murs suite à quelque problèmes ultérieurs dues au coté choisis pour dessiner les fantomes
            if self.ghost_active:
                dx = self.pacman_x - self.ghost_x
                dy = self.pacman_y - self.ghost_y
                distance = (dx ** 2 + dy ** 2) ** 0.5  # On calcule la distance entre les centres
                if distance < 0.9 * self.pacman_radius:
                    self.lives -= 1  # PM perd une vie
                    if self.lives > 0:
                        self.reset_positions()  # On réinitialise le jeu lorsqu'une vie est perdue
                    else:
                        self.game_over()  # Fin de la partie si PM n'a plus de vies

            # fantome 2
            if self.following_ghost_active:
                dx = self.pacman_x - self.following_ghost_x
                dy = self.pacman_y - self.following_ghost_y
                distance = (dx ** 2 + dy ** 2) ** 0.5  # On calcule la distance entre les centres
                if distance < 0.9 * self.pacman_radius:
                    self.lives -= 1  # PM perd une vie
                    if self.lives > 0:
                        self.reset_positions()  # On réinitialise le jeu lorsqu'une vie est perdue
                    else:
                        self.game_over()  # Fin de la partie si PM n'a plus de vies

            # On réitère pour le fantome 3
            if self.Dernier_ghost_active:
                dx = self.pacman_x - self.Dernier_ghost_x
                dy = self.pacman_y - self.Dernier_ghost_y
                distance = (dx ** 2 + dy ** 2) ** 0.5
                if distance < 0.9 * self.pacman_radius:
                    self.lives -= 1
                    if self.lives > 0:
                        self.reset_positions()
                    else:
                        self.game_over()
            # La victoire si le joueur atteint 1020 points car il y a 102 billes
            if self.score >= 1020:
                self.game_state = "victory"

    def draw_pacman(self):
        # On dessine PM
        pyxel.circ(self.pacman_x, self.pacman_y, self.pacman_radius, pyxel.COLOR_YELLOW)
        # Oeuil de PM
        eye_x, eye_y = self.pacman_x + 2, self.pacman_y - 3
        pyxel.circ(eye_x, eye_y, 1, pyxel.COLOR_BLACK)
        # Bouche de PM
        if self.direction == "right":
            pyxel.tri(self.pacman_x, self.pacman_y, self.pacman_x + 5, self.pacman_y - 5, self.pacman_x + 5, self.pacman_y + 5, pyxel.COLOR_BLACK)
        elif self.direction == "left":
            pyxel.tri(self.pacman_x, self.pacman_y, self.pacman_x - 5, self.pacman_y - 5, self.pacman_x - 5, self.pacman_y + 5, pyxel.COLOR_BLACK)
        elif self.direction == "up":
            pyxel.tri(self.pacman_x, self.pacman_y, self.pacman_x - 5, self.pacman_y - 5, self.pacman_x + 5, self.pacman_y - 5, pyxel.COLOR_BLACK)
        elif self.direction == "down":
            pyxel.tri(self.pacman_x, self.pacman_y, self.pacman_x - 5, self.pacman_y + 5, self.pacman_x + 5, self.pacman_y + 5, pyxel.COLOR_BLACK)

    def draw_ghost(self, x, y, color):
        # Dessin de fantome
        pyxel.circb(x, y, self.pacman_radius, color)
        for offset in range(-self.pacman_radius, self.pacman_radius + 1, 3):
            pyxel.line(x + offset, y + self.pacman_radius - 1, x + offset, y + self.pacman_radius + 2, color)
        # Oeuil des fantomes
        pyxel.circ(x - 2, y - 1, 1, pyxel.COLOR_WHITE)
        pyxel.circ(x + 2, y - 1, 1, pyxel.COLOR_WHITE)
        pyxel.pset(x - 2, y - 1, pyxel.COLOR_BLACK)
        pyxel.pset(x + 2, y - 1, pyxel.COLOR_BLACK)

    def draw(self):
        if self.game_state == "playing":
            pyxel.cls(0)
            self.draw_pacman()
            for x in range(self.grid_width):
                for y in range(self.grid_height):
                    cell = self.grid[x][y]
                    if cell['isWall']:
                        pyxel.rect(x * self.cell_size, y * self.cell_size, self.cell_size, self.cell_size, pyxel.COLOR_NAVY)
                    elif cell['containsPill']:
                        pill_x, pill_y = x * self.cell_size + self.cell_size // 2, y * self.cell_size + self.cell_size // 2
                        pyxel.circ(pill_x, pill_y, 2, pyxel.COLOR_WHITE)
            # Dessiner le score
            pyxel.text(5, 5, f"Score: {self.score}", pyxel.COLOR_WHITE)
            # Dessiner premier fantome
            if self.ghost_active:
                self.draw_ghost(self.ghost_x, self.ghost_y, pyxel.COLOR_RED)
            # Dessiner le fantome qui suit
            if self.following_ghost_active:
                self.draw_ghost(self.following_ghost_x, self.following_ghost_y, pyxel.COLOR_GREEN)
            # Dessiner le fantome rapide
            if self.Dernier_ghost_active:
                self.draw_ghost(self.Dernier_ghost_x, self.Dernier_ghost_y, pyxel.COLOR_DARK_BLUE)
        elif self.game_state == "game_over":
            # Dessiner le  "Game Over"
            pyxel.text(self.width // 2 - 20, self.height // 2 - 10, "Game Over!", pyxel.COLOR_RED)
        elif self.game_state == "victory":
            pyxel.cls(0)  # Effacer l'écran
            pyxel.text(self.width // 2 - 20, self.height // 2, "Victoire!", pyxel.COLOR_YELLOW)

         # Dessiner les coeurs
        for i in range(self.lives):
            heart_x = self.width - (i + 1) * 16 - 5  # Position hearts from right to left
            heart_y = 5
            # Dessiner deux cercles
            pyxel.circ(heart_x + 3, heart_y + 3, 3, pyxel.COLOR_RED)
            pyxel.circ(heart_x + 8, heart_y + 3, 3, pyxel.COLOR_RED)
            # Dessiner un triangle à l'envers pour le bas du coeur
            pyxel.tri(heart_x, heart_y + 3, heart_x + 11, heart_y + 3, heart_x + 5.5, heart_y + 10, pyxel.COLOR_RED)

    def game_over(self):
        self.game_state = "game_over"
    def reset_game(self):
        # On réinitialise les éléments du jeux
        self.pacman_x = 80
        self.pacman_y = 80
        self.ghost_x = 80
        self.ghost_y = 100
        self.following_ghost_x = 40
        self.following_ghost_y = 40
        self.Dernier_ghost_x = 100
        self.Dernier_ghost_y = 30
        self.score = 0
        self.game_state = "playing"

    def reset_positions(self):
        # On réinitialise les éléments du jeux
        self.pacman_x = 80
        self.pacman_y = 80
        self.ghost_x = 80
        self.ghost_y = 100
        self.following_ghost_x = 40
        self.following_ghost_y = 40
        self.Dernier_ghost_x = 100
        self.Dernier_ghost_y = 30
        self.ghost_active = False
        self.following_ghost_active = False
        self.Dernier_ghost_active = False
        self.restTime = pyxel.frame_count

 


PacmanGame()

PACMAN.VF.py
Displaying PACMAN.VF.py.

bottom of page