From 6190790a283f1e06ba47ba292621fa5239cc985c Mon Sep 17 00:00:00 2001 From: DylanVsn <43576618+DylanVsn@users.noreply.github.com> Date: Sun, 17 Nov 2019 18:51:03 +0100 Subject: [PATCH] ajout du dossier livrable avec readme.txt et code fonctionnel --- livrables/1/README.txt | 0 livrables/1/graphic.py | 67 +++++++++ livrables/1/pacman.py | 135 +++++++++++++++++ livrables/1/pacman_sprite.py | 13 ++ livrables/1/pacmap.py | 242 +++++++++++++++++++++++++++++++ livrables/1/pacmap_maze1.png | Bin 0 -> 12025 bytes livrables/1/pacmap_rule.txt | 9 ++ livrables/1/physic_engine.py | 21 +++ livrables/1/test_connexity.py | 45 ++++++ livrables/1/test_load_picture.py | 13 ++ 10 files changed, 545 insertions(+) create mode 100644 livrables/1/README.txt create mode 100755 livrables/1/graphic.py create mode 100755 livrables/1/pacman.py create mode 100755 livrables/1/pacman_sprite.py create mode 100755 livrables/1/pacmap.py create mode 100644 livrables/1/pacmap_maze1.png create mode 100644 livrables/1/pacmap_rule.txt create mode 100755 livrables/1/physic_engine.py create mode 100755 livrables/1/test_connexity.py create mode 100755 livrables/1/test_load_picture.py diff --git a/livrables/1/README.txt b/livrables/1/README.txt new file mode 100644 index 0000000..e69de29 diff --git a/livrables/1/graphic.py b/livrables/1/graphic.py new file mode 100755 index 0000000..02d5521 --- /dev/null +++ b/livrables/1/graphic.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +import pygame as pg +import pacman_sprite +import pacman as m_pacman # m_ for module to avoid conflicts +from physic_engine import PhysicEngine +import pacmap as m_pacmap +import sys + +pg.init() + +class Screen: + def __init__(self, size, pacmap: m_pacmap.Map, physic_engine: PhysicEngine, pacman: m_pacman.Pacman): + self.screen = pg.display.set_mode(size) + # self.screen.set_caption("Pacman") + self.physic_engine = physic_engine + self.pacman = pacman + self.pacman_sprite = pacman_sprite.PacmanSprite(size[0]/28) + self.clock = pg.time.Clock() + self.max_fps = 40 + self.entity_group = pg.sprite.Group(self.pacman_sprite) + self.loop() + + + def user_events(self): + key = pg.key.get_pressed() + if key[pg.K_UP]: + pacman.set_next_dir(m_pacman.direction.up) + if key[pg.K_DOWN]: + pacman.set_next_dir(m_pacman.direction.down) + if key[pg.K_LEFT]: + pacman.set_next_dir(m_pacman.direction.left) + if key[pg.K_RIGHT]: + pacman.set_next_dir(m_pacman.direction.right) + + def refresh(self): + """refresh/redraw all""" + pac_x, pac_y = self.pacman.position + pac_res = self.pacman.resolution + pacmap.draw(self.screen) + self.pacman_sprite.rect.x = int(pac_x / 28 / pac_res * self.screen.get_width()) - 10 + self.pacman_sprite.rect.y = int(pac_y / 31 / pac_res * self.screen.get_height()) - 10 + self.entity_group.draw(self.screen) + + + def create_maze_surface(self): + pass + + def loop(self): + while 1: + self.screen.fill((0, 0, 0)) + for event in pg.event.get(): + if event.type == pg.QUIT: + sys.exit() + + self.user_events() + self.physic_engine.move_all() + self.refresh() + + self.clock.tick(self.max_fps) + pg.display.flip() + +if __name__ == '__main__': + pacman = m_pacman.Pacman((1,1)) + pacmap = m_pacmap.Map(maze_img_file="pacmap_maze1.png") + phys_engine = PhysicEngine(pacmap, pacman) + screen = Screen((560, 620), pacmap, phys_engine, pacman) diff --git a/livrables/1/pacman.py b/livrables/1/pacman.py new file mode 100755 index 0000000..867a261 --- /dev/null +++ b/livrables/1/pacman.py @@ -0,0 +1,135 @@ +import pacmap +import os + +from enum import IntEnum +from collections import namedtuple + +_tempdir = namedtuple("Direction", ["up", "down", "left", "right", "none"]) +direction = _tempdir( + up= (0, -1), + down=(0, 1), + left=(-1, 0), + right=(1, 0), + none=(0, 0) +) +pacdot_counter = 88 +score = 0 +lives = 3 + +class FruitType(IntEnum): + A = 0 + +class Fruit: + def __init__(self, fruit_type, score, position = (0, 0)): + self.fruit_type = fruit_type + self.score = score + self.position = position + + +class Pacman: + def __init__(self, position=[0, 0], map_size=(28, 31), resolution=10): + self.position = [position[0]*resolution+int(resolution/2), position[1]*resolution+int(resolution/2)] + self.direction = direction.right + self.next_direction = direction.none + self.super_power = 0 # Counter of super pacdots in effect (> 0 means super power is active) + self.ghost_combo = 0 + self.size = (1.8, 1.8) # size related to tile size + self.speed = 0.1 + self.resolution = resolution # when pacman in 0:10 he's in the 1st cell if resolution=10 !!! must be even number + self.map_size = map_size + + def matrix_position(self): + return int(self.position[0] / self.resolution), int(self.position[1] / self.resolution) + + def next_matrix_position(self): + next_x = int(self.position[0] / self.resolution + self.direction[0]) % self.map_size[0] + next_y = int(self.position[1] / self.resolution + self.direction[1]) % self.map_size[1] + return next_x, next_y + + def has_super_power(self): + return self.super_power > 0 + + def eat_pacdot(self, dot_map): + global score + global pacdot_counter + dot_map.dots_map[self.matrix_position()[0]][self.matrix_position()[1]] = pacmap.DotTile.NDT + pacdot_counter -= 1 + score += 10 + if pacdot_counter == 0: + game_over("win") + + def eat_super_pacdot(self, dot_map): + global score + dot_map.dots_map[self.matrix_position()[0]][self.matrix_position()[1]] = pacmap.DotTile.NDT + score += 50 + self.super_power += 1 + + # TODO + # Requires UNIX - or use of async + # pid = os.fork() + #if pid == 0: + # return + + #os.sleep(10) + self.super_power -= 1 + self.ghost_combo = 0 + + def eat_fruit(self, fruit, dot_map): + global score + dot_map.dots_map[self.matrix_position()[0]][self.matrix_position()[1]] = pacmap.DotTile.NDT + score += fruit.score + + def eat_ghost(self, ghost): + global score + ghost.despawn_and_respawn() + self.ghost_combo += 1 + score += (2 ** self.ghost_combo) * 100 + + def get_eaten(self, dot_map): + global lives + #TODO score loss ? + self.position = dot_map.spawn_point #TODO + lives -= 1 + if lives < 0: # à vérifier + game_over() + + def is_at_center_tile(self): + """return True if pacman is at the center of a tile else False""" + clause_1 = not self.position[0] % (self.resolution/2) and self.position[0] % self.resolution + clause_2 = not self.position[1] % (self.resolution/2) and self.position[1] % self.resolution + return clause_1 and clause_2 + + def change_dir(self, new_dir): + self.direction = new_dir + + def set_next_dir(self, next_dir): + if next_dir[0] and self.direction[0]: + self.change_dir(next_dir) + elif next_dir[1] and self.direction[1]: + self.change_dir(next_dir) + else: + self.next_direction = next_dir + + def change_to_next_dir(self): + self.direction = self.next_direction + self.next_direction = direction.none + + def get_next_dir_tile(self): + """return x, y corresponding to the tile if we move with next_direction""" + next_x = int(self.position[0] / self.resolution + self.next_direction[0]) % self.map_size[0] + next_y = int(self.position[1] / self.resolution + self.next_direction[1]) % self.map_size[1] + return next_x, next_y + + def move(self): + self.position[0] = (self.position[0] + self.direction[0]) % (self.resolution * self.map_size[0]) + self.position[1] = (self.position[1] + self.direction[1]) % (self.resolution * self.map_size[1]) + +def game_over(status = "lose"): + #TODO + pass + + + + + + diff --git a/livrables/1/pacman_sprite.py b/livrables/1/pacman_sprite.py new file mode 100755 index 0000000..4ecfe76 --- /dev/null +++ b/livrables/1/pacman_sprite.py @@ -0,0 +1,13 @@ +"""Pacman Graphic Object""" +import pygame as pg + +class PacmanSprite(pg.sprite.Sprite): + def __init__(self, size): + super().__init__() + self.image = pg.Surface([size, size]) + half_size = int(size / 2) + pg.draw.circle(self.image, (255, 255, 0), (half_size, half_size), half_size) + + # Fetch the rectangle object that has the dimensions of the image + # Update the position of this object by setting the values of rect.x and rect.y + self.rect = self.image.get_rect() \ No newline at end of file diff --git a/livrables/1/pacmap.py b/livrables/1/pacmap.py new file mode 100755 index 0000000..7cb35d1 --- /dev/null +++ b/livrables/1/pacmap.py @@ -0,0 +1,242 @@ +from enum import IntEnum +from copy import deepcopy +from PIL import Image +import pygame +import os + +class DotTile(IntEnum): + NDT = 0 # no dot + SPD = 1 # small pac-dot + BPD = 2 # big pac-dot + FRT = 3 # fruit + +class PhysTile(IntEnum): + GRD = 0 # ground + WAL = 1 # wall + GSD = 2 # ghost door + GWL = 3 # ghost cell wall + GCF = 4 # ghost cell floor + TPT = 5 # teleporter tile + FIT = 6 # fully inaccessible tile + # ghost-cell ground as FIT ? verify no pac-dot here + +class CrossTile(IntEnum): + # code where we can find another GRD/TPT Tile from a given Tile + # with binary masks + UP = 1 # 0001 + DOWN = 2 # 0010 + LEFT = 4 # 0100 + RIGHT = 8 # 1000 + # direction changement if != 3 or != 12 + +class Map: + """ + Pacman maps size is 28×31 + """ + + width = 28 + height = 31 + # tile_size = 16 # tile subdivision for dynamic movement + + def __init__(self, phys_map = [], dots_map = [], maze_img_file=""): + """ + physic_map is the array containing elements: + 0: ground tile + 1: wall + 2: ghost door + 3: teleporter (no need to precise which to go because we assume it will + be at the opposite map tile) + 4: fully inaccessible tile (basically tiles that represent the "in-wall" + space) + + dots_map is a layer on top of the physic_map of the same dimension which contains + 0: no dot + 1: small pac-dot + 2: big pac-dot + """ + self._surf = pygame.Surface((Map.width * 20, Map.height * 20)) + if maze_img_file and os.path.isfile(maze_img_file): + try: + self.phys_map = self.decode_map(maze_img_file) + except Exception as e: + raise e + else: + self.phys_map = phys_map # in the first part we assume phys_map is correct and no need to verify + self.dots_map = dots_map + self.intersect_map = [] # TODO - the layer which contains intersections pre-calculated + + + def verify(self, phys_map, dots_map) -> bool: + """ + This method will verify if a given map is valid or not + Return True if correct else False + + we will assume + there are only: + - 1 ghost door + - 4 big pac-dots + - 240 pac-dots + + Each ground tile must have at least 2 ground tile neighboors because + there is no dead-end tile in pac-man + Each dot must be on a ground tile (not even a teleporter one) + """ + + if not (len(phys_map) == len(dots_map) == 31): + return False + + for i in range(len(phys_map)): + if not (len(phys_map[i]) == len(dots_map[i]) == 28): + return False + + # 1 ghost door verification + if sum(sub_arr.count(PhysTile.GSD) for sub_arr in phys_map) != 1: + return False + + # # 4 big pac dots + if sum(sub_arr.count(DotTile.BPD) for sub_arr in dots_map) != 4: + return False + + # # 240 small pac-dots + if sum(sub_arr.count(DotTile.SPD) for sub_arr in dots_map) != 240: + return False + + # dots are only in ground tile + for row in range(len(phys_map)): + for col in range(len(phys_map[0])): + if dots_map[row][col] and phys_map[row][col]: # no dot = 0; ground = 0 + return False + + # odd number of teleporter tiles + teleporter_count = sum(sub_arr.count(PhysTile.TPT) for sub_arr in phys_map) + if teleporter_count % 2: + return False + + edges = phys_map[0][1:] + phys_map[-1][1:] + [sub[0] for sub in phys_map] \ + + [sub[-1] for sub in phys_map][1:-1] + + # no ground tile on edges + if PhysTile.GRD in edges: + return False + + # teleporters are in front of the other + for col in range(1, len(phys_map[0])-1): + if phys_map[0][col] == PhysTile.TPT or phys_map[-1][col] == PhysTile.TPT: + if phys_map[0][col] == phys_map[-1][col]: + teleporter_count -= 2 + else: + return False + for row in range(1, len(phys_map)-1): + if phys_map[row][0] == PhysTile.TPT or phys_map[row][-1] == PhysTile.TPT: + if phys_map[row][0] == phys_map[row][-1]: + teleporter_count -= 2 + else: + return False + + if teleporter_count: # not all teleporters are on edges + return False + + # no teleporter on corners + if any(PhysTile.TPT in (phys_map[0][0], phys_map[0][-1], + phys_map[self.height-1][0], phys_map[-1][-1])): + return False + + # now we have to verify there is no dead-end ground tile + for col in range(1, self.width-1): + for row in range(1, self.height-1): + cpt = 0 + for x, y in ((0, 1), (1, 0), (-1, 0), (0, -1)): + if phys_map[row+y][col+x] in (PhysTile.GRD, PhysTile.TPT): + cpt += 1 + if cpt == 2: + break + if cpt < 2: + return False + # we have to verify if there is only 1 connexity component of (PhysTile.GRD, PhysTile.TPT, PhysTile.BPD, PhysTile.SPD) + if not connex(phys_map): + return False + + return True + + def get_tile(self, x, y): + return self.phys_map[y][x] + + def create_cross_layer(self): + dictionnary = { + (0, 1):CrossTile.DOWN, + (0, -1): CrossTile.UP, + (1, 0): CrossTile.RIGHT, + (-1, 0): CrossTile.LEFT + } + for row in range(self.height): + self.intersect_map.append([]) + for col in range(self.width): + if not self.get_tile(col, row) in (PhysTile.GRD, PhysTile.TPT): + self.intersect_map[-1].append(0) + if self.get_tile(col, row) in (PhysTile.GRD, PhysTile.TPT): + cpt = 0 + for dir in dictionnary: + test_col = (col + dir[0]) % self.width + test_row = (row + dir[1]) % self.height + if self.get_tile(test_col, test_row) in (PhysTile.GRD, PhysTile.TPT): + cpt |= dictionnary[dir] + self.intersect_map[-1].append(cpt) + + def draw(self, surf): + surf.blit(self._surf, (0, 0)) + + def decode_map(self, img_file): + img = Image.open(img_file) + dictionnary = { + (0 , 0, 0): PhysTile.GRD, + (255, 0, 255): PhysTile.GCF, + (0 , 0, 255): PhysTile.WAL, + (0 , 255, 0): PhysTile.GSD, + (0 , 255, 255): PhysTile.GWL, + (255, 0, 0): PhysTile.TPT, + (255, 255, 0): PhysTile.FIT, + } + data = list(img.getdata()) + matrix = [] + for row in range(img.height): + matrix.append([]) + for col in range(img.width): + try: + color = data[col + row*img.width][:3] + tile = dictionnary[color] # avoid alpha component + self._surf.fill(color, pygame.Rect(col*20, row*20, 20, 20)) + except: + raise ValueError("Pixel " + str(col) + "," + str(row) + " is invalid") + matrix[-1].append(tile) + return matrix + + + +def explore(matrix, x=-1, y=-1): + """explore the given matrix and change it (GRD and TPT become FIT)""" + if x < 0 and y < 0: + # searching the beginning + for row in range(len(matrix)): + for col in range(len(matrix[0])): + if matrix[row][col]: + explore(matrix, col, row) + elif matrix[y][x] in (PhysTile.TPT, PhysTile.GRD): + matrix[y][x] = PhysTile.FIT + for col, row in ((0, 1), (1, 0), (0, -1), (-1, 0)): + explore(matrix, (x+col)%len(matrix[0]), (y+row)%len(matrix)) + + +def connex(matrix, x=-1, y=-1): + """return True if the matrix is connex - has only one connexity part""" + temp = deepcopy(matrix) + explore(temp) + if any(tile in (PhysTile.GRD, PhysTile.TPT) for row in temp for tile in row): + return False + return True + +def get_one_in_digit(number): + cpt = 0 + while number: + cpt += number & 1 + number //= 2 + return cpt diff --git a/livrables/1/pacmap_maze1.png b/livrables/1/pacmap_maze1.png new file mode 100644 index 0000000000000000000000000000000000000000..503e9b941b93768c767e76ca646138a8aa94dbea GIT binary patch literal 12025 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 1: + matrix = decode_map(sys.argv[1]) + else: + matrix = decode_map("../pacmap_maze1.png") + for row in matrix: + print() + for el in row: + print(el.value, end=' ') \ No newline at end of file