C++
Both parts in 40 ms. As always, the real AoC with C++ is parsing the input, although having a number that changes in the puzzle messes up my test/ solution runners.
Uses Boost's implementations of sets, since they're just plain faster than the STL ones.
Code
#include <boost/log/trivial.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
#include <filesystem>
#include <fstream>
#include <stdexcept>
namespace {
struct Point {
int x, y, z;
};
auto distance(const Point &a, const Point &b) {
auto &[ax, ay, az] = a;
auto &[bx, by, bz] = b;
ssize_t dx = ax - bx;
ssize_t dy = ay - by;
ssize_t dz = az - bz;
return size_t(dx * dx + dy * dy + dz * dz);
}
using PList = std::vector<Point>;
using DMap = std::vector<std::pair<std::pair<size_t, size_t>, size_t>>;
using DSet = boost::unordered::unordered_flat_set<size_t>;
auto read() {
auto rval = PList{};
auto ih = std::ifstream{"08.txt"};
auto line = std::string{};
while (std::getline(ih, line)) {
auto c1 = line.find(',');
auto c2 = line.find(',', c1 + 1);
rval.emplace_back(
std::stoi(line.substr(0, c1)),
std::stoi(line.substr(c1 + 1, c2 - c1 - 1)),
std::stoi(line.substr(c2 + 1))
);
}
return rval;
}
auto dmap(const PList &t) {
auto rval = DMap{};
for (auto a = size_t{}; a < t.size() - 1; ++a)
for (auto b = a + 1; b < t.size(); ++b)
rval.emplace_back(std::make_pair(a, b), distance(t.at(a), t.at(b)));
return rval;
}
auto find(size_t c, std::vector<DSet> &sets) {
for (auto i = sets.begin(); i != sets.end(); ++i)
if (i->contains(c))
return i;
throw std::runtime_error{"can't find"};
}
auto part1(const PList &t, size_t count, const DMap &d) {
auto circuits = std::vector<DSet>{};
for (auto i = size_t{}; i < t.size(); ++i) {
auto c = DSet{t.size()};
c.insert(i);
circuits.push_back(std::move(c));
}
for (auto i = size_t{}; i < count; ++i) {
auto a = d.at(i).first.first;
auto b = d.at(i).first.second;
auto ac = find(a, circuits);
auto bc = find(b, circuits);
if (ac == bc)
continue;
ac->insert(bc->begin(), bc->end());
circuits.erase(bc);
}
std::sort(circuits.begin(), circuits.end(), [](auto &a, auto &b) {
return a.size() > b.size();
});
auto total = size_t{1};
for (auto i = size_t{}; i < 3; ++i)
total *= circuits.at(i).size();
return total;
}
auto part2(const PList &t, const DMap &d) {
auto circuits = std::vector<DSet>{};
for (auto i = size_t{}; i < t.size(); ++i) {
auto c = DSet{t.size()};
c.insert(i);
circuits.push_back(std::move(c));
}
for (auto i = size_t{}; i < d.size(); ++i) {
auto a = d.at(i).first.first;
auto b = d.at(i).first.second;
auto ac = find(a, circuits);
auto bc = find(b, circuits);
if (ac == bc)
continue;
ac->insert(bc->begin(), bc->end());
circuits.erase(bc);
if (circuits.size() == 1) {
return ssize_t(t.at(a).x) * ssize_t(t.at(b).x);
}
}
throw std::runtime_error{"never joins?"};
}
} // namespace
auto main() -> int {
auto t = read();
BOOST_LOG_TRIVIAL(info) << "Day 8: read " << t.size();
auto test = std::filesystem::current_path().filename().string() ==
std::string{"test"};
auto d = dmap(t);
std::sort(d.begin(), d.end(), [](auto &a, auto &b) {
return a.second < b.second;
});
BOOST_LOG_TRIVIAL(info) << "1: " << part1(t, test ? 10 : 1000, d);
BOOST_LOG_TRIVIAL(info) << "2: " << part2(t, d);
}