diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c78940..074d3f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_CXX_COMPILER clang++) set(CMAKE_BUILD_TYPE Release) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -Wno-c23-extensions -std=c++23}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -Wno-c23-extensions -std=c++23 -g") endif() set(executables d01 d02 d03 d04 d05 d06 d07 d08 d09 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25) diff --git a/d12/main.cxx b/d12/main.cxx index e7ba6b8..c26d5f6 100644 --- a/d12/main.cxx +++ b/d12/main.cxx @@ -1,15 +1,211 @@ +#include +#include +#include #include -#include +#include +#include +#include +#include +#include using std::cout, std::endl; -int main() { - std::ifstream file{"../d12/input.txt"}; - std::string line; +constexpr const char content[] = { + #embed "input.txt" +}; - while (std::getline(file, line)) { - +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; }