#!/usr/bin/env python3 from enum import IntEnum from copy import deepcopy from PIL import Image 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 Map: """ Pacman maps size is 28×31 """ width = 28 * 2 + 1 height = 31 * 2 + 1 # 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 """ if maze_img_file and os.path.isfile(maze_img_file): print('coucou') try: self.phys_map = 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 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 decode_map(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: tile = dictionnary[data[col + row*img.width][:3]] # avoid alpha component except: raise ValueError("Pixel " + str(col) + "," + str(row) + " is invalid") matrix[-1].append(tile) return matrix