From 62944c8f1c2167180dd4020f3c9281a22f3d7d08 Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Wed, 29 Mar 2023 12:02:14 -0700 Subject: [PATCH] 14 --- 2022/14.cpp | 167 ++++++++++++++++++++++++++++++++++++++++++++ 2022/CMakeLists.txt | 3 + 2 files changed, 170 insertions(+) create mode 100644 2022/14.cpp diff --git a/2022/14.cpp b/2022/14.cpp new file mode 100644 index 0000000..154f6da --- /dev/null +++ b/2022/14.cpp @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +namespace { + +namespace phx = boost::phoenix; +namespace qi = boost::spirit::qi; + +using Input = std::vector>; + +const aocpp::Coord TOP {500, 0}; + +// Input file grammar +template +class Grammar : public qi::grammar { + qi::rule coord; + qi::rule()> line; + qi::rule input; + +public: + Grammar() : Grammar::base_type{input} { + using namespace qi::labels; + + coord = qi::long_long [ phx::bind(&aocpp::Coord::x, _val) = _1 ] >> + "," >> + qi::long_long [ phx::bind(&aocpp::Coord::y, _val) = _1 ]; + line = coord % " -> " >> "\n"; + input = *line; + } +}; + +/// Parse the complete input stream according to the rules defined in 'Grammar'. +/// Throws: std::runtime_error on failed parse +auto Parse(std::istream & in) -> Input +{ + auto result = Input{}; + auto const content = std::string{std::istreambuf_iterator{in}, {}}; + auto b = content.begin(); // updated on successful parse + auto const e = content.end(); + + if (!qi::parse(b, e, Grammar{}, result) || b != e) { + throw std::runtime_error{"Bad input file: " + content}; + } + + return result; +} + +auto ExpandLines(Input const& lines) -> std::set +{ + auto result = std::set{}; + for (auto const& line : lines) { + for (auto const i : boost::irange(std::size_t{1}, line.size())) { + auto const& a = line[i-1]; + auto const& b = line[i]; + + if (a.x == b.x) { + for (auto const y : boost::irange(std::min(a.y, b.y), 1+std::max(a.y, b.y))) { + result.insert({a.x, y}); + } + } else if (a.y == b.y) { + for (auto const x : boost::irange(std::min(a.x, b.x), 1+std::max(a.x, b.x))) { + result.insert({x, a.y}); + } + } else { + throw new std::runtime_error{"Bad line segment"}; + } + } + } + return result; +} + +auto FindBottom(Input const& input) -> std::int64_t +{ + auto result = std::int64_t{TOP.y}; + for (auto const& line : input) { + for (auto const& coord : line) { + result = std::max(result, coord.y); + } + } + return result; +} + +auto Part1(std::int64_t bottom, std::set world) -> std::size_t +{ + auto path = std::vector{TOP}; + auto const starting_size = world.size(); + + while (!path.empty()) { + auto & here = path.back(); + if (here.y == bottom) { + return world.size() - starting_size; + } else if (!world.contains(aocpp::Down(here))) { + path.push_back(aocpp::Down(here)); + } else if (!world.contains(aocpp::Left(aocpp::Down(here)))) { + path.push_back(aocpp::Left(aocpp::Down(here))); + } else if (!world.contains(aocpp::Right(aocpp::Down(here)))) { + path.push_back(aocpp::Right(aocpp::Down(here))); + } else { + world.insert(here); + path.pop_back(); + } + } + + return world.size() - starting_size; +} + +auto Part2(std::int64_t bottom, std::set world) -> std::size_t +{ + auto path = std::vector{TOP}; + auto const starting_size = world.size(); + + while (!path.empty()) { + auto & here = path.back(); + if (here.y == 1 + bottom) { + world.insert(here); + path.pop_back(); + } else if (!world.contains(aocpp::Down(here))) { + path.push_back(aocpp::Down(here)); + } else if (!world.contains(aocpp::Left(aocpp::Down(here)))) { + path.push_back(aocpp::Left(aocpp::Down(here))); + } else if (!world.contains(aocpp::Right(aocpp::Down(here)))) { + path.push_back(aocpp::Right(aocpp::Down(here))); + } else { + world.insert(here); + path.pop_back(); + } + } + + return world.size() - starting_size; +} + +} // namespace + +TEST_SUITE("2022-14 examples") { + TEST_CASE("documented example") { + auto in = std::istringstream{ + "498,4 -> 498,6 -> 496,6\n" + "503,4 -> 502,4 -> 502,9 -> 494,9\n" + }; + auto const input = Parse(in); + auto const bottom = FindBottom(input); + auto world = ExpandLines(input); + CHECK(24 == Part1(bottom, world)); + CHECK(93 == Part2(bottom, std::move(world))); + } +} + +auto Main(std::istream & in, std::ostream & out) -> void +{ + auto const input = Parse(in); + auto const bottom = FindBottom(input); + auto world = ExpandLines(input); + out << "Part 1: " << Part1(bottom, world) << std::endl; + out << "Part 2: " << Part2(bottom, std::move(world)) << std::endl; +} diff --git a/2022/CMakeLists.txt b/2022/CMakeLists.txt index 822951b..68c2069 100644 --- a/2022/CMakeLists.txt +++ b/2022/CMakeLists.txt @@ -31,6 +31,9 @@ target_link_libraries(2022_12 aocpp) add_executable(2022_13 13.cpp) target_link_libraries(2022_13 aocpp Boost::headers) +add_executable(2022_14 14.cpp) +target_link_libraries(2022_14 aocpp Boost::headers) + add_executable(2022_16 16.cpp) target_link_libraries(2022_16 aocpp Boost::headers)