mirror of
https://github.com/Noettore/AdventOfCode.git
synced 2025-10-15 11:46:39 +02:00
AoC 2020: day11
Signed-off-by: Ettore Dreucci <ettore.dreucci@gmail.com>
This commit is contained in:
143
2020-python/solutions/day_11.py
Normal file
143
2020-python/solutions/day_11.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""AOC 2020 Day 11"""
|
||||
|
||||
import pathlib
|
||||
import time
|
||||
import copy
|
||||
|
||||
TEST_INPUT = """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"""
|
||||
|
||||
FLOOR = 0
|
||||
EMPTY_SEAT = 1
|
||||
OCCUPIED_SEAT = 2
|
||||
|
||||
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) -> list:
|
||||
"""take input data and return the appropriate data structure"""
|
||||
entries = []
|
||||
for row in input_data.split('\n'):
|
||||
entries.append([])
|
||||
for seat in row:
|
||||
if seat == '.':
|
||||
entries[-1].append(FLOOR)
|
||||
elif seat == 'L':
|
||||
entries[-1].append(EMPTY_SEAT)
|
||||
elif seat == '#':
|
||||
entries[-1].append(OCCUPIED_SEAT)
|
||||
else:
|
||||
raise ValueError("Invalid seat %s" % seat)
|
||||
return entries
|
||||
|
||||
def occupied_adjacent_neighbors(seats: list, row: int, column: int) -> int:
|
||||
"""return number of occupied adjacent neighbors of a given seat"""
|
||||
neigh_seats = [(0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1)]
|
||||
neighbors = 0
|
||||
rows = len(seats)
|
||||
columns = len(seats[0])
|
||||
for dy, dx in neigh_seats:
|
||||
nrow, ncolumn = row+dy, column+dx
|
||||
if 0 <= nrow < rows and 0 <= ncolumn < columns and seats[nrow][ncolumn] == OCCUPIED_SEAT:
|
||||
neighbors += 1
|
||||
return neighbors
|
||||
|
||||
def occupied_insight_neighbors(seats: list, row: int, column: int) -> int:
|
||||
"""return number of occupied in-sight neighbors of a given seat"""
|
||||
neigh_seats = [(0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1)]
|
||||
neighbors = 0
|
||||
rows = len(seats)
|
||||
columns = len(seats[0])
|
||||
for dy, dx in neigh_seats:
|
||||
nrow, ncolumn = row+dy, column+dx
|
||||
while 0 <= nrow < rows and 0 <= ncolumn < columns:
|
||||
seat = seats[nrow][ncolumn]
|
||||
if seat == OCCUPIED_SEAT:
|
||||
neighbors += 1
|
||||
break
|
||||
elif seat == EMPTY_SEAT:
|
||||
break
|
||||
nrow += dy
|
||||
ncolumn += dx
|
||||
return neighbors
|
||||
|
||||
def part1(entries: list) -> int:
|
||||
"""part1 solver"""
|
||||
seats = copy.deepcopy(entries)
|
||||
while True:
|
||||
new_grid = []
|
||||
changed = False
|
||||
for y, row in enumerate(seats):
|
||||
new_grid.append([])
|
||||
for x, seat in enumerate(row):
|
||||
neighbors = occupied_adjacent_neighbors(seats, y, x)
|
||||
if seat == EMPTY_SEAT and neighbors == 0:
|
||||
new_grid[-1].append(OCCUPIED_SEAT)
|
||||
changed = True
|
||||
elif seat == OCCUPIED_SEAT and neighbors >= 4:
|
||||
new_grid[-1].append(EMPTY_SEAT)
|
||||
changed = True
|
||||
else:
|
||||
new_grid[-1].append(seat)
|
||||
if changed:
|
||||
seats = new_grid
|
||||
else:
|
||||
return sum(row.count(OCCUPIED_SEAT) for row in seats)
|
||||
|
||||
def part2(entries: list) -> int:
|
||||
"""part2 solver"""
|
||||
seats = copy.deepcopy(entries)
|
||||
while True:
|
||||
new_grid = []
|
||||
changed = False
|
||||
for y, row in enumerate(seats):
|
||||
new_grid.append([])
|
||||
for x, seat in enumerate(row):
|
||||
neighbors = occupied_insight_neighbors(seats, y, x)
|
||||
if seat == EMPTY_SEAT and neighbors == 0:
|
||||
new_grid[-1].append(OCCUPIED_SEAT)
|
||||
changed = True
|
||||
elif seat == OCCUPIED_SEAT and neighbors >= 5:
|
||||
new_grid[-1].append(EMPTY_SEAT)
|
||||
changed = True
|
||||
else:
|
||||
new_grid[-1].append(seat)
|
||||
if changed:
|
||||
seats = new_grid
|
||||
else:
|
||||
return sum(row.count(OCCUPIED_SEAT) for row in seats)
|
||||
|
||||
def test_input_day_11():
|
||||
"""pytest testing function"""
|
||||
entries = extract(TEST_INPUT)
|
||||
assert part1(entries) == 37
|
||||
assert part2(entries) == 26
|
||||
|
||||
def test_bench_day_11(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)
|
||||
entries = extract(input_data)
|
||||
print("Part 1: %d" % part1(entries))
|
||||
print("Part 2: %d" % part2(entries))
|
||||
end_time = time.time()
|
||||
print("Execution time: %f" % (end_time-start_time))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user