def part_a(data): seen = set() grid = ZGrid(data) while True: k = tuple(grid.items()) if k in seen: return sum(2**i for i, glyph in enumerate(grid.values()) if glyph == "#") seen.add(k) evolve_a(grid)
def __init__(self, data, part="a"): grid = ZGrid(data, on=".", off="#") if part == "b": [z0] = [z for z, v in grid.items() if v == "@"] pos = ("@0", "@1", "@2", "@3") for dz in [0, -1j, 1, 1j, -1]: grid[z0 + dz] = grid.off for p, dz in zip(pos, [-1 - 1j, 1 - 1j, 1 + 1j, -1 + 1j]): grid[z0 + dz] = p else: pos = ("@", ) graph = grid.graph(extra=frozenset(string.ascii_letters).union(pos)) self.all_keys = {k for k in graph.extra if k in string.ascii_lowercase} # map of the other keys that must be acquired before each key is acquired self.obstructions = {k: set() for k in self.all_keys} # precached distances between points of interest self.kdist = {} for k0 in self.all_keys: for p in pos: try: path = nx.shortest_path(graph, graph.extra[p], graph.extra[k0]) except nx.NetworkXNoPath: pass else: break else: raise Exception self.kdist[k0, p] = self.kdist[p, k0] = len(path) - 1 assert path[0] == graph.extra[p] assert path[-1] == graph.extra[k0] for p in path[1:-1]: if p in graph.extra.values(): k1 = grid[p].lower() self.obstructions[k0].add(k1) for k1, k2 in combinations(self.all_keys, 2): z1 = graph.extra[k1] z2 = graph.extra[k2] try: d = nx.shortest_path_length(graph, z1, z2) except nx.NetworkXNoPath: continue self.kdist[k1, k2] = self.kdist[k2, k1] = d state0 = pos, frozenset() AStar.__init__(self, state0, target=None)
#####.#.###.#######.#######.###.###.#.#.# #.......#.......#.#.#.#.#...#...#...#.#.# #####.###.#####.#.#.#.#.###.###.#.###.### #.......#.....#.#...#...............#...# #############.#.#.###.################### A O F N A A D M """ grid = ZGrid(data, on=".", off="#") h, w = np.array(grid).shape dzs = [-1j, 1, 1j, -1] # parse the warps outside = {} inside = {} for z, glyph in grid.items(): if glyph in string.ascii_uppercase: for dz in dzs: if grid.get(z + dz) == ".": zp = z + dz # actual position of portal name = glyph + grid.get(z - dz) # add other letter if 3 < z.real < w - 3 and 3 < z.imag < h - 3: side = inside else: side = outside if zp - z in {1j, 1}: name = name[::-1] # reverse the label side[name] = zp break state0 = outside.pop("AA")