diff --git a/2020-python/inputs/day_17 b/2020-python/inputs/day_17 new file mode 100644 index 0000000..d8d1d16 --- /dev/null +++ b/2020-python/inputs/day_17 @@ -0,0 +1,8 @@ +##.##### +#.##..#. +.##...## +###.#... +.####### +##....## +###.###. +.#.#.#.. diff --git a/2020-python/solutions/day_17.py b/2020-python/solutions/day_17.py new file mode 100644 index 0000000..2845e57 --- /dev/null +++ b/2020-python/solutions/day_17.py @@ -0,0 +1,95 @@ +"""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() diff --git a/README.md b/README.md index 1bf7564..6c93768 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,4 @@ | [Day 14](https://adventofcode.com/2020/day/14) | [64.058ms](./2020-python/solutions/day_14.py) | [56.398ms](./2020-python/solutions/day_14.py) | | [Day 15](https://adventofcode.com/2020/day/15) | [4.463s](./2020-python/solutions/day_15.py) | [718.712ms](./2020-python/solutions/day_15.py) | | [Day 16](https://adventofcode.com/2020/day/16) | [11.108ms](./2020-python/solutions/day_16.py) | [2.903ms](./2020-python/solutions/day_16.py) | +| [Day 17](https://adventofcode.com/2020/day/17) | [743.804ms](./2020-python/solutions/day_17.py) | [836.255ms](./2020-python/solutions/day_17.py) |