#include #include #include #include #include #include #include #include #include #include using namespace aocpp; namespace { using DirFn = Coord(Coord); auto ToDir(char c) -> DirFn* { switch (c) { case 'L': return Left; case 'R': return Right; case 'D': return Down; case 'U': return Up; default: throw std::runtime_error{"bad input"}; } } /// @brief Return the sign of the number /// @param x number /// @return -1, 0, or 1 matching the sign of the input auto Sign(std::int64_t x) -> std::int64_t { return (x>0) - (x<0); } class Logic { Coord knots[10] {}; // Current location of each knot in the rope std::set s1{{}}, s9{{}}; public: auto answer() const -> std::pair { return {s1.size(), s9.size()}; } auto move(DirFn* dirFn) -> void { knots[0] = dirFn(knots[0]); std::size_t k; for (k = 1; k < 10; ++k) { auto const delta {knots[k-1] - knots[k]}; if (NormInf(delta) <= 1) { break; } knots[k] += {Sign(delta.x), Sign(delta.y)}; } // only update the sets when a knot actually moved if (k > 1) { s1.insert(knots[1]); if (k > 9) { s9.insert(knots[9]); } } } }; auto Solve(std::istream & in) -> std::pair { Logic logic; char d; std::size_t n; while (in >> d >> n) { while (n--) { logic.move(ToDir(d)); } } return logic.answer(); } } // namespace TEST_SUITE("2022-09 examples") { TEST_CASE("example") { std::istringstream in { "R 5\n" "U 8\n" "L 8\n" "D 3\n" "R 17\n" "D 10\n" "L 25\n" "U 20\n"}; auto [p1,p2] = Solve(in); CHECK(p1 == 88); CHECK(p2 == 36); } } auto main(int argc, char** argv) -> int { auto [p1,p2] = Solve(*aocpp::Startup(argc, argv)); std::cout << "Part 1: " << p1 << std::endl; std::cout << "Part 2: " << p2 << std::endl; }