offset_cache = {} with open('input.txt') as fh: start = [list(line.strip()) for line in fh] def print_world(world): if isinstance(world[0][0], str): for line in world: print(''.join(line)) else: for hyperworld in world: print_world(hyperworld) print('') def count_world(world): count = 0 if isinstance(world[0], str): return world.count('#') else: for hyperworld in world: count += count_world(hyperworld) return count def iter_offset(n): for d in [-1, 0, 1]: if n == 1: yield [d] else: for rest in iter_offset(n - 1): yield [d, *rest] def get_offset(n): if n not in offset_cache: offset_cache[n] = [p for p in iter_offset(n) if any(p)] return offset_cache[n] def get_pos(world, *pos): cur = world try: for i in pos: if i < 0: return '.' cur = cur[i] except IndexError: return '.' return cur def decide(world, *pos): neighbors = 0 for offset in get_offset(len(pos)): neighbor = [x + offset[i] for i, x in enumerate(pos)] if get_pos(world, *neighbor) == '#': neighbors += 1 if neighbors > 3: return False if get_pos(world, *pos) == '#': return neighbors in [2, 3] else: return neighbors == 3 def cycle(world, *pos): cur = world for _ in pos: cur = cur[0] if isinstance(cur, str): if decide(world, *pos): return '#' else: return '.' else: new = [] for i in range(-1, len(cur) + 1): new.append(cycle(world, *pos, i)) return new def run(world, n): for i in range(n): # print_world(world) world = cycle(world) print(count_world(world)) run([start], 6) run([[start]], 6)