import itertools import sys import numpy as np from numpy.typing import NDArray grid0 = np.array([[c == "#" for c in line] for line in sys.stdin.read().splitlines()]) # add an always off circle around grid0 = np.concatenate( [ np.zeros((grid0.shape[0] + 2, 1), dtype=bool), np.concatenate( [ np.zeros((1, grid0.shape[1]), dtype=bool), grid0, np.zeros((1, grid0.shape[1]), dtype=bool), ] ), np.zeros((grid0.shape[0] + 2, 1), dtype=bool), ], axis=1, ) moves = list(itertools.product([-1, 0, 1], repeat=2)) moves.remove((0, 0)) jjs, iis = np.meshgrid( np.arange(1, grid0.shape[0] - 1, dtype=int), np.arange(1, grid0.shape[1] - 1, dtype=int), ) iis, jjs = iis.flatten(), jjs.flatten() ins = iis[:, None] + np.array(moves)[:, 0] jns = jjs[:, None] + np.array(moves)[:, 1] def game_of_life(grid: NDArray[np.bool_]) -> NDArray[np.bool_]: neighbors_on = grid[ins, jns].sum(axis=1) cells_on = grid[iis, jjs] grid = np.zeros_like(grid) grid[iis, jjs] = (neighbors_on == 3) | (cells_on & (neighbors_on == 2)) return grid grid = grid0 n_steps = 4 if len(grid) < 10 else 100 for _ in range(n_steps): grid = game_of_life(grid) answer_1 = grid.sum() print(f"answer 1 is {answer_1}") n_steps = 5 if len(grid) < 10 else 100 grid = grid0 for _ in range(n_steps): grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True grid = game_of_life(grid) grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True answer_2 = sum(cell for line in grid for cell in line) print(f"answer 2 is {answer_2}")