diff --git a/2022/puzzle-24-01.cc b/2022/puzzle-24-01.cc new file mode 100644 index 0000000..7ddcd4b --- /dev/null +++ b/2022/puzzle-24-01.cc @@ -0,0 +1,202 @@ +// +// Created by Matthew Gretton-Dann on 16/12/2022. +// + +#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; + } +} diff --git a/2022/puzzle-24-02.cc b/2022/puzzle-24-02.cc new file mode 100644 index 0000000..85c20fa --- /dev/null +++ b/2022/puzzle-24-02.cc @@ -0,0 +1,176 @@ +// +// Created by Matthew Gretton-Dann on 16/12/2022. +// + +#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; + +struct State +{ + Point pt_{0, 0}; + std::size_t dest_{0}; + + [[nodiscard]] constexpr auto operator<(State const& rhs) const noexcept -> bool + { + return pt_ < rhs.pt_ || (pt_ == rhs.pt_ && dest_ < rhs.dest_); + } +}; + +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 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; + } + } + + Point const initial{1, 0}; + Point const target{size.first - 1, size.second}; + std::array targets{target, initial, target}; + State const initial_state{initial, 0}; + std::set next_points{initial_state}; + 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) { + auto dest{pt.dest_}; + if (pt.pt_ == targets.at(dest)) { + dest += 1; + if (dest == targets.size()) { + std::cout << "Number of turns: " << turn_count << "\n"; + return EXIT_SUCCESS; + } + } + + for (auto const& dir : dirs) { + Point const proposed{pt.pt_.first + dir.first, pt.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, dest}); + } + } + + ++turn_count; + } +}