// // Created by Matthew Gretton-Dann on 16/12/2022. // #include #include #include #include #include #include #include #include #include using namespace std::string_literals; using Int = std::int64_t; using UInt = std::uint64_t; using Point = std::pair; using Blizard = std::pair; constexpr Point up{0, -1}; constexpr Point right{1, 0}; constexpr Point down{0, 1}; constexpr Point left{-1, 0}; struct BlizzardCompare { [[nodiscard]] constexpr auto operator()(Point const& lhs, Point const& rhs) const noexcept -> bool { return (lhs.first < rhs.first) || (lhs.first == rhs.first && lhs.second < rhs.second); } [[nodiscard]] constexpr auto operator()(Blizard const& lhs, Blizard const& rhs) const noexcept -> bool { return (*this)(lhs.first, rhs.first); } [[nodiscard]] constexpr auto operator()(Blizard const& lhs, Point const& rhs) const noexcept -> bool { return this->operator()(lhs.first, rhs); } [[nodiscard]] constexpr auto operator()(Point const& lhs, Blizard const& rhs) const noexcept -> bool { return this->operator()(lhs, rhs.first); } }; using Blizards = std::multiset; auto step_time(Blizards const& blizzards, Point size) -> Blizards { Blizards result; for (auto const& blizzard : blizzards) { Blizard new_blizzard{{blizzard.first.first + blizzard.second.first, blizzard.first.second + blizzard.second.second}, blizzard.second}; if (new_blizzard.first.first == 0) { new_blizzard.first.first = size.first - 1; } if (new_blizzard.first.first == size.first) { new_blizzard.first.first = 1; } if (new_blizzard.first.second == 0) { new_blizzard.first.second = size.second - 1; } if (new_blizzard.first.second == size.second) { new_blizzard.first.second = 1; } result.insert(new_blizzard); } return result; } auto print_blizzards(Blizards const& blizards, Point const& size) { for (Int i{0}; i <= size.first; ++i) { std::cout << (i == 1 ? '.' : '#'); } std::cout << '\n'; for (Int y{1}; y < size.second; ++y) { std::cout << '#'; for (Int x{1}; x < size.first; ++x) { auto it(blizards.find({{x, y}, {0, 0}})); char c{'.'}; if (it == blizards.end()) { std::cout << '.'; continue; } if (it->second == left) { c = '<'; } else if (it->second == right) { c = '>'; } else if (it->second == up) { c = '^'; } else if (it->second == down) { c = 'v'; } char count{'0'}; while (it != blizards.end() && it->first == Point{x, y}) { ++count; ++it; } if (count != '1') { c = count; } std::cout << c; } std::cout << "#\n"; } for (Int i{0}; i <= size.first; ++i) { std::cout << (i == (size.first - 1) ? '.' : '#'); } } auto main() -> int { std::string line; Blizards blizzards; // We surround each line of the grid with extra dots, so we don't have to resize as we go along. Point size{0, -1}; while (std::getline(std::cin, line)) { if (size.first == 0) { size.first = static_cast(line.size()) - 1; } assert(size.first == line.size() - 1); ++size.second; Point pos{0, size.second}; for (auto const c : line) { switch (c) { case '>': blizzards.insert({pos, right}); break; case 'v': blizzards.insert({pos, down}); break; case '<': blizzards.insert({pos, left}); break; case '^': blizzards.insert({pos, up}); break; case '#': case '.': break; default: abort(); } pos.first += 1; } } std::set next_points{Point{1, 0}}; Point const target{size.first - 1, size.second}; std::array const dirs{up, right, down, left, {0, 0}}; UInt turn_count{0}; while (true) { blizzards = step_time(blizzards, size); std::set const points{next_points}; next_points.clear(); assert(!points.empty()); for (auto const& pt : points) { if (pt == target) { std::cout << "Number of turns: " << turn_count << "\n"; return EXIT_SUCCESS; } for (auto const& dir : dirs) { Point const proposed{pt.first + dir.first, pt.second + dir.second}; auto it{blizzards.find({proposed, dir})}; if (it != blizzards.end()) { continue; } if (proposed.first <= 0) { continue; } if (proposed.first >= size.first) { continue; } if (proposed.second <= 0 && proposed != Point{1, 0}) { continue; } if (proposed.second >= size.second && proposed != target) { continue; } next_points.insert(proposed); } } ++turn_count; } }