Skip to content

Commit a66fab0

Browse files
author
bt
committed
Day 12
1 parent f3512e5 commit a66fab0

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_challenge(day8)
2020
add_challenge(day9)
2121
add_challenge(day10)
2222
add_challenge(day11)
23+
add_challenge(day12)
2324

2425
add_custom_command(TARGET day1
2526
COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_SOURCE_DIR}/data" "./data"

src/day12.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#include "common_headers.hpp"
2+
#include <array>
3+
#include <set>
4+
#include <stack>
5+
6+
constexpr std::array<int,5> offsets{0, 1, 0, -1, 0};
7+
8+
using CoordinateMap = std::set<std::pair<int, int>>;
9+
10+
[[nodiscard]] size_t calculatePerimeter(const std::vector<std::string>& map, int row, int col) {
11+
size_t perimeter{};
12+
for(int i = 0; i < 4; ++i) {
13+
const int newRow{row + offsets[i]};
14+
const int newCol{col + offsets[i + 1]};
15+
if(newRow < 0 || newCol < 0 || newRow >= map.size() || newCol >= map[0].size()) {
16+
++perimeter;
17+
}
18+
else {
19+
perimeter += map[row][col] != map[newRow][newCol];
20+
}
21+
}
22+
return perimeter;
23+
}
24+
25+
[[nodiscard]] size_t calculateCorner(const std::vector<std::string>& map, int row, int col) {
26+
size_t counter{};
27+
const char plot{map[row][col]};
28+
// top left outer
29+
if((row - 1 < 0 || plot != map[row - 1][col]) && (col - 1 < 0 || map[row][col - 1] != plot)) {
30+
++counter;
31+
}
32+
// top right outer
33+
if((row - 1 < 0 || plot != map[row - 1][col]) && (col + 1 >= map[0].size() || map[row][col + 1] != plot)) {
34+
++counter;
35+
}
36+
// bottom left
37+
if((row + 1 >= map.size() || map[row + 1][col] != plot) && (col - 1 < 0 || map[row][col - 1] != plot)) {
38+
++counter;
39+
}
40+
// bottom right
41+
if((row + 1 >= map.size() || map[row + 1][col] != plot) && (col + 1 >= map[0].size() || map[row][col + 1] != plot)) {
42+
++counter;
43+
}
44+
45+
// top left inner
46+
if(row - 1 >= 0 && map[row - 1][col] == plot && col - 1 >= 0 && map[row][col - 1] == plot && map[row - 1][col - 1] != plot) {
47+
++counter;
48+
}
49+
50+
// top right inner
51+
if(row - 1 >= 0 && map[row - 1][col] == plot && col + 1 < map[0].size() && map[row][col + 1] == plot && map[row - 1][col + 1] != plot) {
52+
++counter;
53+
}
54+
55+
// bottom left inner
56+
if(row + 1 < map.size() && map[row + 1][col] == plot && col - 1 >= 0 && map[row][col - 1] == plot && map[row + 1][col - 1] != plot) {
57+
++counter;
58+
}
59+
// bottom right inner
60+
if(row + 1 < map.size() && map[row + 1][col] == plot && col + 1 < map[0].size() && map[row][col + 1] == plot && map[row + 1][col + 1] != plot) {
61+
++counter;
62+
}
63+
64+
return counter;
65+
}
66+
67+
68+
template<typename Handler>
69+
void traverse(const std::vector<std::string>& map, int row, int col, CoordinateMap& visited, Handler& handler) {
70+
71+
const char plot{map[row][col]};
72+
73+
std::stack<std::pair<int, int>> dfs;
74+
dfs.emplace(row, col);
75+
76+
while(!dfs.empty()) {
77+
const auto [r, c] = dfs.top();
78+
dfs.pop();
79+
if(visited.emplace(std::pair{r, c}).second == false) continue;
80+
81+
handler(map, r, c);
82+
83+
for(int i = 0; i < 4; ++i) {
84+
const int newRow{r + offsets[i]};
85+
const int newCol{c + offsets[i + 1]};
86+
if(newRow < 0 || newCol < 0 || newRow >= map.size() || newCol >= map[0].size()) {
87+
continue;
88+
}
89+
else if(plot == map[newRow][newCol]) {
90+
dfs.emplace(newRow, newCol);
91+
}
92+
}
93+
}
94+
}
95+
96+
[[nodiscard]] size_t solveFirstPart(const std::vector<std::string>& map) {
97+
size_t totalPrice{};
98+
CoordinateMap visited;
99+
100+
101+
for(int row = 0; row < map.size(); ++row) {
102+
for(int col = 0; col < map[0].size(); ++col) {
103+
if(visited.count(std::pair(row, col))) continue;
104+
105+
size_t area{};
106+
size_t perimeter{};
107+
auto calculator = [&](const std::vector<std::string>& map, int row, int col) mutable {
108+
++area;
109+
perimeter += calculatePerimeter(map, row, col);
110+
};
111+
112+
traverse(map, row, col, visited, calculator);
113+
totalPrice += area * perimeter;
114+
}
115+
}
116+
return totalPrice;
117+
}
118+
119+
[[nodiscard]] size_t solveSecondPart(const std::vector<std::string>& map) {
120+
size_t totalPrice{};
121+
CoordinateMap visited;
122+
123+
for(int row = 0; row < map.size(); ++row) {
124+
for(int col = 0; col < map[0].size(); ++col) {
125+
if(visited.count(std::pair(row, col))) continue;
126+
127+
size_t area{};
128+
size_t perimeter{};
129+
auto calculator = [&](const std::vector<std::string>& map, int row, int col) mutable {
130+
++area;
131+
perimeter += calculateCorner(map, row, col);
132+
};
133+
traverse(map, row, col, visited, calculator);
134+
totalPrice += area * perimeter;
135+
}
136+
}
137+
138+
return totalPrice;
139+
}
140+
141+
142+
void printHelp()
143+
{
144+
std::cerr << "\nUsage:\n"
145+
<< "The program requires 2 args: (part1, part2) and the path to the file."
146+
<< "\nFor example, ./day7 part1 data/day7.txt";
147+
}
148+
149+
int main(int argc, char* argv[])
150+
{
151+
if(argc != 3) {
152+
printHelp();
153+
return 1;
154+
}
155+
156+
std::string_view task{argv[1]};
157+
if(task != "part1" && task != "part2") {
158+
std::cerr << "\nfirst arg can be either `part1` or `part2`\n";
159+
printHelp();
160+
return 1;
161+
}
162+
163+
std::vector<std::string> inputVec;
164+
readInput(argv[2], std::back_inserter(inputVec));
165+
166+
if(task == "part1") {
167+
std::cout << solveFirstPart(inputVec);
168+
}
169+
else {
170+
std::cout << solveSecondPart(inputVec);
171+
}
172+
173+
return 0;
174+
}

0 commit comments

Comments
 (0)