Skip to content

Commit 10368f0

Browse files
committed
day 12 part 2, all thanks to other people on the internet
1 parent dead7e6 commit 10368f0

File tree

2 files changed

+147
-31
lines changed

2 files changed

+147
-31
lines changed

2024/include/aoc/2024/pos.h

+9
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,13 @@ Pos down = {.x = 0, .y = 1};
6565
Pos left = {.x = -1, .y = 0};
6666
Pos right = {.x = 1, .y = 0};
6767

68+
Pos N = up;
69+
Pos S = down;
70+
Pos E = right;
71+
Pos W = left;
72+
Pos NE = up + right;
73+
Pos NW = up + left;
74+
Pos SE = down + right;
75+
Pos SW = down + left;
76+
6877
std::vector<Pos> directions = {up, down, left, right};

2024/src/day12.cpp

+138-31
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,31 @@
1010
#include <set>
1111
#include <queue>
1212

13-
struct Data {
13+
struct Data
14+
{
1415
std::vector<std::string> gardens;
1516
};
1617

17-
struct Node {
18-
Node* up;
19-
Node* down;
20-
Node* left;
21-
Node* right;
22-
char value;
18+
bool in_bounds(const Data &data, const Pos &p)
19+
{
20+
return p.x >= 0 && p.y >= 0 && p.x < data.gardens[0].size() && p.y < data.gardens.size();
2321
};
2422

2523
int part1(const Data &data)
2624
{
2725
std::set<Pos> unvisited;
2826
std::map<char, std::vector<std::pair<int, int>>> dimensions;
2927

30-
for(size_t j = 0; j < data.gardens.size(); ++j) {
31-
for(size_t i = 0; i < data.gardens[0].size(); ++i) {
28+
for (size_t j = 0; j < data.gardens.size(); ++j)
29+
{
30+
for (size_t i = 0; i < data.gardens[0].size(); ++i)
31+
{
3232
unvisited.insert({.x = i, .y = j});
3333
}
3434
}
3535

36-
auto in_bounds = [&](const Pos& p) {
37-
return p.x >= 0 && p.y >= 0 && p.x < data.gardens[0].size() && p.y < data.gardens.size();
38-
};
39-
40-
41-
while (unvisited.size() > 0) {
36+
while (unvisited.size() > 0)
37+
{
4238
Pos p = *unvisited.begin();
4339
std::queue<Pos> q;
4440

@@ -48,30 +44,39 @@ int part1(const Data &data)
4844
q.push(p);
4945

5046
// floodfill
51-
while(!q.empty()) {
47+
while (!q.empty())
48+
{
5249
Pos cur = q.front();
53-
if (unvisited.contains(cur)) {
50+
if (unvisited.contains(cur))
51+
{
5452
unvisited.erase(cur);
5553
++area;
56-
for(auto dir: directions) {
54+
for (auto dir : directions)
55+
{
5756
Pos next = cur + dir;
58-
if (in_bounds(next)) {
57+
if (in_bounds(data, next))
58+
{
5959
char next_char = data.gardens[next.y][next.x];
60-
if (next_char == target) {
61-
if (unvisited.contains(next)) {
60+
if (next_char == target)
61+
{
62+
if (unvisited.contains(next))
63+
{
6264
q.push(next);
6365
}
6466
}
65-
else {
67+
else
68+
{
6669
++perimeter;
6770
}
6871
}
69-
else {
72+
else
73+
{
7074
++perimeter;
7175
}
7276
}
7377
}
74-
else {
78+
else
79+
{
7580
// we've been here before
7681
// but this node was added into the queue by another node
7782
}
@@ -82,19 +87,119 @@ int part1(const Data &data)
8287
}
8388

8489
int sum = 0;
85-
for(auto& [key, val] : dimensions) {
86-
for(auto& [area, perimeter]: val) {
90+
for (auto &[key, val] : dimensions)
91+
{
92+
for (auto &[area, perimeter] : val)
93+
{
8794
sum += area * perimeter;
88-
std::cout << std::format("{}: area {}, perimeter: {}\n", key, area, perimeter);
8995
}
9096
}
9197

9298
return sum;
9399
}
94100

101+
int count_corners(const Data &data, Pos cur)
102+
{
103+
int cornes = 0;
104+
auto same = [&](Pos next){ return (in_bounds(data, next) && data.gardens[cur.y][cur.x] == data.gardens[next.y][next.x]);};
105+
106+
bool n = same(cur+N);
107+
bool ne = same(cur+NE);
108+
bool e = same(cur+E);
109+
bool se = same(cur+SE);
110+
bool s = same(cur+S);
111+
bool sw = same(cur+SW);
112+
bool w = same(cur+W);
113+
bool nw = same(cur+NW);
114+
115+
// convex corners, or an L shaped region where the current characters
116+
// AX XA A A
117+
// A A AX AX
118+
// concave corners, or when a cell is the same as its two orthongal neighbors but not the same
119+
// as its diagonal neighboer
120+
// AA AA AX XA
121+
// XA XA AA AA
122+
123+
// the left side of the || operator checks convex corners
124+
// the right side of the || operators chekcs concave corners
125+
if (!n && !e || n && e && !ne) cornes += 1;
126+
if (!e && !s || e && s && !se) cornes += 1;
127+
if (!s && !w || s && w && !sw) cornes += 1;
128+
if (!w && !n || w && n && !nw) cornes += 1;
129+
130+
return cornes;
131+
}
132+
95133
int part2(const Data &data)
96134
{
97-
return 0;
135+
std::set<Pos> unvisited;
136+
std::map<char, std::vector<std::pair<int, int>>> dimensions;
137+
138+
for (size_t j = 0; j < data.gardens.size(); ++j)
139+
{
140+
for (size_t i = 0; i < data.gardens[0].size(); ++i)
141+
{
142+
unvisited.insert({.x = i, .y = j});
143+
}
144+
}
145+
146+
while (unvisited.size() > 0)
147+
{
148+
Pos p = *unvisited.begin();
149+
std::queue<Pos> q;
150+
151+
char target = data.gardens[p.y][p.x];
152+
int area = 0;
153+
int sides = 0;
154+
q.push(p);
155+
156+
// floodfill
157+
while (!q.empty())
158+
{
159+
Pos cur = q.front();
160+
if (unvisited.contains(cur))
161+
{
162+
unvisited.erase(cur);
163+
++area;
164+
sides += count_corners(data, cur);
165+
for (auto dir : directions)
166+
{
167+
Pos next = cur + dir;
168+
if (in_bounds(data, next))
169+
{
170+
char next_char = data.gardens[next.y][next.x];
171+
if (next_char == target)
172+
{
173+
if (unvisited.contains(next))
174+
{
175+
q.push(next);
176+
}
177+
}
178+
}
179+
}
180+
}
181+
else
182+
{
183+
// we've been here before
184+
// but this node was added into the queue by another node
185+
}
186+
q.pop();
187+
}
188+
189+
dimensions[target].push_back({area, sides});
190+
}
191+
192+
int sum = 0;
193+
for (auto &[key, val] : dimensions)
194+
{
195+
for (auto &[area, sides] : val)
196+
{
197+
sum += area * sides;
198+
std::cout << std::format("{}: area {}, sides: {}\n", key, area, sides);
199+
}
200+
}
201+
202+
return sum;
98203
}
99204

100205
Data parse()
@@ -155,15 +260,17 @@ int main(int argc, char **argv)
155260

156261
auto first = part1(data);
157262
std::cout << "Part 1: " << first << std::endl;
158-
263+
159264
auto second = part2(data);
160265
std::cout << "Part 2: " << second << std::endl;
161266

162267
first != answer1 ? throw std::runtime_error("Part 1 incorrect") : nullptr;
163268
second != answer2 ? throw std::runtime_error("Part 2 incorrect") : nullptr;
164269

165-
for (int i = 1; i < argc; ++i) {
166-
if (std::string(argv[i]) == "--benchmark") {
270+
for (int i = 1; i < argc; ++i)
271+
{
272+
if (std::string(argv[i]) == "--benchmark")
273+
{
167274
benchmark::Initialize(&argc, argv);
168275
benchmark::RunSpecifiedBenchmarks();
169276
return 0;

0 commit comments

Comments
 (0)