Skip to content

Instantly share code, notes, and snippets.

@sreynen
Last active December 18, 2021 19:26
Show Gist options
  • Save sreynen/ddc56f86ac9361edf02a6c3754bdcfba to your computer and use it in GitHub Desktop.
Save sreynen/ddc56f86ac9361edf02a6c3754bdcfba to your computer and use it in GitHub Desktop.
Advent of Code 2021
file = open('1a.txt', 'r')
depths = [int(line) for line in file.readlines()]
# Part A
def increases(values):
return [value for index, value in enumerate(values)
if index > 0 and value > values[index - 1]]
print(len(increases(depths)))
# Part B
windows = [depth + depths[index - 1] + depths[index - 2]
for index, depth in enumerate(depths)
if index > 1]
print(len(increases(windows)))
from statistics import median
def is_invalid(line):
opens = ['(', '[', '{', '<']
closes = [')', ']', '}', '>']
stack = []
for character in list(line):
if character in opens:
stack.append(character)
elif opens[closes.index(character)] == stack[-1]:
stack.pop()
else:
return [False, character, stack]
return [True, None, stack]
# Part A
lines = [line.strip() for line in open('10a.txt', 'r').readlines()]
scores = {')': 3, ']': 57, '}': 1197, '>': 25137}
line_validity = [is_invalid(line) for line in lines]
error_scores = [scores[validity[1]] for validity in line_validity if not validity[0]]
print(sum(error_scores))
# Part B
incomplete_lines = [validity[2] for validity in line_validity if validity[0]]
autocomplete_scoring = ['(', '[', '{', '<']
incomplete_line_scores = []
for line in incomplete_lines:
score = 0
for open_chunk in reversed(line):
score *= 5
score += autocomplete_scoring.index(open_chunk) + 1
incomplete_line_scores.append(score)
print(median(incomplete_line_scores))
def step(state):
state['flashed_in_step'] = set()
state['grid'] = [[value + 1 for value in row] for row in state['grid']]
still_flashing = True
while still_flashing:
to_flash = [[row_index, column_index]
for row_index, row in enumerate(state['grid'])
for column_index, value in enumerate(row)
if value > 9
and tuple([row_index, column_index]) not in state['flashed_in_step']]
if len(to_flash):
state = flash(state, to_flash[0][0], to_flash[0][1])
else:
still_flashing = False
state['grid'] = [[0 if value > 9 else value for value in row] for row in state['grid']]
return state
def flash(state, row_index, column_index):
state['flashed_in_step'].add(tuple([row_index, column_index]))
state['flashes'] += 1
state['grid'][row_index][column_index] = 0
if row_index > 0:
state = increase_if_not_flashed(state, row_index - 1, column_index)
if column_index > 0:
state = increase_if_not_flashed(state, row_index - 1, column_index - 1)
if column_index < 9:
state = increase_if_not_flashed(state, row_index - 1, column_index + 1)
if row_index < 9:
state = increase_if_not_flashed(state, row_index + 1, column_index)
if column_index > 0:
state = increase_if_not_flashed(state, row_index + 1, column_index - 1)
if column_index < 9:
state = increase_if_not_flashed(state, row_index + 1, column_index + 1)
if column_index > 0:
state = increase_if_not_flashed(state, row_index, column_index - 1)
if column_index < 9:
state = increase_if_not_flashed(state, row_index, column_index + 1)
return state
def increase_if_not_flashed(state, row_index, column_index):
if tuple([row_index, column_index]) not in state['flashed_in_step']:
state['grid'][row_index][column_index] += 1
return state
lines = [[int(value) for value in line.strip()] for line in open('11a.txt', 'r').readlines()]
# Part A
state = dict(grid=lines, flashes=0, flashed_in_step=set())
for i in range(0, 100):
state = step(state)
print(state['flashes'])
# Part B
state = dict(grid=lines, flashes=0, flashed_in_step=set())
steps_taken = 0
while len(state['flashed_in_step']) < 100:
state = step(state)
steps_taken += 1
print(steps_taken)
from collections import defaultdict
def paths_between(start, end, can_connect, visited=[]):
valid_paths = []
if start in visited and start.lower() == start:
return []
if start == end:
return [visited + [end]]
visited.append(start)
for point in can_connect[start]:
if point not in visited or point.lower() != point:
valid_paths += paths_between(point, end, can_connect, visited.copy())
return valid_paths
def paths_between_repeat_small(start, end, can_connect, visited=[], small_repeated=False):
valid_paths = []
if visited.count(start) > 1 and start.lower() == start and small_repeated:
return []
if start == end:
return [visited + [end]]
visited.append(start)
for point in can_connect[start]:
if point not in visited or point.lower() != point:
valid_paths += paths_between_repeat_small(point, end, can_connect, visited.copy(), small_repeated)
elif point != 'start' and not small_repeated:
valid_paths += paths_between_repeat_small(point, end, can_connect, visited.copy(), True)
return valid_paths
paths = [line.strip().split('-') for line in open('12a.txt', 'r').readlines()]
connects_to = defaultdict(list)
for path in paths:
connects_to[path[0]].append(path[1])
connects_to[path[1]].append(path[0])
# Part A:
print(len(paths_between('start', 'end', connects_to)))
# Part B:
print(len(paths_between_repeat_small('start', 'end', connects_to)))
def print_grid(points):
max_x = max([point[0] for point in points])
max_y = max([point[1] for point in points])
for y in range(0, max_y + 1):
for x in range(0, max_x + 1):
if (x, y) in points:
print('#', end='')
else:
print('.', end='')
print('')
def fold_grid(points, fold):
if fold[0] == 'y':
not_folding = [point for point in points if point[1] < fold[1]]
folding = [(point[0], (fold[1] - (point[1] - fold[1]))) for point in points if point[1] > fold[1]]
else:
not_folding = [point for point in points if point[0] < fold[1]]
folding = [((fold[1] - (point[0] - fold[1])), point[1]) for point in points if point[0] > fold[1]]
return list(set(not_folding + folding))
lines = [line.strip() for line in open('13a.txt', 'r').readlines()]
points = []
folds = []
folding = False
for line in lines:
if folding:
folds.append([line.split()[2].split('=')[0], int(line.split()[2].split('=')[1])])
elif len(line) == 0:
folding = True
else:
points.append(tuple([int(value) for value in line.split(',')]))
# Part A
points = fold_grid(points, folds[0])
print(len(points))
# Part B
for fold in folds[1:]:
points = fold_grid(points, fold)
print_grid(points)
from collections import Counter
from itertools import pairwise
def step(pair_counts, letter_counts):
pairs = list(pair_counts)
new_pair_counts = Counter()
for pair in pairs:
if pair in replacements:
new_pair_counts[pair[0] + replacements[pair]] += pair_counts[pair]
new_pair_counts[replacements[pair] + pair[1]] += pair_counts[pair]
letter_counts[replacements[pair]] += pair_counts[pair]
else:
new_pair_counts[pair] += 1
return new_pair_counts
lines = [line.strip() for line in open('14a.txt', 'r').readlines()]
template = lines[0]
replacements = {line.split(' -> ')[0]: line.split(' -> ')[1] for line in lines[2:]}
pairs = [''.join(pair) for pair in pairwise(template)]
# Part A
pair_counts = Counter(pairs)
letter_counts = Counter(template)
for i in range(0, 10):
pair_counts = step(pair_counts, letter_counts)
letter_count_numbers = letter_counts.values()
print(max(letter_count_numbers) - min(letter_count_numbers))
# Part B
for i in range(0, 30):
pair_counts = step(pair_counts, letter_counts)
letter_count_numbers = letter_counts.values()
print(max(letter_count_numbers) - min(letter_count_numbers))
import networkx
def graph_from_grid(grid):
graph = networkx.DiGraph()
max_x = len(grid[0]) - 1
max_y = len(grid) - 1
for y in range(0, max_y + 1):
for x in range(0, max_x + 1):
if y > 0:
graph.add_edge(tuple([x, y]), tuple([x, y - 1]), weight=grid[y - 1][x])
if y < max_y:
graph.add_edge(tuple([x, y]), tuple([x, y + 1]), weight=grid[y + 1][x])
if x < 0:
graph.add_edge(tuple([x, y]), tuple([x - 1, y]), weight=grid[y][x - 1])
if x < max_x:
graph.add_edge(tuple([x, y]), tuple([x + 1, y]), weight=grid[y][x + 1])
return graph, max_x, max_y
grid = [[int(value) for value in line.strip()] for line in open('15a.txt', 'r').readlines()]
# Part A
graph, max_x, max_y = graph_from_grid(grid)
print(networkx.shortest_path_length(graph, source=tuple([0, 0]), target=tuple([max_y, max_x]), weight='weight'))
# Part B
for y in range(0, max_y + 1):
for x in range(0, max_x + 1):
for y_add in range(0, 5):
for x_add in range(0, 5):
if y_add > 0 or x_add > 0:
new_value = grid[y][x] + x_add + y_add
if new_value > 9:
new_value -= 9
new_y = y + (y_add * (max_y + 1))
new_x = x + (x_add * (max_x + 1))
while new_y > len(grid) - 1:
grid.append([])
while new_x > len(grid[new_y]) - 1:
grid[new_y].append(0)
grid[new_y][new_x] = new_value
graph, max_x, max_y = graph_from_grid(grid)
print(networkx.shortest_path_length(graph, source=tuple([0, 0]), target=tuple([max_y, max_x]), weight='weight'))
import numpy
def parser(bits, tokens=[]):
if len(tokens) == 0:
tokens.append(dict(type='version', value=int(bits[0:3], 2)))
return parser(bits[3:], tokens)
elif tokens[-1]['type'] == 'version':
tokens.append(dict(type='type', value=int(bits[0:3], 2)))
return parser(bits[3:], tokens)
elif tokens[-1]['type'] == 'type' and tokens[-1]['value'] != 4:
tokens.append(dict(type='length_type', value=int(bits[0:1], 2)))
return parser(bits[1:], tokens)
elif tokens[-1]['type'] == 'length_type' and tokens[-1]['value'] == 0:
supackets_length = int(bits[0:15], 2)
tokens.append(dict(type='length', value=supackets_length))
remainder = bits[15:]
subpackets = []
while len(bits[15:]) - len(remainder) < supackets_length:
subpacket_tokens, remainder = parser(remainder, [])
subpackets.append(subpacket_tokens)
tokens.append(dict(type='subpackets', value=subpackets))
elif tokens[-1]['type'] == 'length_type' and tokens[-1]['value'] == 1:
supackets_length = int(bits[0:11], 2)
tokens.append(dict(type='length', value=supackets_length))
remainder = bits[11:]
subpackets = []
while len(subpackets) < supackets_length:
subpacket_tokens, remainder = parser(remainder, [])
subpackets.append(subpacket_tokens)
tokens.append(dict(type='subpackets', value=subpackets))
elif tokens[-1]['type'] == 'last':
tokens.append(dict(type='value', value=bits[0:4]))
if tokens[-2]['value'] == 1:
return parser(bits[4:], tokens)
else:
remainder = bits[4:]
else:
tokens.append(dict(type='last', value=int(bits[0:1], 2)))
return parser(bits[1:], tokens)
return tokens, remainder
def version_sum(tokens):
total_sum = sum([token['value'] for token in tokens
if token['type'] == 'version'])
subpacket_sets = [token for token in tokens
if token['type'] == 'subpackets']
for subpackets in subpacket_sets:
for subpacket in subpackets['value']:
total_sum += version_sum(subpacket)
return total_sum
def calculate(tokens):
type = [token for token in tokens if token['type'] == 'type'][0]['value']
sub_values = []
subpacket_sets = [token for token in tokens
if token['type'] == 'subpackets']
for subpackets in subpacket_sets:
for subpacket in subpackets['value']:
sub_values.append(calculate(subpacket))
if type == 0:
return sum(sub_values)
elif type == 1:
return numpy.prod(sub_values)
elif type == 2:
return min(sub_values)
elif type == 3:
return max(sub_values)
elif type == 4:
bin_value = ''.join([token['value'] for token in tokens if token['type'] == 'value'])
return int(bin_value, 2)
elif type == 5:
if sub_values[0] > sub_values[1]:
return 1
return 0
elif type == 6:
if sub_values[0] < sub_values[1]:
return 1
return 0
elif type == 7:
if sub_values[0] == sub_values[1]:
return 1
return 0
return 0
hex = [line.strip() for line in open('16a.txt', 'r').readlines()][0]
binary = ''.join([bin(int(digit, 16))[2:].zfill(4) for digit in hex])
values, remainder = parser(binary)
# Part A
print(version_sum(values))
# Part B
print(calculate(values))
line = [line.strip() for line in open('17a.txt', 'r').readlines()][0]
area = line.split(': ')[1]
x_range, y_range = [value.split('=')[1] for value in area.split(', ')]
x_min, x_max = [int(value) for value in x_range.split('..')]
y_min, y_max = [int(value) for value in y_range.split('..')]
# Part A
valid_y_velocities = []
for starting_y_velocity in range(y_min, 10000):
high_y = 0
y = 0
y_velocity = starting_y_velocity
steps = 0
while y >= y_min:
y += y_velocity
y_velocity -= 1
steps += 1
if y > high_y:
high_y = y
if y_min <= y <= y_max:
valid_y_velocities.append([starting_y_velocity, steps, high_y])
valid_y_steps = [velocity[1] for velocity in valid_y_velocities]
max_y_steps = max(valid_y_steps)
valid_x_velocities = []
for starting_x_velocity in range(1, x_max + 1):
x = 0
x_velocity = starting_x_velocity
steps = 0
while x <= x_max and steps <= max_y_steps:
x += x_velocity
if x_velocity > 0:
x_velocity -= 1
steps += 1
if x_min <= x <= x_max:
valid_x_velocities.append([starting_x_velocity, steps])
valid_steps = [velocity[1] for velocity in valid_x_velocities if velocity[1] in valid_y_steps]
matching_y_velocities = [velocity for velocity in valid_y_velocities if velocity[1] in valid_steps]
print(max([velocity[2] for velocity in matching_y_velocities]))
# Part B
matching_x_velocities = [velocity for velocity in valid_x_velocities if velocity[1] in valid_steps]
valid_velocities = set()
for y_velocity in matching_y_velocities:
for x_velocity in [velocity for velocity in valid_x_velocities
if velocity[1] == y_velocity[1]]:
valid_velocities.add(tuple([x_velocity[0], y_velocity[0]]))
print(len(valid_velocities))
file = open('2a.txt', 'r')
commands = [line.split() for line in file.readlines()]
# Part A
def directional_total(direction, commands):
return sum([int(command[1]) for command in commands if command[0] == direction])
forward_total = directional_total('forward', commands)
down_total = directional_total('down', commands)
up_total = directional_total('up', commands)
print(forward_total * (down_total - up_total))
# Part B
aim = 0
position = 0
depth = 0
for command in commands:
direction = command[0]
value = int(command[1])
if direction == 'forward':
position += value
depth += aim * value
elif direction == 'up':
aim -= value
elif direction == 'down':
aim += value
print(position * depth)
# Part B, take 2
forward_total = directional_total('forward', commands)
depth_changes = [(directional_total('down', commands[:index]) - directional_total('up', commands[:index])) * int(command[1])
for index, command in enumerate(commands) if command[0] == 'forward' and index > 0]
print(forward_total * sum(depth_changes))
from statistics import mode
file = open('3a.txt', 'r')
binary_numbers = [line.strip() for line in file.readlines()]
# Part A
def characters_at_index(index, strings):
return [string[index] for string in strings]
def binary_list_to_int(binary_list):
return int(''.join(binary_list), 2)
most_common = [mode(characters_at_index(index, binary_numbers))
for index, digit in enumerate(list(binary_numbers[0]))]
least_common = [['1', '0'][int(digit)] for digit in most_common]
gamma = binary_list_to_int(most_common)
epsilon = binary_list_to_int(least_common)
print(gamma * epsilon)
# Part B
def ogr_filter(numbers, index):
characters = characters_at_index(index, numbers)
ones = characters.count('1')
zeros = characters.count('0')
digit = '1'
if zeros > ones:
digit = '0'
return [number for number in numbers if number[index] == digit]
def csr_filter(numbers, index):
characters = characters_at_index(index, numbers)
ones = characters.count('1')
zeros = characters.count('0')
digit = '0'
if zeros > ones:
digit = '1'
return [number for number in numbers if number[index] == digit]
ogr_values = binary_numbers
index = 0
while len(ogr_values) > 1:
ogr_values = ogr_filter(ogr_values, index)
index += 1
csr_values = binary_numbers
index = 0
while len(csr_values) > 1:
csr_values = csr_filter(csr_values, index)
index += 1
ogr = int(ogr_values[0], 2)
csr = int(csr_values[0], 2)
print(ogr * csr)
from itertools import chain
file = open('4a.txt', 'r')
lines = [line.strip() for line in file.readlines()]
# Part A
calls = [int(number) for number in lines[0].split(',')]
boards = []
line_index = 2
while line_index < len(lines):
rows = [[int(number) for number in lines[row_index].split()]
for row_index in list(range(line_index, line_index + 5))]
columns = [[row[column_index] for row in rows]
for column_index in list(range(0, 5))]
board = {
'full': list(chain.from_iterable(rows)),
'wins': rows + columns
}
boards.append(board)
line_index += 6
def won(board):
return len([win for win in board['wins'] if len(win) == 0]) > 0
def marked_board(board, call):
return {
'full': [number for number in board['full'] if number != call],
'wins': [[number for number in win if number != call] for win in board['wins']]
}
call_index = 0
won_boards = []
while len(won_boards) == 0:
boards = [marked_board(board, calls[call_index]) for board in boards]
won_boards = [board for board in boards if won(board)]
call_index += 1
unmarked = sum(won_boards[0]['full'])
last_call = calls[call_index - 1]
print(unmarked * last_call)
# Part B
unwon_boards = [board for board in boards if not won(board)]
while len(unwon_boards) > 1:
boards = [marked_board(board, calls[call_index]) for board in boards]
unwon_boards = [board for board in boards if not won(board)]
call_index += 1
last_board = unwon_boards[0]
while not won(last_board):
last_board = marked_board(last_board, calls[call_index])
call_index += 1
unmarked = sum(last_board['full'])
last_call = calls[call_index - 1]
print(unmarked * last_call)
from collections import Counter
from itertools import chain
def smart_range(a, b):
return list(range(a, b + 1) if b > a else reversed(range(b, a + 1)))
def points_between(a, b, diagonals=False):
if a[0] == b[0]:
return [(a[0], y) for y in smart_range(a[1], b[1])]
elif a[1] == b[1]:
return [(x, a[1]) for x in smart_range(a[0], b[0])]
elif diagonals:
return list(zip(smart_range(a[0], b[0]), smart_range(a[1], b[1])))
else:
return []
lines = [line.strip() for line in open('5ex.txt', 'r').readlines()]
line_ends = [[[int(coordinate) for coordinate in coordinates.split(',')]
for coordinates in line.split(' -> ')]
for line in lines]
line_points = chain.from_iterable([points_between(a, b, False) for a, b in line_ends])
duplicate_points = [point for point, count in Counter(line_points).items()
if count > 1]
print(len(duplicate_points))
from collections import Counter
file = open('5a.txt', 'r')
lines = [line.strip() for line in file.readlines()]
# Part A
line_ends = [[[int(coordinate) for coordinate in coordinates.split(',')]
for coordinates in line.split(' -> ')]
for line in lines]
vertical_line_ends = [sorted(end, key=lambda point: point[1])
for end in line_ends if end[0][0] == end[1][0]]
horizontal_line_ends = [sorted(end, key=lambda point: point[0])
for end in line_ends if end[0][1] == end[1][1]]
vertical_line_coordinates = [(end[0][0], y)
for end in vertical_line_ends
for y in list(range(end[0][1], end[1][1] + 1))]
horizontal_line_coordinates = [(x, end[0][1])
for end in horizontal_line_ends
for x in list(range(end[0][0], end[1][0] + 1))]
all_coordinates = vertical_line_coordinates + horizontal_line_coordinates
duplicate_coordinates = [coordinate for coordinate, count in Counter(all_coordinates).items()
if count > 1]
print(len(duplicate_coordinates))
# Part B
diagonal_up_line_ends = [end for end in line_ends
if abs(end[0][0] - end[1][0]) == abs(end[0][1] - end[1][1])
and end[1][1] < end[0][1]]
diagonal_down_line_ends = [end for end in line_ends
if abs(end[0][0] - end[1][0]) == abs(end[0][1] - end[1][1])
and end[1][1] > end[0][1]]
def sign(first, second):
return (second - first) / abs(second - first)
diagonal_up_line_coordinates = [(end[1][0] - int(diff * sign(end[0][0], end[1][0])), y)
for end in diagonal_up_line_ends
for diff, y in enumerate(list(range(end[1][1], end[0][1] + 1)))]
diagonal_down_line_coordinates = [(end[0][0] + int(diff * sign(end[0][0], end[1][0])), y)
for end in diagonal_down_line_ends
for diff, y in enumerate(list(range(end[0][1], end[1][1] + 1)))]
all_coordinates += diagonal_up_line_coordinates + diagonal_down_line_coordinates
duplicate_coordinates = [coordinate for coordinate, count in Counter(all_coordinates).items()
if count > 1]
print(len(duplicate_coordinates))
from functools import lru_cache
@lru_cache(maxsize=1000)
def fish_production(countdown, days):
if countdown - days > -1:
return 1
new_days = days - (countdown + 1)
return fish_production(6, new_days) + fish_production(8, new_days)
lines = [line.strip() for line in open('6a.txt', 'r').readlines()]
fish = tuple(int(number) for number in lines[0].split(','))
# Part A
print(sum([fish_production(n, 80) for n in fish]))
# Part B
print(sum([fish_production(n, 256) for n in fish]))
from functools import lru_cache
@lru_cache(maxsize=10000)
def fuel_for_distance(distance):
return sum(range(1, distance + 1))
lines = [line.strip() for line in open('7a.txt', 'r').readlines()]
crabs = [int(number) for number in lines[0].split(',')]
# Part A
print(min([sum([abs(position - crab) for crab in crabs]) for position in range(min(crabs), max(crabs))]))
# Part B
print(min([sum([fuel_for_distance(abs(position - crab)) for crab in crabs]) for position in range(min(crabs), max(crabs))]))
from itertools import chain
def output_from_digit(digit, unique_digits):
if digit == 0:
return [unique for unique in unique_digits
if len(unique) == 6
and output_from_digit(7, unique_digits).issubset(unique)
and not output_from_digit(5, unique_digits).issubset(unique)][0]
if digit == 1:
return [unique for unique in unique_digits
if len(unique) == 2][0]
if digit == 2:
return [unique for unique in unique_digits
if len(unique) == 5
and not output_from_digit(1, unique_digits).issubset(unique)
and not unique.issubset(output_from_digit(6, unique_digits))][0]
if digit == 3:
return [unique for unique in unique_digits
if len(unique) == 5
and output_from_digit(1, unique_digits).issubset(unique)][0]
if digit == 4:
return [unique for unique in unique_digits
if len(unique) == 4][0]
if digit == 5:
return [unique for unique in unique_digits
if len(unique) == 5
and not output_from_digit(1, unique_digits).issubset(unique)
and unique.issubset(output_from_digit(6, unique_digits))][0]
if digit == 6:
return [unique for unique in unique_digits
if len(unique) == 6
and not output_from_digit(7, unique_digits).issubset(unique)][0]
if digit == 7:
return [unique for unique in unique_digits
if len(unique) == 3][0]
if digit == 8:
return [unique for unique in unique_digits
if len(unique) == 7][0]
if digit == 9:
return [unique for unique in unique_digits
if len(unique) == 6
and output_from_digit(7, unique_digits).issubset(unique)
and output_from_digit(5, unique_digits).issubset(unique)][0]
def numeric_output(unique_digits, output):
every_digit = [output_from_digit(digit, unique_digits) for digit in range(0, 10)]
output_digits = [every_digit.index(digit) for digit in output]
return int(''.join([str(digit) for digit in output_digits]))
# Part A
lines = [line.strip() for line in open('8a.txt', 'r').readlines()]
uniques = [[set(item) for item in line.split(' | ')[0].split()] for line in lines]
outputs = [[set(item) for item in line.split(' | ')[1].split()] for line in lines]
output_lengths = [[len(item) for item in output] for output in outputs]
unique_digit_counts = [2, 3, 4, 7]
unique_digit_output_lengths = [length for length in chain.from_iterable(output_lengths)
if length in unique_digit_counts]
print(len(unique_digit_output_lengths))
# Part B
numeric_outputs = [numeric_output(uniques[index], output) for index, output in enumerate(outputs)]
print(sum(numeric_outputs))
from itertools import chain
lines = ['9' + line.strip() + '9' for line in open('9a.txt', 'r').readlines()]
lines = ['9' * len(lines[0])] + lines + ['9' * len(lines[0])]
# Part A
low_points = [int(value) + 1
for row, line in enumerate(lines[1:-1])
for column, value in enumerate(line[1:-1])
if int(value) < int(lines[row][column + 1])
and int(value) < int(lines[row + 2][column + 1])
and int(value) < int(lines[row + 1][column])
and int(value) < int(lines[row + 1][column + 2])]
print(sum(low_points))
# Part B
basins = []
for i in range(0, len(lines)):
basins.append(['9'] * len(lines[0]))
basin_letter = 'a'
for row, line in enumerate(lines[1:-1]):
for column, value in enumerate(line[1:-1]):
if value != '9':
if basins[row][column + 1] != '9':
basins[row + 1][column + 1] = basins[row][column + 1]
elif basins[row + 1][column] != '9':
basins[row + 1][column + 1] = basins[row + 1][column]
else:
basins[row + 1][column + 1] = basin_letter
basin_letter = chr(ord(basin_letter) + 1)
merged = True
while merged:
merged = False
for row, line in enumerate(basins[1:-1]):
for column, value in enumerate(line[1:-1]):
previous_value = basins[row + 1][column]
if value != '9' and previous_value != '9' and value != previous_value:
basins = [[letter if letter != value else previous_value for letter in update_row] for update_row in basins]
merged = True
flat_basins = list(chain.from_iterable(basins))
basins_set = set(flat_basins)
basins_set.remove('9')
basin_sizes = sorted([flat_basins.count(item) for item in basins_set])
print(basin_sizes[-3] * basin_sizes[-2] * basin_sizes[-1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment