aoc25/day8/main.cpp
2025-12-13 12:35:10 +00:00

176 lines
6.6 KiB
C++

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <expected>
#include <format>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <print>
#include <ranges>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
using Point = std::tuple<int, int, int>;
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));
}
static auto parseInput(const std::string &filename) -> std::expected<std::vector<Point>, std::string> {
std::ifstream inputF{filename};
if (!inputF) {
return std::unexpected{"Some file open error."};
}
std::vector<Point> pointVec{};
std::string puzzleLine;
while (std::getline(inputF, puzzleLine)) {
if (!puzzleLine.empty()) {
auto subParts = std::ranges::to<std::vector<std::string>>(std::views::split(puzzleLine, ','));
pointVec.emplace_back(std::stoi(subParts[0]), std::stoi(subParts[1]), std::stoi(subParts[2]));
}
}
// std::println("pointList: {}", pointVec);
return pointVec;
}
static auto getCombinations(const std::vector<Point> &pointVec) -> std::vector<std::pair<Point, Point>> {
std::vector<std::pair<Point, Point>> result;
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]);
}
}
return std::move(result);
}
static auto getDistances(const std::vector<Point> &pointVec) -> auto {
auto comparisonLamb = [](const std::pair<Point, Point> &pair1, const std::pair<Point, Point> &pair2) -> bool {
return getDistance(pair1.first, pair1.second) < getDistance(pair2.first, pair2.second);
};
std::map<std::pair<Point, Point>, double, decltype(comparisonLamb)> sortedMap(comparisonLamb);
auto combinations = getCombinations(pointVec);
for (const auto &combination : combinations) {
// std::println("Combinations: {}:{}", combination.first, combination.second);
sortedMap[combination] = getDistance(combination.first, combination.second);
}
return sortedMap;
}
static auto doTheThing(const std::vector<Point> &pointVec, int numberToTake) -> long long {
auto distanceMap = getDistances(pointVec);
std::map<Point, int> pointToCircuitMap{};
std::unordered_map<int, std::vector<Point>> 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);
}
}
} 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::vector<int>>();
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;
}
auto main() -> int {
auto testCase = parseInput("test_input");
if (testCase) {
constexpr int TEST_PUZZLE_TAKE = 10;
auto testResult = doTheThing(*testCase, TEST_PUZZLE_TAKE);
std::println("P1 Testcase result: {}", testResult);
// auto testResultP2 = treePartTwo(*testCase);
// std::println("P2 Testcase result: {}", testResultP2);
} else {
std::print("{}\n", testCase.error());
}
auto realPuzzle = parseInput("puzzle_input");
if (realPuzzle) {
constexpr int REAL_PUZZLE_TAKE = 1000;
// auto realResult = doTheThing(*realPuzzle, REAL_PUZZLE_TAKE);
// std::println("P1 Real result: {}", realResult);
auto realResultP2 = doTheThing(*realPuzzle, REAL_PUZZLE_TAKE);
std::println("P2 Real result: {}", realResultP2);
} else {
std::print("{}\n", realPuzzle.error());
}
return 0;
}