diff --git a/src/pacmap.py b/src/pacmap.py new file mode 100755 index 0000000..0207823 --- /dev/null +++ b/src/pacmap.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +from enum import IntEnum +from copy import deepcopy + +class DotTile(IntEnum): + NDT = 0 # no dot + SPD = 1 # small pac-dot + BPD = 2 # big pac-dot + +class PhysTile(IntEnum): + GRD = 0 # ground + WAL = 1 # wall + GSD = 2 # ghost door + TPT = 3 # teleporter tile + FIT = 4 # fully inaccessible tile + # ghost-cell ground as FIT ? verify no pac-dot here + +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 = []): + """ + 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.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 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 diff --git a/src/test_connexity.py b/src/test_connexity.py new file mode 100755 index 0000000..e9553eb --- /dev/null +++ b/src/test_connexity.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +import pacmap + +maps = [ + [ + [4, 1, 1, 1, 3, 1, 1, 4], + [1, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 1, 1, 0, 1, 0, 1], + [3, 0, 1, 0, 0, 1, 0, 3], + [1, 0, 0, 0, 1, 0, 0, 1], + [1, 1, 1, 0, 0, 0, 1, 1], + [4, 4, 1, 1, 3, 1, 1, 4], + ], + [ + [4, 1, 1, 1, 3, 1, 1, 4], + [1, 1, 1, 1, 0, 1, 1, 1], + [1, 1, 1, 1, 0, 1, 1, 1], + [1, 1, 1, 1, 0, 1, 1, 1], + [1, 1, 1, 1, 0, 1, 1, 1], + [1, 1, 1, 1, 0, 1, 1, 1], + [4, 1, 1, 1, 3, 1, 1, 4], + ], + [ + [4, 1, 1, 1, 3, 1, 1, 4], + [1, 1, 1, 1, 0, 1, 1, 1], + [1, 1, 1, 1, 0, 1, 1, 1], + [1, 0, 0, 1, 0, 1, 1, 1], + [1, 0, 0, 1, 0, 1, 1, 1], + [1, 1, 1, 1, 0, 1, 1, 1], + [4, 1, 1, 1, 3, 1, 1, 4], + ], + [ + [4, 1, 1, 1, 3, 1, 1, 4], + [1, 1, 1, 1, 0, 1, 1, 1], + [1, 1, 1, 1, 0, 0, 0, 1], + [1, 1, 1, 1, 1, 1, 0, 1], + [3, 0, 0, 0, 0, 1, 0, 3], + [1, 1, 1, 1, 0, 1, 1, 1], + [4, 1, 1, 1, 3, 1, 1, 4], + ] +] + +for pac_map in maps: + is_connex = pacmap.connex(pac_map) + print(is_connex) \ No newline at end of file