Advent of Code 2020 day 11
Advent of Code 2020 day 11. Again this is a slightly sanitised version of the code used to submit my answers, but there’s a still a lot of duplication.
The trickiest and messiest part of this was dealing with negative list indexes for seats right at the edge of the grid. I briefly considered adding a gutter to the grid but that would only have caused a different assortment of edge cases.
Used pytest for this one as well. Nicer output than unittest, but for dead-simple cases like this I don’t see much difference.
import pytest
from pathlib import Path
def directions():
return [[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]]
def get_raw_input():
return (Path(__file__).parent/'day_11_input.txt').read_text()
def parse_raw_input(raw_input):
return [[char for char in line.strip()] for line in raw_input.strip().splitlines()]
def get_adjacent(grid, seat, position):
if (y := seat[0]+position[0]) < 0:
return None
if (x := seat[1]+position[1]) < 0:
return None
try:
return grid[y][x]
except IndexError:
return None
def get_visible(grid, seat, direction):
y = seat[0]+direction[0]
x = seat[1]+direction[1]
try:
while y >= 0 and x >= 0 and grid[y][x] == '.':
y += direction[0]
x += direction[1]
if y >= 0 and x >= 0:
return grid[y][x]
else:
return None
except IndexError:
return None
def count_all_occupied_adjacent(grid, seat):
return len(
[
result for result
in (get_adjacent(grid, seat, position_to_check) for position_to_check in directions())
if result == '#'
]
)
def count_all_occupied_visible(grid, seat):
return len(
[
result for result
in (get_visible(grid, seat, direction_to_check) for direction_to_check in directions())
if result == '#'
]
)
def count_all_occupied(grid):
return len([seat for row in grid for seat in row if seat == '#'])
def tick(grid, method, trigger):
new_grid = [[seat for seat in row] for row in grid]
last_count = 0
iterations = 0
while last_count != count_all_occupied(new_grid) or last_count == 0:
last_count = count_all_occupied(grid)
for y, row in enumerate(grid):
for x, seat in enumerate(row):
current_seat = grid[y][x]
if method == 'visible':
count_of_occupied = count_all_occupied_visible(grid, [y, x])
else:
count_of_occupied = count_all_occupied_adjacent(grid, [y, x])
if current_seat == 'L' and count_of_occupied == 0:
new_grid[y][x] = '#'
elif current_seat == '#' and count_of_occupied >= trigger:
new_grid[y][x] = 'L'
grid = [[seat for seat in row] for row in new_grid]
return last_count
class TestExamples():
@pytest.fixture
def example_input(self):
return """L.LL.LL.LL
LLLLLLL.LL
L.L.L..L..
LLLL.LL.LL
L.LL.LL.LL
L.LLLLL.LL
..L.L.....
LLLLLLLLLL
L.LLLLLL.L
L.LLLLL.LL"""
def test_part_one_example(self, example_input):
assert tick(parse_raw_input(example_input), 'adjacent', 4) == 37
def test_part_two_example(self, example_input):
assert tick(parse_raw_input(example_input), 'visible', 5) == 26
if __name__ == '__main__':
print(tick(parse_raw_input(get_raw_input()), 'adjacent', 4))
print(tick(parse_raw_input(get_raw_input()), 'visible', 5))