-
Notifications
You must be signed in to change notification settings - Fork 0
/
day12.py
145 lines (117 loc) · 3.75 KB
/
day12.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# Advent of Code 2024 - day 12
from common import get_input
# Part 1
def main1():
grid = get_input("input12.txt")
grid_height = len(grid)
grid_width = len(grid[0])
positions_to_check = {
(row, col) for row in range(grid_height) for col in range(grid_width)
}
regions = []
while not len(positions_to_check) == 0:
pos = positions_to_check.pop()
region_positions = get_region_positions(pos, grid)
positions_to_check -= region_positions
regions.append(region_positions)
price = 0
for region in regions:
price += cost(region)
print(f"Answer 1 is: {price}")
def get_region_positions(pos, grid):
grid_positions = {pos}
positions_to_check = {pos}
checked_positions = set()
while len(positions_to_check) > 0:
pos = positions_to_check.pop()
neighbors = get_same_type_neighbors(pos, grid)
grid_positions |= neighbors
checked_positions.add(pos)
for neighbor in neighbors:
if neighbor not in checked_positions:
positions_to_check.add(neighbor)
return grid_positions
def get_same_type_neighbors(pos, grid):
letter = grid[pos[0]][pos[1]]
connected = set()
for d_pos in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
new_pos = (pos[0] + d_pos[0], pos[1] + d_pos[1])
if not allowed_position(new_pos, grid):
continue
new_letter = grid[new_pos[0]][new_pos[1]]
if new_letter == letter:
connected.add(new_pos)
return connected
def allowed_position(pos, grid):
grid_height = len(grid)
grid_width = len(grid[0])
if not (0 <= pos[0] < grid_height):
return False
if not (0 <= pos[1] < grid_width):
return False
return True
def cost(region):
area = len(region)
perimeter = 0
for pos in region:
perimeter += 4 - num_neighbors_in_region(pos, region)
return area * perimeter
def num_neighbors_in_region(pos, region):
num = 0
for d_pos in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
new_pos = (pos[0] + d_pos[0], pos[1] + d_pos[1])
if new_pos in region:
num += 1
return num
# Part 2
def main2():
grid = get_input("input12.txt")
grid_height = len(grid)
grid_width = len(grid[0])
positions_to_check = {
(row, col) for row in range(grid_height) for col in range(grid_width)
}
regions = []
while not len(positions_to_check) == 0:
pos = positions_to_check.pop()
region_positions = get_region_positions(pos, grid)
positions_to_check -= region_positions
regions.append(region_positions)
price = 0
for region in regions:
price += cost2(region)
print(f"Answer 2 is: {price}")
def cost2(region):
area = len(region)
num_sides = 0
for pos in region:
# Use that number of sides = number of corners of the region
num_sides += num_corners(pos, region)
return area * num_sides
def num_corners(pos, region):
num = 0
neighbor_pairs = [
[(-1, 0), (0, 1)],
[(0, 1), (1, 0)],
[(1, 0), (0, -1)],
[(0, -1), (-1, 0)],
]
for neighbor_pair in neighbor_pairs:
d1, d2 = neighbor_pair
neighbor1 = (pos[0] + d1[0], pos[1] + d1[1])
neighbor2 = (pos[0] + d2[0], pos[1] + d2[1])
outside_corner = (not neighbor1 in region) and (not neighbor2 in region)
if outside_corner:
num += 1
pos_between_neighbors = (pos[0] + d1[0] + d2[0], pos[1] + d1[1] + d2[1])
inside_corner = (
neighbor1 in region
and neighbor2 in region
and pos_between_neighbors not in region
)
if inside_corner:
num += 1
return num
if __name__ == "__main__":
main1()
main2()