diff --git a/2022/18.cpp b/2022/18.cpp new file mode 100644 index 0000000..ea046c3 --- /dev/null +++ b/2022/18.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace { + +using Coord = aocpp::Vec; + +/// @brief Parse an input stream +/// @param in white-space delimited, comma-separated coordinates +/// @return set of coordinates +auto Parse(std::istream & in) -> std::set +{ + std::set result; + std::string line; + while (in >> line) { + Coord x; + if (3 != std::sscanf(line.c_str(), "%lld,%lld,%lld\n", &x[0], &x[1], &x[2])) + throw std::runtime_error{"bad input line"}; + result.insert(x); + } + return result; +} + +auto Part1(std::set const& obj) -> std::size_t +{ + std::size_t result {0}; + for (auto && x : obj) { + x.EachNeighbor([&](Coord const& y) { + if (!obj.contains(y)) result++; + }); + } + return result; +} + +auto Part2(std::set const& obj) -> std::size_t +{ + auto [lo, hi] = Coord::BoundingBox(obj.begin(), obj.end()); + lo -= Coord(1); + hi += Coord(1); + + std::set seen {lo}; + std::vector work {lo}; + std::size_t result {0}; + + while (!work.empty()) { + auto x = work.back(); + work.pop_back(); + x.EachNeighbor([&, lo=lo, hi=hi](Coord const& y) { + if (obj.contains(y)) { + result++; + } else if (y.InRange(lo, hi) && seen.insert(y).second) { + work.push_back(y); + } + }); + } + + return result; +} + +} // namespace + +TEST_SUITE("2022-12 examples") { + TEST_CASE("documented example") { + std::istringstream in { + "2,2,2\n" + "1,2,2\n" + "3,2,2\n" + "2,1,2\n" + "2,3,2\n" + "2,2,1\n" + "2,2,3\n" + "2,2,4\n" + "2,2,6\n" + "1,2,5\n" + "3,2,5\n" + "2,1,5\n" + "2,3,5\n" + }; + auto obj = Parse(in); + CHECK(64 == Part1(obj)); + CHECK(58 == Part2(obj)); + } +} + +auto main(int argc, char** argv) -> int +{ + auto obj = Parse(*aocpp::Startup(argc, argv)); + std::cout << "Part 1: " << Part1(obj) << std::endl; + std::cout << "Part 2: " << Part2(obj) << std::endl; +} diff --git a/2022/CMakeLists.txt b/2022/CMakeLists.txt index 76613ed..9e8cbb9 100644 --- a/2022/CMakeLists.txt +++ b/2022/CMakeLists.txt @@ -10,5 +10,8 @@ target_link_libraries(2022_04 aocpp) add_executable(2022_12 12.cpp) target_link_libraries(2022_12 aocpp) +add_executable(2022_18 18.cpp) +target_link_libraries(2022_18 aocpp) + add_executable(2022_25 25.cpp) target_link_libraries(2022_25 aocpp) diff --git a/lib/include/aocpp/Vec.hpp b/lib/include/aocpp/Vec.hpp new file mode 100644 index 0000000..82e338f --- /dev/null +++ b/lib/include/aocpp/Vec.hpp @@ -0,0 +1,143 @@ +#ifndef AOCPP_VEC_HPP_ +#define AOCPP_VEC_HPP_ + +#include +#include +#include +#include +#include + +namespace aocpp { + +template +class Vec { + T arr[N]; + + auto range_check(std::size_t i) -> void { + if (i >= N) { + throw std::out_of_range{std::to_string(i) + " >= " + std::to_string(N)}; + } + } + +public: + Vec() = default; + auto operator<=>(const Vec&) const = default; + + explicit Vec(T x) { + std::fill_n(arr, N, x); + } + + Vec(std::initializer_list xs) { + assert(N == xs.size()); + std::move(xs.begin(), xs.end(), arr); + } + + auto operator[](std::size_t i) const -> T { + return arr[i]; + } + + auto operator[](std::size_t i) -> T & { + return arr[i]; + } + + auto at(std::size_t i) const -> T { + range_check(i); + return arr[i]; + } + + auto at(std::size_t i) -> T & { + range_check(i); + return arr[i]; + } + + auto operator+(Vec const& rhs) const -> Vec + { + Vec result; + for (std::size_t i = 0; i < N; ++i) result[i] = arr[i] + rhs[i]; + return result; + } + + auto operator-(Vec const& rhs) const -> Vec { + Vec result; + for (std::size_t i = 0; i < N; ++i) result[i] = arr[i] - rhs[i]; + return result; + } + + auto operator-() const -> Vec { + Vec result; + for (std::size_t i = 0; i < N; ++i) result[i] = -arr[i]; + return result; + } + + auto operator+=(Vec const& rhs) -> Vec & { + for (std::size_t i = 0; i < N; ++i) arr[i] += rhs[i]; + return *this; + } + + auto operator-=(Vec const& rhs) -> Vec & + { + for (std::size_t i = 0; i < N; ++i) arr[i] -= rhs[i]; + return *this; + } + + /// @brief Check inclusion in a cuboid + /// @param lo lower-corner + /// @param hi upper-corner + /// @return this point is contained in the cuboid + auto InRange(Vec const& lo, Vec const& hi) const -> bool + { + for (std::size_t i = 0; i < N; ++i) { + if (arr[i] < lo[i] || hi[i] < arr[i]) return false; + } + return true; + } + + /// @brief Find the corners of the smallest cuboid containing all the given points. + /// @tparam It InputIterator + /// @param first beginning of input range + /// @param last end of input range + /// @return pair of lower and upper cuboid corners + template + static auto BoundingBox(It first, It last) -> std::pair + { + Vec lo(0), hi(0); + std::for_each(first, last, [&](auto const& x) { + for (std::size_t i = 0; i < N; ++i) { + if (lo[i] > x[i]) lo[i] = x[i]; + if (hi[i] < x[i]) hi[i] = x[i]; + } + }); + return {lo, hi}; + } + + /// @brief Apply a continuation to each neighbor of a vector + /// @param k continuation applied to cardinal direction neighbors + auto EachNeighbor(std::invocable auto k) const -> void + { + auto x = *this; + for (std::size_t i = 0; i < N; ++i) { + x[i] += 1; + k(std::as_const(x)); + x[i] -= 2; + k(std::as_const(x)); + x[i] += 1; + } + } +}; + +template +auto operator<<(std::ostream & out, Vec const& rhs) -> std::ostream & +{ + out << "{"; + if (0 < N) { + out << rhs[0]; + } + for (std::size_t i = 1; i < N; ++i) { + out << "," << rhs[i]; + } + return out << "}"; +} + +} + +#endif