Files
AdventOfCode/2020/solutions/day_17.py
2020-12-25 23:50:29 +01:00

96 lines
2.8 KiB
Python

"""AOC 2020 Day 17"""
import pathlib
import time
import itertools
TEST_INPUT = """.#.
..#
###"""
def read_input(input_path: str) -> str:
"""take input file path and return a str with the file's content"""
with open(input_path, 'r') as input_file:
input_data = input_file.read().strip()
return input_data
def extract(input_data: str, dims: int) -> set:
"""take input data and return the appropriate data structure"""
alive_cells = set()
zeros = [0]*(dims-2)
for x_cell, row in enumerate(input_data.split('\n')):
for y_cell, cell in enumerate(row):
if cell == '#':
alive_cells.add((x_cell, y_cell, *zeros))
return alive_cells
def count_alive_neighbors(alive_cells: set, coords: tuple) -> int:
"""return the number of alive neighbors of a given cell"""
alive = 0
ranges = ((c-1, c, c+1) for c in coords)
for cell in itertools.product(*ranges):
if cell in alive_cells:
alive += 1
if coords in alive_cells:
alive -= 1
return alive
def get_cube_limits(alive_cells: set, dims: int) -> list:
"""return cube bounds incremented for expansion"""
limits = list()
for i in range(dims):
low = float('Inf')
high = -float('Inf')
for row in alive_cells:
if row[i] < low:
low = row[i]
elif row[i] > high:
high = row[i]
limits.append(range(low-1, high+2))
return limits
def step_cube(alive_cells: set, dims: int) -> set:
"""return next step alive cells"""
next_step = set()
for cell in itertools.product(*get_cube_limits(alive_cells, dims)):
alive_neighbors = count_alive_neighbors(alive_cells, cell)
if (cell in alive_cells and alive_neighbors in (2, 3)) or alive_neighbors == 3:
next_step.add(cell)
return next_step
def part1(input_data: str) -> int:
"""part1 solver"""
cube = extract(input_data, 3)
for _ in range(6):
cube = step_cube(cube, 3)
return len(cube)
def part2(input_data: str) -> int:
"""part2 solver"""
cube = extract(input_data, 4)
for _ in range(6):
cube = step_cube(cube, 4)
return len(cube)
def test_input_day_17():
"""pytest testing function"""
assert part1(TEST_INPUT) == 112
assert part2(TEST_INPUT) == 848
def test_bench_day_17(benchmark):
"""pytest-benchmark function"""
benchmark(main)
def main():
"""main function"""
input_path = str(pathlib.Path(__file__).resolve().parent.parent) + "/inputs/" + str(pathlib.Path(__file__).stem)
start_time = time.time()
input_data = read_input(input_path)
print("Part 1: %d" % part1(input_data))
print("Part 2: %d" % part2(input_data))
end_time = time.time()
print("Execution time: %f" % (end_time-start_time))
if __name__ == "__main__":
main()