2019-12-24 - Planet of Discord
(original .ipynb)
Day 24 puzzle input is a (mine is here) sort of cellular automata system. Part 1 involves running the system until a certain calculation (called "biodiversity") returns a value previously seen. Part 2 involves modifying this to be weirdly recursive in a way that is too complex to summarise in a single sentence (check the linked problem definition).
import functools import operator puzzle_input_raw = open("puzzle_input/day24.txt").read() state = [ [ c for c in line ] for line in puzzle_input_raw.split("\n") if line.strip() ] # A bug dies (becoming an empty space) unless there is exactly one bug # adjacent to it. # An empty space becomes infested with a bug if exactly one or two bugs # are adjacent to it. def next_cell_value(state, r, c): # count the bugs bugs = 0 if r > 0 and state[r-1][c] == "#": bugs += 1 if r < 4 and state[r+1][c] == "#": bugs += 1 if c > 0 and state[r][c-1] == "#": bugs += 1 if c < 4 and state[r][c+1] == "#": bugs += 1 if state[r][c] == "#" and bugs != 1: return "." if state[r][c] == "." and (bugs == 1 or bugs == 2): return "#" return state[r][c] def advance_state(state): new_state = [] for r, row in enumerate(state): new_row = [] for c, col in enumerate(row): new_row.append(next_cell_value(state, r, c)) new_state.append(new_row) return new_state def calculate_biodiversity(state): flattened_state = functools.reduce(operator.iconcat, state, []) xxx = [ 2 ** i for i, tile in enumerate(flattened_state) if tile == "#" ] return sum(xxx) biodiversities = set() count = 0 while True: biodiversity = calculate_biodiversity(state) if biodiversity in biodiversities: print(biodiversity) break else: biodiversities.add(biodiversity) state = advance_state(state) count += 1 from pprint import pprint print(count) pprint(state)
19923473 62 [['#', '.', '.', '.', '#'], ['.', '.', '.', '.', '#'], ['.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.'], ['#', '#', '.', '.', '#']]
# part 2 - extra dimensions added, so I rewrote this completely. # to reduce likelihood of any errors I made LOTS of tiny # little functions that are easy to read and debug even # though it makes the solution more verbose. def count_bugs_parent(grids, l, r, c): if l == 0: return 0 parent = grids[l - 1] bugs = 0 if c == 0: bugs += parent[2][1] if c == 4: bugs += parent[2][3] if r == 0: bugs += parent[1][2] if r == 4: bugs += parent[3][2] return bugs def count_bugs_child(grids, l, r, c): if l == len(grids) - 1: return 0 child = grids[l + 1] bugs = 0 if r == 2 and c == 1: bugs += sum([child[n][0] for n in range(5)]) if r == 2 and c == 3: bugs += sum([child[n][4] for n in range(5)]) if r == 1 and c == 2: bugs += sum(child[0]) if r == 3 and c == 2: bugs += sum(child[4]) return bugs def count_bugs_left(grids, l, r, c): if c == 0: return 0 return grids[l][r][c-1] def count_bugs_right(grids, l, r, c): if c == 4: return 0 return grids[l][r][c+1] def count_bugs_up(grids, l, r, c): if r == 0: return 0 return grids[l][r-1][c] def count_bugs_down(grids, l, r, c): if r == 4: return 0 return grids[l][r+1][c] def count_nearby_bugs(grids, l, r, c): return sum([ count_bugs_left(grids, l, r, c), count_bugs_right(grids, l, r, c), count_bugs_up(grids, l, r, c), count_bugs_down(grids, l, r, c), count_bugs_parent(grids, l, r, c), count_bugs_child(grids, l, r, c) ]) def bug_to_int(cell): if cell == "#": return 1 return 0 def parse_grid(grid_str): return [ [ bug_to_int(c) for c in line ] for line in grid_str.split("\n") if line.strip() ] def step_cell(grids, l, r, c): nearby_bugs = count_nearby_bugs(grids, l, r, c) # center tile doesn't ever change if r == 2 and c == 2: return 0 if grids[l][r][c] == 1: # A bug dies (becoming an empty space) unless there is exactly one bug # adjacent to it. if nearby_bugs != 1: return 0 return 1 if grids[l][r][c] == 0: # An empty space becomes infested with a bug if exactly one or two bugs # are adjacent to it. if (nearby_bugs == 1 or nearby_bugs == 2): return 1 return 0 raise Exception(f"Invalid bug count on cell: {grids[l][r][c]}") def step_grid(grids, l): new_grid = [] for r, row in enumerate(grids[l]): new_row = [] for c, col in enumerate(row): new_row.append(step_cell(grids, l, r, c)) new_grid.append(new_row) return new_grid empty_grid_str = """ ..... ..... ..... ..... ..... """ test_grid_str = """ ....# #..#. #..## ..#.. #.... """ def empty_grid(): return parse_grid(empty_grid_str) def copy_grid(grid): return [ [ c for c in row] for row in grid ] def any_bugs(grid): return 0 != sum([sum(row) for row in grid]) def count_bugs(grids): # could be a big list comp but ... no total = 0 for grid in grids: for row in grid: total += sum(row) return total # all_grids = [ empty_grid(), parse_grid(test_grid_str), empty_grid() ] all_grids = [ empty_grid(), parse_grid(puzzle_input_raw), empty_grid() ] minutes = 200 while minutes > 0: minutes -= 1 new_all_grids = [] for l, grid in enumerate(all_grids): new_all_grids.append(step_grid(all_grids, l)) if (any_bugs(all_grids[0])): new_all_grids.insert(0, empty_grid()) if (any_bugs(all_grids[len(all_grids)-1])): new_all_grids.append(empty_grid()) all_grids = new_all_grids print(count_bugs(all_grids))
1902