mirror of
https://github.com/Noettore/AdventOfCode.git
synced 2025-10-14 19:26:39 +02:00
96 lines
2.8 KiB
Python
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()
|