#include #include #include #include #include #include #include #include #include using std::cout, std::endl; constexpr const char content[] = { #embed "input.txt" }; struct Position { int x; int y; bool operator==(const Position& other) const { return x == other.x && y == other.y; } }; struct SidePosition { int x; int y; bool operator==(const Position& other) const { return x == other.x && y == other.y; } }; struct Region { char type; std::vector positions; int perimeter; }; struct SideRegion { char type; std::vector positions; int corners; }; namespace std { template <> struct hash> { size_t operator()(const std::pair& p) const { return std::hash{}(p.first) ^ std::hash{}(p.second); } }; } void find_region(std::vector& garden, std::vector& regions, std::unordered_set>& visited, Position p, Region& curr_region, char target) { if(p.x < 0 || p.x >= garden.size() || p.y < 0 || p.y >= garden[p.x].size()) { return; } for(auto [i, j] : {std::pair{0, 1}, {0, -1}, {1, 0}, {-1, 0}}) { if(p.x + i >= 0 && p.x + i < garden.size() && p.y + j >= 0 && p.y + j < garden[p.x].size()) { if(garden[p.x + i][p.y + j] == target && !visited.contains({p.x + i, p.y + j})) { visited.insert({p.x + i, p.y + j}); find_region(garden, regions, visited, {p.x + i, p.y + j}, curr_region, target); } else if(garden[p.x + i][p.y + j] != target){ curr_region.perimeter++; } } else { curr_region.perimeter++; } } curr_region.positions.push_back(p); } int part_1() { std::istringstream input_stream(content); std::string line; std::vector garden = {}; std::vector regions = {}; std::unordered_set> visited = {}; while(getline(input_stream, line)) { garden.emplace_back(line); } int id = 0; for(int x = 0; x < garden.size(); x++) { for(int y = 0; y < garden[x].size(); y++) { if(!visited.contains({x, y})) { auto curr_region = Region{garden[x][y], {}, 0}; visited.insert({x, y}); find_region(garden, regions, visited, {x, y}, curr_region, garden[x][y]); regions.push_back(curr_region); } } } int total = 0; for(auto& region : regions) { total += region.perimeter * region.positions.size(); } return total; } void find_corners(std::vector& garden, std::vector& regions, std::unordered_set>& visited, SidePosition p, SideRegion& curr_region, char target) { if(p.x < 0 || p.x >= garden.size() || p.y < 0 || p.y >= garden[p.x].size()) { return; } int edge_count = 0; for(auto [i, j] : {std::pair{0, 1}, {0, -1}, {1, 0}, {-1, 0}}) { if(p.x + i >= 0 && p.x + i < garden.size() && p.y + j >= 0 && p.y + j < garden[p.x].size()) { if(garden[p.x + i][p.y + j] != target) { edge_count++; } } else { edge_count++; } } if(edge_count == 0) { for(auto [i, j] : {std::pair{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}) { if(p.x + i >= 0 && p.x + i < garden.size() && p.y + j >= 0 && p.y + j < garden[p.x].size()) { if(garden[p.x + i][p.y + j] != target) { edge_count++; } } else { edge_count++; } } if(edge_count == 1) { curr_region.corners++; } } else if(edge_count == 2) { curr_region.corners++; } for(auto [i, j] : {std::pair{0, 1}, {0, -1}, {1, 0}, {-1, 0}}) { if(p.x + i >= 0 && p.x + i < garden.size() && p.y + j >= 0 && p.y + j < garden[p.x].size()) { if(garden[p.x + i][p.y + j] == target && !visited.contains({p.x + i, p.y + j})) { visited.insert({p.x + i, p.y + j}); find_corners(garden, regions, visited, {p.x + i, p.y + j}, curr_region, target); } else if(garden[p.x + i][p.y + j] != target){ } } else { } } curr_region.positions.push_back(p); } int part_2() { std::istringstream input_stream(content); std::string line; std::vector garden = {}, scaled_garden = {}; std::vector regions = {}; std::unordered_set> visited = {}; while(getline(input_stream, line)) { garden.emplace_back(line); } for(auto& row : garden) { std::string s{""}; for(int i = 0; i < row.size(); i++) { for(auto _ : std::views::iota(0, 3)) { s.push_back(row[i]); } } for(auto _ : std::views::iota(0, 3)) { scaled_garden.push_back(s); } } int id = 0; for(int x = 0; x < scaled_garden.size(); x++) { for(int y = 0; y < scaled_garden[x].size(); y++) { if(!visited.contains({x, y})) { auto curr_region = SideRegion{scaled_garden[x][y], {}, 0}; visited.insert({x, y}); find_corners(scaled_garden, regions, visited, {x, y}, curr_region, scaled_garden[x][y]); regions.push_back(curr_region); } } } int total = 0; for(auto& region : regions) { total += region.corners * (region.positions.size()/9) ; } return total; } int main() { std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); auto p1_val = part_1(); std::chrono::steady_clock::time_point middle = std::chrono::steady_clock::now(); auto p2_val = part_2(); std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); cout << "Part 1: " << p1_val << " (" << std::chrono::duration_cast(middle - begin).count() << "µs)" << endl; cout << "Part 2: " << p2_val << " (" << std::chrono::duration_cast(end - middle).count() << "µs)" << endl; return 0; }