From ae9a5c230c59ba94ac8a2724f7b9f453af41cd5b Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Mon, 18 Dec 2023 10:46:52 +0000 Subject: [PATCH] 2023 Day 18 --- 2023/puzzle-18-01.cc | 147 +++++++++++++++++++++++ 2023/puzzle-18-02.cc | 273 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 420 insertions(+) create mode 100644 2023/puzzle-18-01.cc create mode 100644 2023/puzzle-18-02.cc diff --git a/2023/puzzle-18-01.cc b/2023/puzzle-18-01.cc new file mode 100644 index 0000000..572c6fd --- /dev/null +++ b/2023/puzzle-18-01.cc @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Int = std::int64_t; +using UInt = std::uint64_t; + +enum class Dir { horiz, vert }; + +struct Location +{ + Location(Int const x, Int const y) noexcept + : x_(x), y_(y) + { + } + + [[nodiscard]] auto operator==(Location const& rhs) const noexcept -> bool + { + return x_ == rhs.x_ && y_ == rhs.y_; + } + + [[nodiscard]] auto x() const noexcept -> Int { return x_; } + [[nodiscard]] auto y() const noexcept -> Int { return y_; } + +private: + Int x_; + Int y_; +}; + +struct Data +{ + Data(Dir const dir, UInt const colour) noexcept + : dir_(dir), colour_(colour) + { + } + + void dir(Dir const dir) noexcept { dir_ = dir; } + [[nodiscard]] auto dir() const noexcept -> Dir { return dir_; } + [[nodiscard]] auto colour() const noexcept -> UInt { return colour_; } + +private: + Dir dir_; + UInt colour_; +}; + +template<> +struct std::hash +{ + [[nodiscard]] auto operator()(Location const& loc) const noexcept -> std::size_t + { + return std::hash{}((loc.x() << 16) + loc.y()); + } +}; + +struct Grid +{ + void add_instruction(std::string const& line) + { + auto const dir = line[0]; + char* pos = nullptr; + UInt const amt = std::strtoull(line.data() + 2, &pos, 10); + pos += 3; + UInt const colour = std::strtoull(pos, &pos, 16); + + auto const vert = (dir == 'U' || dir == 'D') ? Dir::vert : Dir::horiz; + Data const data(vert, colour); + + if (auto const it = edges_.find(current_loc_); it != edges_.end()) { + it->second.dir((entered_from_north || dir == 'U') ? Dir::vert : Dir::horiz); + } + + Int const dx = dir == 'R' ? 1 : (dir == 'L' ? -1 : 0); + Int const dy = dir == 'D' ? 1 : (dir == 'U' ? -1 : 0); + for (unsigned i = 0; i < amt; ++i) { + current_loc_ = Location(current_loc_.x() + dx, current_loc_.y() + dy); + auto [it, success] = edges_.insert({current_loc_, data}); + assert(success); + } + + min_y_ = std::min(min_y_, current_loc_.y()); + max_y_ = std::max(max_y_, current_loc_.y()); + min_x_ = std::min(min_x_, current_loc_.x()); + max_x_ = std::max(max_x_, current_loc_.x()); + + entered_from_north = dir == 'D'; + } + + [[nodiscard]] auto count_interior() noexcept -> UInt + { + if (auto const it = edges_.find(current_loc_); it != edges_.end()) { + it->second.dir((entered_from_north) ? Dir::vert : Dir::horiz); + } + + UInt count{0}; + for (Int y{min_y_}; y < max_y_ + 1; ++y) { + bool inside{false}; + for (Int x{min_x_}; x < max_x_ + 1; ++x) { + auto it = edges_.find(Location(x, y)); + if (it == edges_.end()) { + count += inside ? 1 : 0; + std::cout << (inside ? 'X' : '.'); + } + else { + ++count; + if (it->second.dir() == Dir::vert) { inside = !inside; } + std::cout << (it->second.dir() == Dir::vert ? '|' : '-'); + } + } + std::cout << '\n'; + } + + return count; + } + +private: + Location current_loc_{0, 0}; + bool entered_from_north{false}; + Int min_y_{0}; + Int max_y_{0}; + Int min_x_{0}; + Int max_x_{0}; + std::unordered_map edges_; +}; + +auto main() -> int try { + std::string line; + Grid grid; + + while (std::getline(std::cin, line)) { + grid.add_instruction(line); + } + + UInt const count{grid.count_interior()}; + std::cout << "Total: " << count << '\n'; + + return EXIT_SUCCESS; +} +catch (...) { + std::cerr << "Uncaught exception.\n"; + return EXIT_FAILURE; +} diff --git a/2023/puzzle-18-02.cc b/2023/puzzle-18-02.cc new file mode 100644 index 0000000..c3d8fc5 --- /dev/null +++ b/2023/puzzle-18-02.cc @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Int = std::int64_t; +using UInt = std::uint64_t; + +enum class State { on, off }; + +struct Location +{ + Location(Int const x, Int const y) noexcept + : x_(x), y_(y) + { + } + + [[nodiscard]] auto operator==(Location const& rhs) const noexcept -> bool + { + return x_ == rhs.x_ && y_ == rhs.y_; + } + + [[nodiscard]] auto operator<=>(const Location& rhs) const noexcept -> std::strong_ordering + { + if (y_ == rhs.y_) { return x_ <=> rhs.x_; } + return y_ <=> rhs.y_; + } + + [[nodiscard]] auto x() const noexcept -> Int { return x_; } + [[nodiscard]] auto y() const noexcept -> Int { return y_; } + +private: + Int x_; + Int y_; +}; + +struct Data +{ + void horiz(State const horiz) noexcept { horiz_ = horiz; } + void vert(State const vert) noexcept { vert_ = vert; } + [[nodiscard]] auto horiz() const noexcept -> State { return horiz_; } + [[nodiscard]] auto vert() const noexcept -> State { return vert_; } + +private: + State horiz_{State::off}; + State vert_{State::off}; +}; + +template<> +struct std::hash +{ + [[nodiscard]] auto operator()(Location const& loc) const noexcept -> std::size_t + { + return std::hash{}((loc.x() << 16) + loc.y()); + } +}; + +auto operator<<(std::ostream& os, Location const& loc) -> std::ostream& +{ + return os << '(' << loc.x() << ", " << loc.y() << ')'; +} + +auto operator<<(std::ostream& os, State state) -> std::ostream& +{ + if (state == State::on) { return os << "on"; } + return os << "off"; +} + +auto operator<<(std::ostream& os, Data const& data) -> std::ostream& +{ + return os << '(' << data.horiz() << ", " << data.vert() << ')'; +} + +struct Grid +{ + void add_instruction(std::string const& line) + { + char dir; + char* pos = nullptr; + std::strtoll(line.data() + 2, &pos, 10); + pos += 3; + Int const colour = std::strtoll(pos, nullptr, 16); + + switch (colour & 0xf) { + case 0: + dir = 'R'; + break; + case 1: + dir = 'D'; + break; + case 2: + dir = 'L'; + break; + case 3: + dir = 'U'; + break; + default: + std::abort(); + } + + Int const amt = colour / 16; + + Int const dx = amt * (dir == 'R' ? 1 : (dir == 'L' ? -1 : 0)); + Int const dy = amt * (dir == 'D' ? 1 : (dir == 'U' ? -1 : 0)); + + if (dir == 'L' || dir == 'R') { + auto [it, success] = corners_.insert(std::make_pair(current_loc_, Data())); + it->second.horiz(dir == 'R' ? State::on : State::off); + } + if (dir == 'U' || dir == 'D') { + auto [it, success] = corners_.insert(std::make_pair(current_loc_, Data())); + it->second.vert(dir == 'D' ? State::on : State::off); + } + + current_loc_ = Location(current_loc_.x() + dx, current_loc_.y() + dy); + min_x_ = std::min(current_loc_.x(), min_x_); + + if (dir == 'L' || dir == 'R') { + auto [it, success] = corners_.insert(std::make_pair(current_loc_, Data())); + it->second.horiz(dir == 'L' ? State::on : State::off); + } + if (dir == 'U' || dir == 'D') { + auto [it, success] = corners_.insert(std::make_pair(current_loc_, Data())); + it->second.vert(dir == 'U' ? State::on : State::off); + } + } + + [[nodiscard]] auto count_interior() const noexcept -> UInt + { + assert(current_loc_.x() == 0); + assert(current_loc_.y() == 0); + + std::set vertical_set; + Int y{corners_.begin()->first.y()}; + Int x{min_x_}; + bool inside{false}; + bool horiz_on{false}; + UInt count{0}; + constexpr bool debug{true}; + + for (auto const& [loc, data] : corners_) { + if (y != loc.y()) { + for (auto it{vertical_set.upper_bound(x)}; it != vertical_set.end(); ++it) { + assert(!horiz_on); + if (inside) { + count += *it - x; + if (debug) { + std::cout << count << ": tail inside [" << x << ", " << *it << ")\n"; + } + } + ++count; + inside = !inside; + x = *it + 1; + if (debug) { + std::cout << count << ": tail wall [" << *it << "]\n"; + } + } + + // Reset for a new line. + assert(!inside); + assert(!horiz_on); + + auto const repeat_count{loc.y() - y - 1}; + UInt row_count{0}; + inside = false; + Int row_x{*vertical_set.begin()}; + if (debug) { + std::cout << "R0: Repeating rows [" << y + 1 << ", " << loc.y() << ")\n"; + } + for (auto const vert_entry : vertical_set) { + if (inside) { + row_count += vert_entry - row_x; + if (debug) { + std::cout << "R" << row_count << " inside: [" << row_x << ", " << vert_entry << ")\n"; + } + } + ++row_count; + if (debug) { + std::cout << "R" << row_count << " wall: [" << vert_entry << "]\n"; + } + row_x = vert_entry + 1; + inside = !inside; + } + count += row_count * repeat_count; + if (debug) { + std::cout << count << ": repeated rows " << row_count << " * " << repeat_count << '\n'; + } + assert(!inside); + + y = loc.y(); + x = min_x_; + inside = false; + horiz_on = false; + } + + for (auto it{vertical_set.upper_bound(x - 1)}; + it != vertical_set.end() && *it < loc.x(); ++it) { + assert(!horiz_on); + if (inside) { + count += *it - x; + if (debug) { + std::cout << count << ": inside [" << x << ", " << *it << ")\n"; + } + } + ++count; + if (debug) { + std::cout << count << ": vertical wall [" << *it << "]\n"; + } + inside = !inside; + x = *it + 1; + } + if (inside || horiz_on) { + count += loc.x() - x; + if (debug) { + if (horiz_on) { + std::cout << count << ": horizontal wall [" << x << ", " << loc.x() << ")\n"; + } + else { + std::cout << count << ": inside new [" << x << ", " << loc.x() << ")\n"; + } + } + } + ++count; + if (debug) { + std::cout << count << ": wall new [" << loc.x() << "] = " << loc << " - " << + data << "\n"; + } + x = loc.x() + 1; + + horiz_on = data.horiz() == State::on; + if (data.vert() == State::on) { + inside = !inside; + auto const [_, success] = vertical_set.insert(loc.x()); + assert(success); + } + else { + assert(data.vert() == State::off); + auto const success = vertical_set.erase(loc.x()); + assert(success != 0); + } + } + + return count; + } + +private : + Location current_loc_{0, 0}; + std::map corners_; + Int min_x_{0}; +}; + +auto main() -> int try { + std::string line; + Grid grid; + + while (std::getline(std::cin, line)) { + grid.add_instruction(line); + } + + UInt const count{grid.count_interior()}; + std::cout << "Total: " << count << '\n'; + + return EXIT_SUCCESS; +} +catch (...) { + std::cerr << "Uncaught exception.\n"; + return EXIT_FAILURE; +}