diff --git a/2016/puzzle-17-01.CMakeLists.txt b/2016/puzzle-17-01.CMakeLists.txt new file mode 100644 index 0000000..1f298ff --- /dev/null +++ b/2016/puzzle-17-01.CMakeLists.txt @@ -0,0 +1,2 @@ +find_package(OpenSSL REQUIRED) +target_link_libraries("${puzzle_name}" OpenSSL::SSL) diff --git a/2016/puzzle-17-01.cc b/2016/puzzle-17-01.cc new file mode 100644 index 0000000..318b2d1 --- /dev/null +++ b/2016/puzzle-17-01.cc @@ -0,0 +1,114 @@ +// +// Created by Matthew Gretton-Dann on 06/12/2021. +// + +#include +#include +#include +#include +#include + +#include + +using MD5Digest = std::array; + +enum Directions { none = 0x0, up = 0x1, down = 0x2, left = 0x4, right = 0x8 }; +auto md5_directions(std::string const& s) -> Directions +{ + MD5Digest digest; + EVP_MD const* md{EVP_md5()}; + unsigned int md_len{0}; + + EVP_MD_CTX* md_context{EVP_MD_CTX_new()}; + assert(md_context != nullptr); + EVP_DigestInit_ex2(md_context, md, nullptr); + EVP_DigestUpdate(md_context, s.data(), s.length()); + EVP_DigestFinal_ex(md_context, digest.data(), &md_len); + Directions d{Directions::none}; + if (((digest[0] & 0xf0) >> 4) > 0xa) { + d = static_cast(d | Directions::up); + } + if ((digest[0] & 0xf) > 0xa) { + d = static_cast(d | Directions::down); + } + if (((digest[1] & 0xf0) >> 4) > 0xa) { + d = static_cast(d | Directions::left); + } + if ((digest[1] & 0xf) > 0xa) { + d = static_cast(d | Directions::right); + } + return d; +} + +struct Node +{ + explicit Node(std::string s) : str_(std::move(s)) {} + + auto operator==(Node const& rhs) const noexcept -> bool { return str_ == rhs.str_; } + + auto operator<(Node const& rhs) const noexcept -> bool + { + return str_.size() < rhs.str_.size() || (str_.size() == rhs.str_.size() && str_ < rhs.str_); + } + + [[nodiscard]] auto str() const -> std::string const& { return str_; } + [[nodiscard]] auto x() const noexcept -> unsigned { return x_; } + [[nodiscard]] auto y() const noexcept -> unsigned { return y_; } + + template + void insert_moves(T inserter) const + { + Directions d{md5_directions(str_)}; + if (x_ > 0 && (d & Directions::left) != Directions::none) { + *inserter++ = Node{str_ + 'L', x_ - 1, y_}; + } + if (y_ > 0 && (d & Directions::up) != Directions::none) { + *inserter++ = Node{str_ + 'U', x_, y_ - 1}; + } + if (x_ < 3 && (d & Directions::right) != Directions::none) { + *inserter++ = Node{str_ + 'R', x_ + 1, y_}; + } + if (y_ < 3 && (d & Directions::down) != Directions::none) { + *inserter++ = Node{str_ + 'D', x_, y_ + 1}; + } + } + +private: + Node(std::string s, unsigned x, unsigned y) : str_(std::move(s)), x_(x), y_(y) {} + + std::string str_; + unsigned x_{0}; + unsigned y_{0}; +}; + +auto main() -> int +{ + std::string line; + if (!std::getline(std::cin, line)) { + std::cerr << "Failed to read line\n"; + return 1; + } + + Node initial_state{line}; + + std::set visited; + std::set to_visit; + to_visit.insert(initial_state); + + while (!to_visit.empty()) { + auto it{to_visit.begin()}; + Node n{*it}; + to_visit.erase(it); + visited.insert(n); + if (n.x() == 3 && n.y() == 3) { + std::cout << "Movements " << n.str().substr(line.size()) << '\n'; + return 0; + } + else { + n.insert_moves(std::inserter(to_visit, to_visit.end())); + } + } + + std::cerr << "No solution.\n"; + return 1; +} diff --git a/2016/puzzle-17-02.CMakeLists.txt b/2016/puzzle-17-02.CMakeLists.txt new file mode 100644 index 0000000..1f298ff --- /dev/null +++ b/2016/puzzle-17-02.CMakeLists.txt @@ -0,0 +1,2 @@ +find_package(OpenSSL REQUIRED) +target_link_libraries("${puzzle_name}" OpenSSL::SSL) diff --git a/2016/puzzle-17-02.cc b/2016/puzzle-17-02.cc new file mode 100644 index 0000000..fd51c4d --- /dev/null +++ b/2016/puzzle-17-02.cc @@ -0,0 +1,117 @@ +// +// Created by Matthew Gretton-Dann on 06/12/2021. +// + +#include +#include +#include +#include +#include + +#include + +using MD5Digest = std::array; + +enum Directions { none = 0x0, up = 0x1, down = 0x2, left = 0x4, right = 0x8 }; +auto md5_directions(std::string const& s) -> Directions +{ + MD5Digest digest; + EVP_MD const* md{EVP_md5()}; + unsigned int md_len{0}; + + EVP_MD_CTX* md_context{EVP_MD_CTX_new()}; + assert(md_context != nullptr); + EVP_DigestInit_ex2(md_context, md, nullptr); + EVP_DigestUpdate(md_context, s.data(), s.length()); + EVP_DigestFinal_ex(md_context, digest.data(), &md_len); + Directions d{Directions::none}; + if (((digest[0] & 0xf0) >> 4) > 0xa) { + d = static_cast(d | Directions::up); + } + if ((digest[0] & 0xf) > 0xa) { + d = static_cast(d | Directions::down); + } + if (((digest[1] & 0xf0) >> 4) > 0xa) { + d = static_cast(d | Directions::left); + } + if ((digest[1] & 0xf) > 0xa) { + d = static_cast(d | Directions::right); + } + return d; +} + +struct Node +{ + explicit Node(std::string s) : str_(std::move(s)) {} + + auto operator==(Node const& rhs) const noexcept -> bool { return str_ == rhs.str_; } + + auto operator<(Node const& rhs) const noexcept -> bool + { + return str_.size() < rhs.str_.size() || (str_.size() == rhs.str_.size() && str_ < rhs.str_); + } + + [[nodiscard]] auto str() const -> std::string const& { return str_; } + [[nodiscard]] auto x() const noexcept -> unsigned { return x_; } + [[nodiscard]] auto y() const noexcept -> unsigned { return y_; } + + template + void insert_moves(T inserter) const + { + Directions d{md5_directions(str_)}; + if (x_ > 0 && (d & Directions::left) != Directions::none) { + *inserter++ = Node{str_ + 'L', x_ - 1, y_}; + } + if (y_ > 0 && (d & Directions::up) != Directions::none) { + *inserter++ = Node{str_ + 'U', x_, y_ - 1}; + } + if (x_ < 3 && (d & Directions::right) != Directions::none) { + *inserter++ = Node{str_ + 'R', x_ + 1, y_}; + } + if (y_ < 3 && (d & Directions::down) != Directions::none) { + *inserter++ = Node{str_ + 'D', x_, y_ + 1}; + } + } + +private: + Node(std::string s, unsigned x, unsigned y) : str_(std::move(s)), x_(x), y_(y) {} + + std::string str_; + unsigned x_{0}; + unsigned y_{0}; +}; + +auto main() -> int +{ + std::string line; + if (!std::getline(std::cin, line)) { + std::cerr << "Failed to read line\n"; + return 1; + } + + Node initial_state{line}; + + std::set visited; + std::set to_visit; + to_visit.insert(initial_state); + + std::size_t longest_path_len{0}; + while (!to_visit.empty()) { + auto it{to_visit.begin()}; + Node n{*it}; + to_visit.erase(it); + visited.insert(n); + if (n.x() == 3 && n.y() == 3) { + auto path_len{n.str().size() - line.size()}; + if (path_len > longest_path_len) { + longest_path_len = path_len; + } + } + else { + n.insert_moves(std::inserter(to_visit, to_visit.end())); + } + } + + std::cout << "Longest path " << longest_path_len << '\n'; + return 0; +}