diff --git a/CMakeLists.txt b/CMakeLists.txt index c5dcae0..c2e71ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,5 +42,6 @@ add_subdirectory(day5) add_subdirectory(day6) add_subdirectory(day7) add_subdirectory(day8) +add_subdirectory(day9) diff --git a/day8/main.cpp b/day8/main.cpp index b57022d..d81e7c6 100644 --- a/day8/main.cpp +++ b/day8/main.cpp @@ -1,174 +1,150 @@ #include #include -#include #include -#include #include -#include #include -#include +#include #include #include #include -#include -#include +#include #include -using Point = std::tuple; +// Use a simple struct for clarity +struct Point { + int x, y, z; + // Default spaceship operator for easy comparisons if needed + auto operator<=>(const Point &) const = default; +}; + +struct Edge { + int u; // Index of first point + int v; // Index of second point + double dist; +}; + +// --- Modern Union-Find (DSU) Implementation --- +struct DSU { + std::vector parent; + int groups; + + explicit DSU(size_t n) : parent(n), groups(static_cast(n)) { + // Fill with 0, 1, 2, ..., n-1 + std::iota(parent.begin(), parent.end(), 0); + } + + // Find with Path Compression + // Returns the representative ID of the set containing i + auto find(int i) -> int { + if (parent[i] == i) { + return i; + } + return parent[i] = find(parent[i]); + } + + // Unite two sets. Returns true if they were different sets. + auto unite(int i, int j) -> bool { + int root_i = find(i); + int root_j = find(j); + + if (root_i != root_j) { + parent[root_i] = root_j; // Link roots + groups--; + return true; + } + return false; + } +}; + +// --- Helper Functions --- static auto getDistance(const Point &a, const Point &b) -> double { - return sqrt(pow(std::get<0>(a) - std::get<0>(b), 2) + pow(std::get<1>(a) - std::get<1>(b), 2) + - pow(std::get<2>(a) - std::get<2>(b), 2)); + // std::hypot is cleaner (C++17) and avoids manual pow/sqrt + // For 3D: hypot(x, y, z) is C++17 specific overload + return std::hypot(a.x - b.x, a.y - b.y, a.z - b.z); } -static auto parseInput(const std::string &filename) -> std::expected, std::string> { - std::ifstream inputF{filename}; - +static auto parseInput(std::string_view filename) -> std::expected, std::string> { + std::ifstream inputF{std::string(filename)}; if (!inputF) { - return std::unexpected{"Some file open error."}; + return std::unexpected{"Could not open file."}; } - std::vector pointVec{}; + std::vector points; + std::string line; - std::string puzzleLine; - while (std::getline(inputF, puzzleLine)) { - if (!puzzleLine.empty()) { - auto subParts = std::ranges::to>(std::views::split(puzzleLine, ',')); - pointVec.emplace_back(std::stoi(subParts[0]), std::stoi(subParts[1]), std::stoi(subParts[2])); + while (std::getline(inputF, line)) { + if (line.empty()) { + continue; + } + + // Modern splitting and parsing + auto parts = line | std::views::split(',') | std::views::transform([](auto &&rng) -> auto { + // Convert range to string for stoi (C++23 common_range fix) + return std::string(rng.begin(), rng.end()); + }) | + std::ranges::to>(); + + if (parts.size() >= 3) { + points.emplace_back(std::stoi(parts[0]), std::stoi(parts[1]), std::stoi(parts[2])); } } - // std::println("pointList: {}", pointVec); - return pointVec; + return points; } -static auto getCombinations(const std::vector &pointVec) -> std::vector> { - std::vector> result; +static auto generateSortedEdges(const std::vector &points) -> std::vector { + std::vector edges; + edges.reserve((points.size() * (points.size() - 1)) / 2); - result.reserve(pointVec.size() * pointVec.size()); - - for (size_t i = 0; i < pointVec.size(); ++i) { - for (size_t j = i + 1; j < pointVec.size(); ++j) { - result.emplace_back(pointVec[i], pointVec[j]); + // Standard triangular loop is still clearer/faster than range combinatorics here + for (size_t i = 0; i < points.size(); ++i) { + for (size_t j = i + 1; j < points.size(); ++j) { + edges.push_back( + {.u = static_cast(i), .v = static_cast(j), .dist = getDistance(points[i], points[j])}); } } - return std::move(result); + + // Modern Sort with Projections + // Sorts by the 'dist' member automatically + std::ranges::sort(edges, {}, &Edge::dist); + + return edges; } -static auto getDistances(const std::vector &pointVec) -> auto { - auto comparisonLamb = [](const std::pair &pair1, const std::pair &pair2) -> bool { - return getDistance(pair1.first, pair1.second) < getDistance(pair2.first, pair2.second); - }; - std::map, double, decltype(comparisonLamb)> sortedMap(comparisonLamb); +// --- Main Logic --- - auto combinations = getCombinations(pointVec); - for (const auto &combination : combinations) { +static auto solvePartTwo(const std::vector &points) -> long long { + auto edges = generateSortedEdges(points); + DSU dsu(points.size()); - // std::println("Combinations: {}:{}", combination.first, combination.second); - sortedMap[combination] = getDistance(combination.first, combination.second); - } - - return sortedMap; -} - -static auto doTheThing(const std::vector &pointVec, int numberToTake) -> long long { - auto distanceMap = getDistances(pointVec); - std::map pointToCircuitMap{}; - std::unordered_map> circuitMap; - int currentCircuit = 0; - for (const auto &[combination, distance] : distanceMap) { - const auto &[left, right] = combination; - // std::println("DistanceMap: {}:{} = {}", left, right, distance); - - if (pointToCircuitMap.contains(left) && pointToCircuitMap.contains(right)) { - int leftCircuit = pointToCircuitMap[left]; - int rightCircuit = pointToCircuitMap[right]; - // std::println("Left circuit: {}. Right circuit: {}", leftCircuit, rightCircuit); - if (leftCircuit != rightCircuit) { - std::println("Merge: {} to {}", left, right); - - for (const auto &rightCircuitPoint : circuitMap[rightCircuit]) { - pointToCircuitMap[rightCircuitPoint] = leftCircuit; - } - circuitMap[leftCircuit].append_range(circuitMap[rightCircuit]); - circuitMap.erase(rightCircuit); - - // std::println("mergesize: {}", circuitMap[leftCircuit].size()); - if (circuitMap[leftCircuit].size() >= pointVec.size()) { - - return (long long)std::get<0>(left) * (long long)std::get<0>(right); - } + for (const auto &[u, v, dist] : edges) { + // Try to unite the two points + if (dsu.unite(u, v)) { + // If this connection reduced us to exactly 1 group, we are done! + if (dsu.groups == 1) { + std::println("Connected all points! Last edge: {} <-> {}", u, v); + return (long long)points[u].x * (long long)points[v].x; } - } else if (pointToCircuitMap.contains(left)) { - int leftCircuit = pointToCircuitMap[left]; - // std::println("Only left exists: {} to {}", left, right); - pointToCircuitMap[right] = pointToCircuitMap[left]; - - circuitMap[leftCircuit].push_back(right); - // std::println("leftsize: {}", circuitMap[leftCircuit].size()); - if (circuitMap[leftCircuit].size() >= pointVec.size()) { - - return (long long)std::get<0>(left) * (long long)std::get<0>(right); - } - } else if (pointToCircuitMap.contains(right)) { - int rightCircuit = pointToCircuitMap[right]; - // std::println("Only right exists: {} to {}", left, right); - pointToCircuitMap[left] = pointToCircuitMap[right]; - - circuitMap[rightCircuit].push_back(left); - // std::println("rightsize: {}", circuitMap[rightCircuit].size()); - if (circuitMap[rightCircuit].size() >= pointVec.size()) { - - return (long long)std::get<0>(left) * (long long)std::get<0>(right); - } - } else { - currentCircuit++; - pointToCircuitMap[left] = currentCircuit; - pointToCircuitMap[right] = currentCircuit; - - circuitMap[currentCircuit].push_back(left); - circuitMap[currentCircuit].push_back(right); } } - // std::println("pointToCircuitMap: {}", pointToCircuitMap); - std::println("circuitMap"); - for (const auto &[circuit, pointsInCircuit] : circuitMap) { - - std::println("{} : {}", circuit, pointsInCircuit); - } - - auto sizesOfLists = circuitMap | std::views::transform([](const auto &kvp) -> int { return kvp.second.size(); }) | - std::ranges::to>(); - std::ranges::sort(sizesOfLists, std::greater{}); - auto threeLargest = std::ranges::fold_left(sizesOfLists | std::views::take(3), 1, std::multiplies{}); - - // std::println("{}", threeLargest); - return threeLargest; + return -1; // Should not happen if graph is connected } auto main() -> int { - auto testCase = parseInput("test_input"); - if (testCase) { + // Usage of std::string_view literal + auto puzzle = parseInput("puzzle_input"); - auto testResult = doTheThing(*testCase, 10); - std::println("P1 Testcase result: {}", testResult); - - // auto testResultP2 = treePartTwo(*testCase); - // std::println("P2 Testcase result: {}", testResultP2); - } else { - std::print("{}\n", testCase.error()); + if (!puzzle) { + std::println(stderr, "Error: {}", puzzle.error()); + return 1; } - auto realPuzzle = parseInput("puzzle_input"); - if (realPuzzle) { - // auto realResult = doTheThing(*realPuzzle, 1000); - // std::println("P1 Real result: {}", realResult); + std::println("Loaded {} points.", puzzle->size()); - auto realResultP2 = doTheThing(*realPuzzle, 1000); - std::println("P2 Real result: {}", realResultP2); - } else { - std::print("{}\n", realPuzzle.error()); - } + // Part 2 Logic + auto result = solvePartTwo(*puzzle); + std::println("Part 2 Result: {}", result); return 0; -} +} \ No newline at end of file diff --git a/day9/CMakeLists.txt b/day9/CMakeLists.txt new file mode 100644 index 0000000..bee3402 --- /dev/null +++ b/day9/CMakeLists.txt @@ -0,0 +1,2 @@ + # Use top-level helper to add the target and copy input files + aoc_add_day(day9 "${CMAKE_CURRENT_SOURCE_DIR}" main.cpp) \ No newline at end of file diff --git a/day9/main.cpp b/day9/main.cpp new file mode 100644 index 0000000..d092c2c --- /dev/null +++ b/day9/main.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct CoordinateHash { + auto operator()(const std::pair &p) const -> std::size_t { + + auto h1 = std::hash{}(p.first); + auto h2 = std::hash{}(p.second); + + return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2)); + } +}; + +using Coordinate = std::pair; + +static auto parseInput(std::string_view filename) -> std::expected, std::string> { + std::ifstream inputF{std::string(filename)}; + if (!inputF) { + return std::unexpected{"Could not open file."}; + } + + std::vector coordinates; + std::string line; + + while (std::getline(inputF, line)) { + if (line.empty()) { + continue; + } + + auto parts = line | std::views::split(',') | + std::views::transform([](auto &&rng) -> auto { return std::string(rng.begin(), rng.end()); }) | + std::ranges::to>(); + + if (parts.size() >= 2) { + coordinates.emplace_back(std::stoi(parts[0]), std::stoi(parts[1])); + } + } + return coordinates; +} + +static auto getArea(const Coordinate &left, const Coordinate &right) -> long long { + long long leftPart = std::abs(left.first - right.first) + 1; + long long rightPart = std::abs(left.second - right.second) + 1; + // std::print("{:2} * {:2} ", leftPart, rightPart); + return leftPart * rightPart; +} + +static auto solvePartOne(std::vector &coordinates) -> long long { + long long maxArea{}; + for (size_t i = 0; i < coordinates.size(); ++i) { + for (size_t j = i + 1; j < coordinates.size(); ++j) { + long long area = getArea(coordinates[i], coordinates[j]); + maxArea = std::max(area, maxArea); + // std::println("= {:2}; area of {} and {}", area, coordinates[i], coordinates[j]); + } + } + + return maxArea; +} + +auto main() -> int { + auto test = parseInput("test_input"); + + if (!test) { + std::println(stderr, "Error: {}", test.error()); + return 1; + } + + std::println("Test: Loaded {} points.", test->size()); + + auto testResult = solvePartOne(*test); + std::println("Test: Max Area: {}.", testResult); + + auto puzzle = parseInput("puzzle_input"); + + if (!puzzle) { + std::println(stderr, "Error: {}", puzzle.error()); + return 1; + } + + std::println("Puzzle: Loaded {} points.", puzzle->size()); + auto puzzleResult = solvePartOne(*puzzle); + std::println("Puzzle: Max Area: {}.", puzzleResult); + + // // Part 2 Logic + // auto result = solvePartTwo(*puzzle); + // std::println("Part 2 Result: {}", result); + + return 0; +} \ No newline at end of file diff --git a/day9/puzzle_input b/day9/puzzle_input new file mode 100644 index 0000000..8dcddfd --- /dev/null +++ b/day9/puzzle_input @@ -0,0 +1,496 @@ +98154,50433 +98154,51659 +98318,51659 +98318,52839 +97531,52839 +97531,54072 +97795,54072 +97795,55273 +97599,55273 +97599,56438 +97163,56438 +97163,57630 +96985,57630 +96985,58862 +97008,58862 +97008,60001 +96540,60001 +96540,61238 +96525,61238 +96525,62389 +96136,62389 +96136,63729 +96399,63729 +96399,64863 +95924,64863 +95924,66096 +95742,66096 +95742,67229 +95265,67229 +95265,68026 +93975,68026 +93975,69483 +94298,69483 +94298,70437 +93447,70437 +93447,71702 +93255,71702 +93255,72471 +92092,72471 +92092,73807 +91997,73807 +91997,74541 +90844,74541 +90844,75747 +90491,75747 +90491,76890 +90014,76890 +90014,77857 +89265,77857 +89265,78572 +88186,78572 +88186,79561 +87489,79561 +87489,80961 +87282,80961 +87282,81285 +85786,81285 +85786,82279 +85089,82279 +85089,83598 +84715,83598 +84715,84056 +83449,84056 +83449,85206 +82869,85206 +82869,85762 +81730,85762 +81730,86992 +81183,86992 +81183,87726 +80201,87726 +80201,88040 +78902,88040 +78902,89210 +78253,89210 +78253,89823 +77189,89823 +77189,90009 +75855,90009 +75855,90815 +74933,90815 +74933,91606 +73991,91606 +73991,92411 +73044,92411 +73044,92934 +71938,92934 +71938,93433 +70822,93433 +70822,93648 +69581,93648 +69581,94314 +68546,94314 +68546,95024 +67517,95024 +67517,94716 +66105,94716 +66105,95285 +65025,95285 +65025,96388 +64097,96388 +64097,96208 +62773,96208 +62773,96508 +61596,96508 +61596,96717 +60399,96717 +60399,96734 +59166,96734 +59166,97217 +58025,97217 +58025,97725 +56872,97725 +56872,97536 +55620,97536 +55620,97385 +54388,97385 +54388,97698 +53201,97698 +53201,97956 +51999,97956 +51999,98476 +50792,98476 +50792,97657 +49571,97657 +49571,97890 +48355,97890 +48355,98145 +47124,98145 +47124,98316 +45883,98316 +45883,97952 +44687,97952 +44687,97958 +43453,97958 +43453,97033 +42361,97033 +42361,96816 +41173,96816 +41173,97033 +39892,97033 +39892,96956 +38657,96956 +38657,96441 +37528,96441 +37528,96130 +36350,96130 +36350,95671 +35217,95671 +35217,95273 +34069,95273 +34069,95070 +32845,95070 +32845,94644 +31699,94644 +31699,93613 +30817,93613 +30817,93489 +29542,93489 +29542,93176 +28337,93176 +28337,92481 +27321,92481 +27321,91694 +26364,91694 +26364,90870 +25443,90870 +25443,90230 +24418,90230 +24418,89472 +23473,89472 +23473,88942 +22372,88942 +22372,88622 +21100,88622 +21100,87565 +20378,87565 +20378,86615 +19591,86615 +19591,86172 +18377,86172 +18377,85663 +17192,85663 +17192,84787 +16331,84787 +16331,83324 +16070,83324 +16070,82800 +14866,82800 +14866,82182 +13727,82182 +13727,80646 +13644,80646 +13644,80193 +12282,80193 +12282,79304 +11429,79304 +11429,78018 +11116,78018 +11116,77212 +10142,77212 +10142,75913 +9901,75913 +9901,74888 +9258,74888 +9258,74205 +8022,74205 +8022,73085 +7511,73085 +7511,71792 +7349,71792 +7349,70816 +6580,70816 +6580,69681 +6128,69681 +6128,68714 +5284,68714 +5284,67466 +5106,67466 +5106,66359 +4578,66359 +4578,65036 +4680,65036 +4680,63974 +4016,63974 +4016,62754 +3859,62754 +3859,61611 +3430,61611 +3430,60462 +2997,60462 +2997,59180 +3198,59180 +3198,57968 +3115,57968 +3115,56786 +2871,56786 +2871,55659 +2129,55659 +2129,54386 +2631,54386 +2631,53203 +2260,53203 +2260,51991 +2244,51991 +2244,50781 +2169,50781 +2169,50401 +94699,50401 +94699,48346 +1840,48346 +1840,47122 +1834,47122 +1834,45904 +1930,45904 +1930,44738 +2511,44738 +2511,43572 +2915,43572 +2915,42343 +2857,42343 +2857,41166 +3146,41166 +3146,39955 +3257,39955 +3257,38776 +3536,38776 +3536,37554 +3652,37554 +3652,36292 +3674,36292 +3674,35141 +4091,35141 +4091,33976 +4464,33976 +4464,32898 +5069,32898 +5069,31972 +6021,31972 +6021,30493 +5649,30493 +5649,29455 +6326,29455 +6326,28642 +7432,28642 +7432,27428 +7720,27428 +7720,26421 +8405,26421 +8405,25199 +8722,25199 +8722,24020 +9143,24020 +9143,23327 +10310,23327 +10310,21943 +10453,21943 +10453,21244 +11568,21244 +11568,20019 +11979,20019 +11979,19219 +12936,19219 +12936,18221 +13648,18221 +13648,17813 +15012,17813 +15012,16324 +15205,16324 +15205,16070 +16674,16070 +16674,14784 +17123,14784 +17123,14187 +18224,14187 +18224,13164 +18948,13164 +18948,12901 +20301,12901 +20301,12140 +21235,12140 +21235,10741 +21711,10741 +21711,10344 +22925,10344 +22925,10007 +24155,10007 +24155,9447 +25226,9447 +25226,8483 +26060,8483 +26060,8154 +27263,8154 +27263,7684 +28378,7684 +28378,6350 +29073,6350 +29073,6326 +30407,6326 +30407,6127 +31638,6127 +31638,4808 +32417,4808 +32417,5034 +33804,5034 +33804,4750 +34986,4750 +34986,4343 +36124,4343 +36124,3377 +37112,3377 +37112,3398 +38380,3398 +38380,3461 +39640,3461 +39640,3182 +40816,3182 +40816,2907 +41995,2907 +41995,2161 +43111,2161 +43111,2019 +44327,2019 +44327,2137 +45567,2137 +45567,2347 +46801,2347 +46801,2084 +48001,2084 +48001,1828 +49212,1828 +49212,2439 +50427,2439 +50427,2250 +51639,2250 +51639,2565 +52833,2565 +52833,2148 +54077,2148 +54077,2164 +55299,2164 +55299,2738 +56451,2738 +56451,2802 +57665,2802 +57665,3238 +58815,3238 +58815,2830 +60136,2830 +60136,3636 +61199,3636 +61199,3429 +62505,3429 +62505,3635 +63718,3635 +63718,4523 +64718,4523 +64718,5061 +65813,5061 +65813,5459 +66953,5459 +66953,5293 +68325,5293 +68325,6415 +69169,6415 +69169,6668 +70383,6668 +70383,6681 +71733,6681 +71733,7882 +72484,7882 +72484,7927 +73849,7927 +73849,8994 +74637,8994 +74637,9180 +75955,9180 +75955,10353 +76643,10353 +76643,10757 +77840,10757 +77840,11923 +78490,11923 +78490,12448 +79611,12448 +79611,13185 +80573,13185 +80573,13529 +81882,13529 +81882,14572 +82591,14572 +82591,15742 +83155,15742 +83155,16606 +83999,16606 +83999,17071 +85270,17071 +85270,18173 +85870,18173 +85870,19222 +86510,19222 +86510,20214 +87207,20214 +87207,21071 +88074,21071 +88074,22170 +88622,22170 +88622,22934 +89641,22934 +89641,23738 +90637,23738 +90637,24863 +91147,24863 +91147,25941 +91723,25941 +91723,26996 +92336,26996 +92336,28213 +92638,28213 +92638,29042 +93713,29042 +93713,30301 +93909,30301 +93909,31451 +94317,31451 +94317,32685 +94502,32685 +94502,33573 +95609,33573 +95609,34821 +95747,34821 +95747,36122 +95663,36122 +95663,37280 +96014,37280 +96014,38330 +96801,38330 +96801,39562 +96891,39562 +96891,40803 +96883,40803 +96883,42002 +97055,42002 +97055,43148 +97578,43148 +97578,44409 +97285,44409 +97285,45554 +97999,45554 +97999,46811 +97513,46811 +97513,48015 +97582,48015 +97582,49215 +98001,49215 +98001,50433 diff --git a/day9/test_input b/day9/test_input new file mode 100644 index 0000000..7fb70de --- /dev/null +++ b/day9/test_input @@ -0,0 +1,8 @@ +7,1 +11,1 +11,7 +9,7 +9,5 +2,5 +2,3 +7,3 \ No newline at end of file