advent-of-code/2020-python/solutions/day_11.py

112 lines
3.2 KiB
Python
Raw Normal View History

2021-11-01 16:40:46 +01:00
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "11.txt"
directions = [
(-1, -1), # NW
(0, -1), # N
(1, -1), # NE
(-1, 0), # W
(1, 0), # E
(-1, 1), # SW
(0, 1), # S
(1, 1), # SE
]
def __str__(self):
return "Day 11: Seating System"
def parse_input(self, data):
return [[*l] for l in data.split()]
def solve(self, puzzle_input):
return self.update_seats(puzzle_input)
def solve_again(self, puzzle_input):
return self.update_seats(puzzle_input, tolerance=5, in_view=True)
def update_seats(self, seats, tolerance=4, in_view=False):
maxy = len(seats)
maxx = len(seats[0])
states = [seats]
keep_alive = True
rearranged = [s[:] for s in seats]
while keep_alive:
current = states[-1]
rearranged = self.tick(current, maxy, maxx, tolerance, in_view)
states.append([r[:] for r in rearranged])
if current == rearranged:
keep_alive = False
return len(
[*filter(lambda x: x == "#", "".join(["".join(r) for r in rearranged]))]
)
def tick(self, current, maxy, maxx, tolerance, in_view):
rearranged = [s[:] for s in current]
for y in range(maxy):
for x in range(maxx):
v = current[y][x]
if v == ".":
continue
if in_view:
adjacent = self.get_in_view(current, y, x)
else:
adjacent = self.get_adjacent(current, y, x)
adjval = [a for _, a in adjacent]
if v == "L":
v = self.occupy_if_empty(v, adjval)
else:
v = self.empty_if_occupied(v, adjval, tolerance)
rearranged[y][x] = v
return rearranged
def get_adjacent(self, seats, posy, posx, directions=None, factor=1):
adjacent = []
directions = directions or self.directions[:]
for x, y in directions:
r = y * factor + posy
c = x * factor + posx
if r < 0 or c < 0:
continue
try:
adjacent.append(((x, y), seats[r][c]))
except IndexError:
pass
return adjacent
def get_in_view(self, seats, posy, posx):
found = []
directions = True
distance = 1
directions = self.directions[:]
while directions:
adjacents = self.get_adjacent(
seats, posy, posx, directions, factor=distance
)
found += [a for a in adjacents if a[1] != "."]
directions = [a[0] for a in adjacents if a[1] == "."]
distance += 1
return found
def occupy_if_empty(self, seat, neighbours):
if seat == "L" and not any(n == "#" for n in neighbours):
return "#"
return seat
def empty_if_occupied(self, seat, neighbours, tolerance=4):
if seat == "#" and sum(map(lambda s: s == "#", neighbours)) >= tolerance:
return "L"
return seat
if __name__ == "__main__":
solution = Solution()
solution.show_results()