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 << " \n Usage:\n "
145+ << " The program requires 2 args: (part1, part2) and the path to the file."
146+ << " \n For 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 << " \n first 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