advent-of-code/2020/Python/day11.py

105 lines
3.3 KiB
Python
Raw Permalink Normal View History

2020-12-11 18:57:24 +01:00
from typing import Union, Callable
2020-12-11 14:24:24 +01:00
def parse_data() -> list[str]:
data = []
with open("input.txt") as file:
for line in file:
_line = line.rstrip()
data.append(_line)
return data
2020-12-11 18:10:06 +01:00
# Count occupied seats *adjacent* to the seat at position (x, y)
2020-12-11 18:57:24 +01:00
def count_adjacent(x: int, y: int, data: list[str]) -> int:
2020-12-11 14:24:24 +01:00
2020-12-11 18:10:06 +01:00
def check_occupied(_x: int, _y: int, _data: list[str]) -> int:
if 0 <= _x < len(data[0]) and 0 <= _y < len(data):
if data[_y][_x] == '#':
return 1
return 0
2020-12-11 14:24:24 +01:00
2020-12-11 18:10:06 +01:00
places = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
2020-12-11 14:24:24 +01:00
result = 0
2020-12-11 18:10:06 +01:00
for _x, _y in places:
result += check_occupied(x + _x, y + _y, data)
2020-12-11 14:24:24 +01:00
return result
2020-12-11 18:57:24 +01:00
def generate_new_grid(past_data: list[str], counting_func: Callable, threshold: int):
2020-12-11 14:24:24 +01:00
output = []
for y in range(len(past_data)):
line = ""
for x in range(len(past_data[0])):
2020-12-11 18:57:24 +01:00
if past_data[y][x] == "#" and counting_func(x, y, past_data) >= threshold:
2020-12-11 14:24:24 +01:00
new_char = 'L'
2020-12-11 18:57:24 +01:00
elif past_data[y][x] == 'L' and counting_func(x, y, past_data) == 0:
2020-12-11 14:24:24 +01:00
new_char = '#'
else:
new_char = past_data[y][x]
line += new_char
output.append(line)
return output
2020-12-11 18:57:24 +01:00
def find_stable_state(grid: list[str], counting_func: Callable, threshold: int = 4, limit_iter: int = 10000) -> Union[None, list[str]]:
2020-12-11 14:24:24 +01:00
2020-12-11 18:57:24 +01:00
def is_it_different(old_data: list[str], new_data: list[str]) -> bool:
for y in range(len(old_data)):
for x in range(len(old_data[0])):
if old_data[y][x] != new_data[y][x]:
return True
return False
2020-12-11 14:24:24 +01:00
for i in range(limit_iter):
2020-12-11 18:57:24 +01:00
new_state = generate_new_grid(grid, counting_func, threshold)
2020-12-11 14:24:24 +01:00
if not is_it_different(grid, new_state):
return new_state
grid = new_state
return None
2020-12-11 18:10:06 +01:00
2020-12-11 14:24:24 +01:00
def solve_p1(input_data: list[str]) -> int:
2020-12-11 18:57:24 +01:00
final_state = find_stable_state(input_data, counting_func=count_adjacent, threshold=4)
2020-12-11 14:24:24 +01:00
if final_state is None:
return -1
result = 0
2020-12-11 18:10:06 +01:00
for line in final_state:
result += line.count('#')
2020-12-11 14:24:24 +01:00
return result
2020-12-11 18:57:24 +01:00
# Count occupied seats *raycast* from the seat at position (x, y)
def count_raycast(x: int, y: int, data: list[str]) -> int:
def check_occupied_ray(start: (int, int), direction: (int, int), _data: list[str]) -> int:
curr_x, curr_y = start
dir_x, dir_y = direction
while 0 <= (curr_x := curr_x + dir_x) < len(data[0]) and 0 <= (curr_y := curr_y + dir_y) < len(data):
if data[curr_y][curr_x] == '#':
return 1
elif data[curr_y][curr_x] == 'L':
return 0
return 0
directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
result = 0
for direction in directions:
result += check_occupied_ray((x, y), direction, data)
return result
def solve_p2(input_data: list[str]) -> int:
final_state = find_stable_state(input_data, counting_func=count_raycast, threshold=5)
if final_state is None:
return -1
result = 0
for line in final_state:
result += line.count('#')
return result
DATA = parse_data()
print(solve_p1(DATA))
print(solve_p2(DATA))