commit 2f44ccc3a73a8eec059a51cb139a4242ad05333f Author: Matthew Gretton-Dann Date: Wed Dec 1 20:31:53 2021 +0000 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da6ed1a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Project exclude paths +/cmake-build-debug/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/advent-of-code.iml b/.idea/advent-of-code.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/advent-of-code.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..79b3c94 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..5d82457 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/2015/puzzle-01-01.cc b/2015/puzzle-01-01.cc new file mode 100644 index 0000000..ec8d52e --- /dev/null +++ b/2015/puzzle-01-01.cc @@ -0,0 +1,16 @@ +#include +#include +#include + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + int floor = 0; + for (auto c : line) { + assert(c == '(' || c == ')'); + floor += (c == '(') - (c == ')'); + } + std::cout << floor << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-01-02.cc b/2015/puzzle-01-02.cc new file mode 100644 index 0000000..804c132 --- /dev/null +++ b/2015/puzzle-01-02.cc @@ -0,0 +1,21 @@ +#include +#include +#include + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + int floor = 0; + std::string::size_type pos = 0; + for (auto c : line) { + ++pos; + assert(c == '(' || c == ')'); + floor += (c == '(') - (c == ')'); + if (floor == -1) { + break; + } + } + std::cout << pos << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-02-01.cc b/2015/puzzle-02-01.cc new file mode 100644 index 0000000..a5c4c8c --- /dev/null +++ b/2015/puzzle-02-01.cc @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +struct Box { + /** Construct box. + * \param s String representation of dimensions 'lxwxh' + */ + Box(std::string const &s) { + std::size_t pos = 0; + l_ = std::stoul(s, &pos, 10); + assert(s[pos] == 'x'); + auto s2 = s.substr(pos + 1); + w_ = std::stoul(s2, &pos, 10); + assert(s2[pos] == 'x'); + s2 = s2.substr(pos + 1); + h_ = std::stoul(s2, &pos, 10); + assert(pos == s2.length()); + } + + // How much paper does this box need? + unsigned long paper_needed() const { + unsigned long s1 = l_ * w_; + unsigned long s2 = w_ * h_; + unsigned long s3 = h_ * l_; + return 2 * (s1 + s2 + s3) + std::min({s1, s2, s3}); + } + + unsigned long l_; + unsigned long w_; + unsigned long h_; +}; + +int main(int argc, char **argv) { + unsigned long total = 0; + for (std::string line; std::getline(std::cin, line);) { + Box b(line); + auto amount = b.paper_needed(); + std::cout << "Box " << line << " needs " << amount << " sq.feet\n"; + total += amount; + } + std::cout << "Total = " << total << " square feet\n"; + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-02-02.cc b/2015/puzzle-02-02.cc new file mode 100644 index 0000000..3eea86d --- /dev/null +++ b/2015/puzzle-02-02.cc @@ -0,0 +1,56 @@ +#include +#include +#include +#include + +struct Box { + /** Construct box. + * \param s String representation of dimensions 'lxwxh' + */ + Box(std::string const &s) { + std::size_t pos = 0; + l_ = std::stoul(s, &pos, 10); + assert(s[pos] == 'x'); + auto s2 = s.substr(pos + 1); + w_ = std::stoul(s2, &pos, 10); + assert(s2[pos] == 'x'); + s2 = s2.substr(pos + 1); + h_ = std::stoul(s2, &pos, 10); + assert(pos == s2.length()); + } + + // How much paper does this box need? + unsigned long paper_needed() const { + unsigned long s1 = l_ * w_; + unsigned long s2 = w_ * h_; + unsigned long s3 = h_ * l_; + return 2 * (s1 + s2 + s3) + std::min({s1, s2, s3}); + } + + // How much ribbon do we need? + unsigned long ribbon_needed() const { + // The various side perimeters - we want the min of these multiplied by + // volume. + unsigned long p1 = 2 * (l_ + w_); + unsigned long p2 = 2 * (w_ + h_); + unsigned long p3 = 2 * (h_ + l_); + return l_ * w_ * h_ + std::min({p1, p2, p3}); + } + + unsigned long l_; + unsigned long w_; + unsigned long h_; +}; + +int main(int argc, char **argv) { + unsigned long total = 0; + for (std::string line; std::getline(std::cin, line);) { + Box b(line); + auto amount = b.ribbon_needed(); + std::cout << "Box " << line << " needs " << amount << " feet of ribbon\n"; + total += amount; + } + std::cout << "Total = " << total << " feet of ribbon\n"; + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-03-01.cc b/2015/puzzle-03-01.cc new file mode 100644 index 0000000..15e696d --- /dev/null +++ b/2015/puzzle-03-01.cc @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +struct Pos { + Pos(int x, int y) : x_(x), y_(y) {} + + bool operator<(Pos const &rhs) const noexcept { + return x_ < rhs.x_ || (x_ == rhs.x_ && y_ < rhs.y_); + } + + bool operator==(Pos const &rhs) const noexcept { + return x_ == rhs.x_ && y_ == rhs.y_; + } + + void move(char c) { + switch (c) { + case '>': + ++x_; + break; + case '<': + --x_; + break; + case '^': + ++y_; + break; + case 'v': + --y_; + break; + default: + std::cout << "Unrecognised character: " << c << "\n"; + break; + } + } + + int x_; + int y_; +}; + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + std::set visited; + Pos santa(0, 0); + visited.insert(santa); + for (auto c : line) { + santa.move(c); + visited.insert(santa); + } + std::cout << "Houses visited = " << visited.size() << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-03-02.cc b/2015/puzzle-03-02.cc new file mode 100644 index 0000000..556d6b5 --- /dev/null +++ b/2015/puzzle-03-02.cc @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +struct Pos { + Pos(int x, int y) : x_(x), y_(y) {} + + bool operator<(Pos const &rhs) const noexcept { + return x_ < rhs.x_ || (x_ == rhs.x_ && y_ < rhs.y_); + } + + bool operator==(Pos const &rhs) const noexcept { + return x_ == rhs.x_ && y_ == rhs.y_; + } + + void move(char c) noexcept { + switch (c) { + case '>': + ++x_; + break; + case '<': + --x_; + break; + case '^': + ++y_; + break; + case 'v': + --y_; + break; + default: + std::cout << "Unrecognised character: " << c << "\n"; + break; + } + } + + int x_; + int y_; +}; + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + std::set visited; + Pos santa(0, 0); + Pos robo_santa(0, 0); + visited.insert(santa); + visited.insert(robo_santa); + bool do_robo = false; + for (auto c : line) { + if (do_robo) { + robo_santa.move(c); + visited.insert(robo_santa); + } else { + santa.move(c); + visited.insert(santa); + } + do_robo = !do_robo; + } + std::cout << "Houses visited = " << visited.size() << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-04-01.CMakeLists.txt b/2015/puzzle-04-01.CMakeLists.txt new file mode 100644 index 0000000..1f298ff --- /dev/null +++ b/2015/puzzle-04-01.CMakeLists.txt @@ -0,0 +1,2 @@ +find_package(OpenSSL REQUIRED) +target_link_libraries("${puzzle_name}" OpenSSL::SSL) diff --git a/2015/puzzle-04-01.cc b/2015/puzzle-04-01.cc new file mode 100644 index 0000000..73b5df3 --- /dev/null +++ b/2015/puzzle-04-01.cc @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include + +#include + +using MD5Digest = unsigned char[EVP_MAX_MD_SIZE]; + +unsigned int md5(MD5Digest digest, std::string const &s) { + EVP_MD const* md{EVP_md5()}; + unsigned int md_len; + + EVP_MD_CTX* md_ctxt{EVP_MD_CTX_new()}; + assert(md_ctxt != NULL); + EVP_DigestInit_ex2(md_ctxt, md, NULL); + EVP_DigestUpdate(md_ctxt, s.data(), s.length()); + EVP_DigestFinal_ex(md_ctxt, digest, &md_len); + return md_len; +} + +bool is_valid(std::string const &s) { + MD5Digest digest; + auto len = md5(digest, s); + assert(len >= 3); + return digest[0] == 0 && digest[1] == 0 && (digest[2] & 0xf0) == 0; +} + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + unsigned i = 0; + while (!is_valid(line + std::to_string(i))) { + ++i; + } + std::cout << "Value = " << i << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-04-02.CMakeLists.txt b/2015/puzzle-04-02.CMakeLists.txt new file mode 100644 index 0000000..1f298ff --- /dev/null +++ b/2015/puzzle-04-02.CMakeLists.txt @@ -0,0 +1,2 @@ +find_package(OpenSSL REQUIRED) +target_link_libraries("${puzzle_name}" OpenSSL::SSL) diff --git a/2015/puzzle-04-02.cc b/2015/puzzle-04-02.cc new file mode 100644 index 0000000..d5f613a --- /dev/null +++ b/2015/puzzle-04-02.cc @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include + +#include + +using MD5Digest = unsigned char[EVP_MAX_MD_SIZE]; + +unsigned int md5(MD5Digest digest, std::string const &s) { + EVP_MD const* md{EVP_md5()}; + unsigned int md_len; + + EVP_MD_CTX* md_ctxt{EVP_MD_CTX_new()}; + assert(md_ctxt != NULL); + EVP_DigestInit_ex2(md_ctxt, md, NULL); + EVP_DigestUpdate(md_ctxt, s.data(), s.length()); + EVP_DigestFinal_ex(md_ctxt, digest, &md_len); + return md_len; +} + +bool is_valid(std::string const &s) { + MD5Digest digest; + auto len = md5(digest, s); + assert(len >= 3); + return digest[0] == 0 && digest[1] == 0 && digest[2] == 0; +} + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + unsigned i = 0; + while (!is_valid(line + std::to_string(i))) { + ++i; + } + std::cout << "Value = " << i << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-05-01.cc b/2015/puzzle-05-01.cc new file mode 100644 index 0000000..c919a06 --- /dev/null +++ b/2015/puzzle-05-01.cc @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +// Check if a string is nice: +// Nice strings have: +// >=3 vowels +// At least one double letter +// No instances of 'ab', 'cd', 'pq', or 'xy'. +bool is_nice(std::string const &s) noexcept { + unsigned vowel_count = 0; + bool repeated = false; + char last = '\0'; + + std::string vowels("aeiou"); + for (auto c : s) { + if (vowels.find(c) != std::string::npos) { + ++vowel_count; + } + if (c == last) { + repeated = true; + } + if ((last == 'a' && c == 'b') || (last == 'c' && c == 'd') || + (last == 'p' && c == 'q') || (last == 'x' && c == 'y')) { + return false; + } + last = c; + } + + return repeated && vowel_count >= 3; +} + +int main(int argc, char **argv) { + unsigned nice_strings; + for (std::string line; std::getline(std::cin, line);) { + nice_strings += is_nice(line); + } + std::cout << nice_strings << '\n'; + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-05-02.cc b/2015/puzzle-05-02.cc new file mode 100644 index 0000000..e63c908 --- /dev/null +++ b/2015/puzzle-05-02.cc @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include + +// Check if a string is nice: +// Nice strings have: +// repeated double letters - but not overlapping +// repeated letters separated by one other. +bool is_nice(std::string const &s) noexcept { + bool repeated_pair = false; + bool repeated_sep = false; + + std::cout << s; + for (std::string::size_type i = 0; i < s.length() - 2; ++i) { + // Find whether the two characters starting at i are repeated in the rest of + // the string. We don't have to look backwards as we will already have + // scanned it. + if (s.substr(i + 2).find(s.substr(i, 2)) != std::string::npos) { + repeated_pair = true; + std::cout << " repeated(" << s.substr(i, 2) << ")"; + } + if (s[i] == s[i + 2]) { + repeated_sep = true; + std::cout << " pair(" << s.substr(i, 3) << ")"; + } + } + if (repeated_pair && repeated_sep) { + std::cout << " match"; + } + std::cout << "\n"; + + return repeated_pair && repeated_sep; +} + +int main(int argc, char **argv) { + unsigned nice_strings; + for (std::string line; std::getline(std::cin, line);) { + nice_strings += is_nice(line); + } + std::cout << nice_strings << '\n'; + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-06-01.cc b/2015/puzzle-06-01.cc new file mode 100644 index 0000000..a55357f --- /dev/null +++ b/2015/puzzle-06-01.cc @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +enum class Action { TurnOn, Toggle, TurnOff }; + +using Point = std::pair; + +/// A command +struct Command { + Command(std::string const &s) { + const char *re = + "(turn on|toggle|turn off)\\s(\\d+),(\\d+)\\sthrough\\s(\\d+),(\\d+)"; + std::smatch m; + if (!std::regex_search(s, m, std::regex(re))) { + std::cerr << "Unable to interpret:" << s << "\n"; + assert(false); + } + if (m.str(1) == std::string("turn on")) { + act_ = Action::TurnOn; + } else if (m.str(1) == std::string("turn off")) { + act_ = Action::TurnOff; + } else if (m.str(1) == std::string("toggle")) { + act_ = Action::Toggle; + } else { + assert(false); + } + bottom_left_.first = std::stoul(m.str(2), nullptr, 10); + bottom_left_.second = std::stoul(m.str(3), nullptr, 10); + top_right_.first = std::stoul(m.str(4), nullptr, 10); + top_right_.second = std::stoul(m.str(5), nullptr, 10); + } + + Action act_; + Point bottom_left_; + Point top_right_; +}; + +/// Array of lights +template struct Array { + Array() noexcept { + for (unsigned i = 0; i < N; ++i) { + for (unsigned j = 0; j < N; ++j) { + lights_[i][j] = false; + } + } + } + + /// Apply a command + void apply(Command const &command) noexcept { + assert(command.bottom_left_.first < N); + assert(command.bottom_left_.second < N); + assert(command.top_right_.first < N); + assert(command.top_right_.second < N); + + for (unsigned i = command.bottom_left_.first; i <= command.top_right_.first; + ++i) { + for (unsigned j = command.bottom_left_.second; + j <= command.top_right_.second; ++j) { + switch (command.act_) { + case Action::TurnOn: + lights_[i][j] = true; + break; + case Action::Toggle: + lights_[i][j] = !lights_[i][j]; + break; + case Action::TurnOff: + lights_[i][j] = false; + break; + } + } + } + } + + /// How many lights are on + unsigned num_on() const noexcept { + unsigned count = 0; + for (unsigned i = 0; i < N; ++i) { + for (unsigned j = 0; j < N; ++j) { + count += lights_[i][j]; + } + } + + return count; + } + + /// Output a bitmap + void bitmap() const { + std::cout << "P1\n" << N << " " << N << "\n"; + for (unsigned i = 0; i < N; ++i) { + for (unsigned j = 0; j < N; ++j) { + std::cout << (lights_[i][j] ? "1" : "0"); + if (j % 70 == 0) { + std::cout << "\n"; + } + } + std::cout << "\n"; + } + } + + bool lights_[N][N]; +}; + +int main(int argc, char **argv) { + Array<1000> arr; + for (std::string line; std::getline(std::cin, line);) { + Command cmd(line); + arr.apply(cmd); + } + arr.bitmap(); + std::cout << arr.num_on() << '\n'; + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-06-02.cc b/2015/puzzle-06-02.cc new file mode 100644 index 0000000..ffd9f35 --- /dev/null +++ b/2015/puzzle-06-02.cc @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include + +enum class Action { TurnOn, Toggle, TurnOff }; +using Point = std::pair; +struct Command { + Command(std::string const &s) { + const char *re = + "(turn on|toggle|turn off)\\s(\\d+),(\\d+)\\sthrough\\s(\\d+),(\\d+)"; + std::smatch m; + if (!std::regex_search(s, m, std::regex(re))) { + std::cerr << "Unable to interpret:" << s << "\n"; + assert(false); + } + if (m.str(1) == std::string("turn on")) { + act_ = Action::TurnOn; + } else if (m.str(1) == std::string("turn off")) { + act_ = Action::TurnOff; + } else if (m.str(1) == std::string("toggle")) { + act_ = Action::Toggle; + } else { + assert(false); + } + bottom_left_.first = std::stoul(m.str(2), nullptr, 10); + bottom_left_.second = std::stoul(m.str(3), nullptr, 10); + top_right_.first = std::stoul(m.str(4), nullptr, 10); + top_right_.second = std::stoul(m.str(5), nullptr, 10); + } + + Action act_; + Point bottom_left_; + Point top_right_; +}; + +template struct Array { + Array() noexcept { + for (unsigned i = 0; i < N; ++i) { + for (unsigned j = 0; j < N; ++j) { + lights_[i][j] = 0; + } + } + } + + void apply(Command const &command) noexcept { + assert(command.bottom_left_.first < N); + assert(command.bottom_left_.second < N); + assert(command.top_right_.first < N); + assert(command.top_right_.second < N); + + for (unsigned i = command.bottom_left_.first; i <= command.top_right_.first; + ++i) { + for (unsigned j = command.bottom_left_.second; + j <= command.top_right_.second; ++j) { + switch (command.act_) { + case Action::TurnOn: + ++lights_[i][j]; + break; + case Action::Toggle: + lights_[i][j] += 2; + break; + case Action::TurnOff: + if (lights_[i][j] > 0) { + --lights_[i][j]; + } + break; + } + } + } + } + + unsigned brightness() const noexcept { + unsigned count = 0; + for (unsigned i = 0; i < N; ++i) { + for (unsigned j = 0; j < N; ++j) { + count += lights_[i][j]; + } + } + return count; + } + + /// Output a bitmap + void bitmap() const { + unsigned max = 0; + for (unsigned i = 0; i < N; ++i) { + for (unsigned j = 0; j < N; ++j) { + if (lights_[i][j] > max) { + max = lights_[i][j]; + } + } + } + std::cout << "P2\n" << N << " " << N << "\n" << max << "\n"; + for (unsigned i = 0; i < N; ++i) { + for (unsigned j = 0; j < N; ++j) { + std::cout << lights_[i][j] << "\n"; + } + } + } + + unsigned lights_[N][N]; +}; + +int main(int argc, char **argv) { + Array<1000> arr; + for (std::string line; std::getline(std::cin, line);) { + Command cmd(line); + arr.apply(cmd); + } + arr.bitmap(); + std::cout << arr.brightness() << '\n'; + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-07-01.cc b/2015/puzzle-07-01.cc new file mode 100644 index 0000000..d54873b --- /dev/null +++ b/2015/puzzle-07-01.cc @@ -0,0 +1,373 @@ +#include +#include +#include +#include +#include +#include +#include + +// Algorithm overview: +// +// Because I'm lazy we basically build up a list of 'instructions' and +// repeatedly walking through them executing the ones we can. We assume that +// each pass will achieve more than the previous pass as more signal values will +// have been determined (and they don't change between passes). Eventually the +// VM reaches a steady state and at that point we can determine what the value +// of the wire 'a'. +// +// This is fast enough and simple enough for our purposes. +// +// A "better" way would be to start from the instruction that sets 'a' and then +// execute the instructions that determine the signals coming into it, and so +// on. This would only require a single pass through the instruction list so +// would probably be significantly quicker. (But requires more thought in the +// implementation) + +// helper type for the visitor #4 +template struct Overloaded : Ts... { using Ts::operator()...; }; +// explicit deduction guide (not needed as of C++20) +template Overloaded(Ts...) -> Overloaded; + +/// Instruction action +enum class Action { + Set, ///< Set value + And, ///< And two values + Or, ///< Or two values + LShift, ///< Left shift + RShift, ///< Right shift + Not ///< Bitwise not +}; + +/// Pretty-print action +std::ostream &operator<<(std::ostream &os, Action act) { + switch (act) { + case Action::Set: + os << "SET"; + break; + case Action::And: + os << "AND"; + break; + case Action::Or: + os << "OR"; + break; + case Action::LShift: + os << "LSHIFT"; + break; + case Action::RShift: + os << "RSHIFT"; + break; + case Action::Not: + os << "NOT"; + break; + } + return os; +} + +using Value = std::uint16_t; ///< Value +using Wire = std::string; ///< Wire name (string) +using Signal = std::variant; ///< Either a wire or explicit value + +/// Outputter for a signal +std::ostream &operator<<(std::ostream &os, Signal const &signal) { + return std::visit( + [&os](auto &&arg) -> std::ostream & { + os << arg; + return os; + }, + signal); +} + +/** \brief An instruction. */ +struct Instruction { + /** \brief Construct an instruction. + * + * \subsection Grammar + * + * wire := [a-z]+ + * value := [0-9]+ + * signal := value | wire + * set := signal '->' wire + * not := 'NOT' set + * op := 'AND' | 'OR' | 'LSHIFT' | 'RSHIFT' + * binop := signal op signal '->' wire + * instr := binop | not | set + */ + Instruction(std::string const &s) { + if (parse_bin_op(s)) { + return; + } + if (parse_not(s)) { + return; + } + if (parse_set(s)) { + return; + } + std::cout << "Unrecognised string: " << s << "\n"; + assert(false); + } + + /// Get action + Action action() const noexcept { return act_; } + + /// Get the destination wire + Wire const &dest() const noexcept { return dest_; } + + /// Get the first (or only) source + Signal const &src1() const noexcept { return src1_; } + + /// Get the second source + Signal const &src2() const noexcept { + assert(act_ != Action::Set && act_ != Action::Not); + return src2_; + } + +private: + /// Parse a instruction. Return true if successful. + bool parse_not(std::string const &s) { + if (s.substr(0, 4) == "NOT ") { + std::string::size_type pos = 4; + while (s[pos] == ' ') { + ++pos; + } + return parse_set(s.substr(pos), Action::Not); + } + return false; + } + + /// Parse a instruction. Return true if successful. + bool parse_bin_op(std::string const &s) { + static const std::regex re("^([[:lower:][:digit:]]+) ([[:upper:]]+) " + "([[:lower:][:digit:]]+) -> ([[:lower:]]+)"); + std::smatch m; + if (!std::regex_search(s, m, re)) { + return false; + } + + if (m.str(2) == "AND") { + act_ = Action::And; + } else if (m.str(2) == "OR") { + act_ = Action::Or; + } else if (m.str(2) == "LSHIFT") { + act_ = Action::LShift; + } else if (m.str(2) == "RSHIFT") { + act_ = Action::RShift; + } else { + return false; + } + dest_ = m.str(4); + src1_ = make_signal(m.str(1)); + src2_ = make_signal(m.str(3)); + std::cout << act_ << " " << dest_ << ", " << src1_ << ", " << src2_ << "\n"; + return true; + } + + /// Parse a instruction. + /// + /// Also used for the latter half of parsing. ACT tells you what is + /// being parsed. Returns true if parsing successful. + bool parse_set(std::string const &s, Action act = Action::Set) { + static const std::regex re("^([[:lower:][:digit:]]+) -> ([[:lower:]]+)"); + std::smatch m; + if (!std::regex_search(s, m, re)) { + return false; + } + act_ = act; + dest_ = m.str(2); + src1_ = make_signal(m.str(1)); + std::cout << act_ << " " << dest_ << ", " << src1_ << "\n"; + return true; + } + + /// Make a Signal from a string. + Signal make_signal(std::string const &s) { + if (std::isdigit(s[0])) { + auto u = std::stoul(s, nullptr, 10); + assert(u <= UINT16_MAX); + return Signal(static_cast(u)); + } else { + return Signal(s); + } + } + + Action act_; ///< Action + Wire dest_; ///< Destination wire + Signal src1_, src2_; ///< Source signals +}; + +/// Outputter for an instruction. +std::ostream &operator<<(std::ostream &os, Instruction const &instr) { + os << instr.action() << " " << instr.dest() << ", " << instr.src1(); + if (instr.action() != Action::Set && instr.action() != Action::Not) { + os << ", " << instr.src2(); + } + return os; +} + +/// Ma +using ValueMap = std::map; ///< Map wires to values +using Instructions = std::vector; ///< Instructions to execute + +struct VM { + /// Add an instruction the the list we have + void add_instr(Instruction const &instr) { instrs_.push_back(instr); } + + /// Has this wire a known value? + bool has_value(Wire const &w) const noexcept { + return values_.find(w) != values_.end(); + } + + /// Has this signal a known value? + bool has_value(Signal const &s) const noexcept { + return std::visit(Overloaded{[](Value v) { return true; }, + [&](Wire const &w) { return has_value(w); }}, + s); + } + + /// Get the value on the wire + Value value(Wire const &w) const noexcept { + assert(has_value(w)); + return values_.find(w)->second; + } + + /// Get the value of a signal + Value value(Signal const &s) const noexcept { + return std::visit(Overloaded{[](Value v) { return v; }, + [&](Wire const &w) { return value(w); }}, + s); + } + + /// Set the value of a wire + void value(Wire const &w, Value value) { + auto [it, success] = values_.insert({w, value}); + assert(success); + } + + /// Set the value of a signal + void value(Signal const &s, Value v) { + std::visit(Overloaded{[v](Value v2) { assert(v == v2); }, + [&, v](Wire const &w) { value(w, v); }}, + s); + } + + /// Execute the instructions. Returns true if we have updated some wire + /// values. + bool execute() { + bool done_anything = false; + for (auto const &instr : instrs_) { + done_anything |= execute_instr(instr); + } + + return done_anything; + } + +private: + /** \brief Attempt to execute an instruction + * \param instr Instruction + * \return True if instruction was executed. + * + * An instruction may not be executed if the incoming signals have not been + * set yet. + */ + bool execute_instr(Instruction const &instr) { + std::cout << instr << " # "; + + // First of all check there is something to do - i.e. that the destination + // register has not been set already. + Wire dest = instr.dest(); + if (has_value(dest)) { + std::cout << "already has value: " << dest << " = " << value(dest) + << "\n"; + return false; + } + + switch (instr.action()) { + case Action::Set: + return execute_single_src(instr, [](Value src) { return src; }); + case Action::Not: + return execute_single_src(instr, [](Value src) { return ~src; }); + case Action::And: + return execute_double_src( + instr, [](Value src1, Value src2) { return src1 & src2; }); + case Action::Or: + return execute_double_src( + instr, [](Value src1, Value src2) { return src1 | src2; }); + case Action::LShift: + return execute_double_src( + instr, [](Value src1, Value src2) { return src1 << src2; }); + case Action::RShift: + return execute_double_src( + instr, [](Value src1, Value src2) { return src1 >> src2; }); + } + + return false; + } + + /** \brief Attempt to execute a single source instruction. + * \param instr Instruction + * \param fn How to modify the source value to the dest. + * \return True if we executed the function. + */ + bool execute_single_src(Instruction const &instr, + std::function fn) { + Wire dest = instr.dest(); + Signal src = instr.src1(); + if (has_value(src)) { + value(dest, fn(value(src))); + std::cout << "setting wire to: " << dest << " = " << value(dest) << "\n"; + return true; + } + + std::cout << "missing value for signal: " << src << "\n"; + return false; + } + + /** \brief Attempt to execute a two source instruction. + * \param instr Instruction + * \param fn How to modify the source values to the dest. + * \return True if we executed the function. + */ + bool execute_double_src(Instruction const &instr, + std::function fn) { + Wire dest = instr.dest(); + Signal src1 = instr.src1(); + Signal src2 = instr.src2(); + if (has_value(src1) && has_value(src2)) { + value(dest, fn(value(src1), value(src2))); + std::cout << "setting wire to: " << dest << " = " << value(dest) << "\n"; + return true; + } + + std::cout << "missing value for signals: " << src1 << ", " << src2 << "\n"; + return false; + } + + ValueMap values_; + Instructions instrs_; +}; + +int main(int argc, char **argv) { + VM vm; + + // Parse the input + for (std::string line; std::getline(std::cin, line);) { + Instruction instr(line); + vm.add_instr(instr); + } + + // Execute the VM until it reaches a steady state + bool changed = true; + while (changed) { + changed = vm.execute(); + } + + // Get the value of wire 'a' + Wire a = Wire("a"); + std::cout << "a = "; + if (!vm.has_value(a)) { + std::cout << "UNSET\n"; + } else { + std::cout << vm.value(a) << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-07-02.cc b/2015/puzzle-07-02.cc new file mode 100644 index 0000000..d54873b --- /dev/null +++ b/2015/puzzle-07-02.cc @@ -0,0 +1,373 @@ +#include +#include +#include +#include +#include +#include +#include + +// Algorithm overview: +// +// Because I'm lazy we basically build up a list of 'instructions' and +// repeatedly walking through them executing the ones we can. We assume that +// each pass will achieve more than the previous pass as more signal values will +// have been determined (and they don't change between passes). Eventually the +// VM reaches a steady state and at that point we can determine what the value +// of the wire 'a'. +// +// This is fast enough and simple enough for our purposes. +// +// A "better" way would be to start from the instruction that sets 'a' and then +// execute the instructions that determine the signals coming into it, and so +// on. This would only require a single pass through the instruction list so +// would probably be significantly quicker. (But requires more thought in the +// implementation) + +// helper type for the visitor #4 +template struct Overloaded : Ts... { using Ts::operator()...; }; +// explicit deduction guide (not needed as of C++20) +template Overloaded(Ts...) -> Overloaded; + +/// Instruction action +enum class Action { + Set, ///< Set value + And, ///< And two values + Or, ///< Or two values + LShift, ///< Left shift + RShift, ///< Right shift + Not ///< Bitwise not +}; + +/// Pretty-print action +std::ostream &operator<<(std::ostream &os, Action act) { + switch (act) { + case Action::Set: + os << "SET"; + break; + case Action::And: + os << "AND"; + break; + case Action::Or: + os << "OR"; + break; + case Action::LShift: + os << "LSHIFT"; + break; + case Action::RShift: + os << "RSHIFT"; + break; + case Action::Not: + os << "NOT"; + break; + } + return os; +} + +using Value = std::uint16_t; ///< Value +using Wire = std::string; ///< Wire name (string) +using Signal = std::variant; ///< Either a wire or explicit value + +/// Outputter for a signal +std::ostream &operator<<(std::ostream &os, Signal const &signal) { + return std::visit( + [&os](auto &&arg) -> std::ostream & { + os << arg; + return os; + }, + signal); +} + +/** \brief An instruction. */ +struct Instruction { + /** \brief Construct an instruction. + * + * \subsection Grammar + * + * wire := [a-z]+ + * value := [0-9]+ + * signal := value | wire + * set := signal '->' wire + * not := 'NOT' set + * op := 'AND' | 'OR' | 'LSHIFT' | 'RSHIFT' + * binop := signal op signal '->' wire + * instr := binop | not | set + */ + Instruction(std::string const &s) { + if (parse_bin_op(s)) { + return; + } + if (parse_not(s)) { + return; + } + if (parse_set(s)) { + return; + } + std::cout << "Unrecognised string: " << s << "\n"; + assert(false); + } + + /// Get action + Action action() const noexcept { return act_; } + + /// Get the destination wire + Wire const &dest() const noexcept { return dest_; } + + /// Get the first (or only) source + Signal const &src1() const noexcept { return src1_; } + + /// Get the second source + Signal const &src2() const noexcept { + assert(act_ != Action::Set && act_ != Action::Not); + return src2_; + } + +private: + /// Parse a instruction. Return true if successful. + bool parse_not(std::string const &s) { + if (s.substr(0, 4) == "NOT ") { + std::string::size_type pos = 4; + while (s[pos] == ' ') { + ++pos; + } + return parse_set(s.substr(pos), Action::Not); + } + return false; + } + + /// Parse a instruction. Return true if successful. + bool parse_bin_op(std::string const &s) { + static const std::regex re("^([[:lower:][:digit:]]+) ([[:upper:]]+) " + "([[:lower:][:digit:]]+) -> ([[:lower:]]+)"); + std::smatch m; + if (!std::regex_search(s, m, re)) { + return false; + } + + if (m.str(2) == "AND") { + act_ = Action::And; + } else if (m.str(2) == "OR") { + act_ = Action::Or; + } else if (m.str(2) == "LSHIFT") { + act_ = Action::LShift; + } else if (m.str(2) == "RSHIFT") { + act_ = Action::RShift; + } else { + return false; + } + dest_ = m.str(4); + src1_ = make_signal(m.str(1)); + src2_ = make_signal(m.str(3)); + std::cout << act_ << " " << dest_ << ", " << src1_ << ", " << src2_ << "\n"; + return true; + } + + /// Parse a instruction. + /// + /// Also used for the latter half of parsing. ACT tells you what is + /// being parsed. Returns true if parsing successful. + bool parse_set(std::string const &s, Action act = Action::Set) { + static const std::regex re("^([[:lower:][:digit:]]+) -> ([[:lower:]]+)"); + std::smatch m; + if (!std::regex_search(s, m, re)) { + return false; + } + act_ = act; + dest_ = m.str(2); + src1_ = make_signal(m.str(1)); + std::cout << act_ << " " << dest_ << ", " << src1_ << "\n"; + return true; + } + + /// Make a Signal from a string. + Signal make_signal(std::string const &s) { + if (std::isdigit(s[0])) { + auto u = std::stoul(s, nullptr, 10); + assert(u <= UINT16_MAX); + return Signal(static_cast(u)); + } else { + return Signal(s); + } + } + + Action act_; ///< Action + Wire dest_; ///< Destination wire + Signal src1_, src2_; ///< Source signals +}; + +/// Outputter for an instruction. +std::ostream &operator<<(std::ostream &os, Instruction const &instr) { + os << instr.action() << " " << instr.dest() << ", " << instr.src1(); + if (instr.action() != Action::Set && instr.action() != Action::Not) { + os << ", " << instr.src2(); + } + return os; +} + +/// Ma +using ValueMap = std::map; ///< Map wires to values +using Instructions = std::vector; ///< Instructions to execute + +struct VM { + /// Add an instruction the the list we have + void add_instr(Instruction const &instr) { instrs_.push_back(instr); } + + /// Has this wire a known value? + bool has_value(Wire const &w) const noexcept { + return values_.find(w) != values_.end(); + } + + /// Has this signal a known value? + bool has_value(Signal const &s) const noexcept { + return std::visit(Overloaded{[](Value v) { return true; }, + [&](Wire const &w) { return has_value(w); }}, + s); + } + + /// Get the value on the wire + Value value(Wire const &w) const noexcept { + assert(has_value(w)); + return values_.find(w)->second; + } + + /// Get the value of a signal + Value value(Signal const &s) const noexcept { + return std::visit(Overloaded{[](Value v) { return v; }, + [&](Wire const &w) { return value(w); }}, + s); + } + + /// Set the value of a wire + void value(Wire const &w, Value value) { + auto [it, success] = values_.insert({w, value}); + assert(success); + } + + /// Set the value of a signal + void value(Signal const &s, Value v) { + std::visit(Overloaded{[v](Value v2) { assert(v == v2); }, + [&, v](Wire const &w) { value(w, v); }}, + s); + } + + /// Execute the instructions. Returns true if we have updated some wire + /// values. + bool execute() { + bool done_anything = false; + for (auto const &instr : instrs_) { + done_anything |= execute_instr(instr); + } + + return done_anything; + } + +private: + /** \brief Attempt to execute an instruction + * \param instr Instruction + * \return True if instruction was executed. + * + * An instruction may not be executed if the incoming signals have not been + * set yet. + */ + bool execute_instr(Instruction const &instr) { + std::cout << instr << " # "; + + // First of all check there is something to do - i.e. that the destination + // register has not been set already. + Wire dest = instr.dest(); + if (has_value(dest)) { + std::cout << "already has value: " << dest << " = " << value(dest) + << "\n"; + return false; + } + + switch (instr.action()) { + case Action::Set: + return execute_single_src(instr, [](Value src) { return src; }); + case Action::Not: + return execute_single_src(instr, [](Value src) { return ~src; }); + case Action::And: + return execute_double_src( + instr, [](Value src1, Value src2) { return src1 & src2; }); + case Action::Or: + return execute_double_src( + instr, [](Value src1, Value src2) { return src1 | src2; }); + case Action::LShift: + return execute_double_src( + instr, [](Value src1, Value src2) { return src1 << src2; }); + case Action::RShift: + return execute_double_src( + instr, [](Value src1, Value src2) { return src1 >> src2; }); + } + + return false; + } + + /** \brief Attempt to execute a single source instruction. + * \param instr Instruction + * \param fn How to modify the source value to the dest. + * \return True if we executed the function. + */ + bool execute_single_src(Instruction const &instr, + std::function fn) { + Wire dest = instr.dest(); + Signal src = instr.src1(); + if (has_value(src)) { + value(dest, fn(value(src))); + std::cout << "setting wire to: " << dest << " = " << value(dest) << "\n"; + return true; + } + + std::cout << "missing value for signal: " << src << "\n"; + return false; + } + + /** \brief Attempt to execute a two source instruction. + * \param instr Instruction + * \param fn How to modify the source values to the dest. + * \return True if we executed the function. + */ + bool execute_double_src(Instruction const &instr, + std::function fn) { + Wire dest = instr.dest(); + Signal src1 = instr.src1(); + Signal src2 = instr.src2(); + if (has_value(src1) && has_value(src2)) { + value(dest, fn(value(src1), value(src2))); + std::cout << "setting wire to: " << dest << " = " << value(dest) << "\n"; + return true; + } + + std::cout << "missing value for signals: " << src1 << ", " << src2 << "\n"; + return false; + } + + ValueMap values_; + Instructions instrs_; +}; + +int main(int argc, char **argv) { + VM vm; + + // Parse the input + for (std::string line; std::getline(std::cin, line);) { + Instruction instr(line); + vm.add_instr(instr); + } + + // Execute the VM until it reaches a steady state + bool changed = true; + while (changed) { + changed = vm.execute(); + } + + // Get the value of wire 'a' + Wire a = Wire("a"); + std::cout << "a = "; + if (!vm.has_value(a)) { + std::cout << "UNSET\n"; + } else { + std::cout << vm.value(a) << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-08-01.cc b/2015/puzzle-08-01.cc new file mode 100644 index 0000000..a02cf9b --- /dev/null +++ b/2015/puzzle-08-01.cc @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include +#include + +enum class State { Begin, Normal, Escape, Hex1, Hex2, End }; + +std::string unescape(std::string const &s) { + std::string unescaped; + static const std::string hex = "0123456789abcdef0123456789ABCDEF"; + + State state = State::Begin; + unsigned char byte = 0; + for (auto c : s) { + switch (state) { + case State::Begin: + assert(c == '"'); + state = State::Normal; + break; + case State::Normal: + if (c == '\\') { + state = State::Escape; + } else if (c == '"') { + state = State::End; + } else { + unescaped += c; + } + break; + case State::Escape: + if (c == '\\' || c == '"') { + state = State::Normal; + unescaped += c; + } else if (c == 'x') { + byte = 0; + state = State::Hex1; + } else { + assert(false); + } + break; + case State::Hex1: { + auto idx = hex.find(c); + assert(idx != std::string::npos); + byte = idx & 0xf; + state = State::Hex2; + break; + } + case State::Hex2: { + auto idx = hex.find(c); + assert(idx != std::string::npos); + byte = (byte << 4) + (idx & 0xf); + unescaped += (char)byte; + state = State::Normal; + break; + } + case State::End: + assert(false); + } + } + return unescaped; +} + +int main(int argc, char **argv) { + unsigned len = 0; + + // Parse the input + for (std::string line; std::getline(std::cin, line);) { + std::string unescaped = unescape(line); + len += line.length() - unescaped.length(); + std::cout << line << ": " << line.length() << " written bytes, " + << unescaped.length() << " memory bytes, difference: " + << line.length() - unescaped.length() << "\n"; + } + + std::cout << len << "\n"; + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-08-02.cc b/2015/puzzle-08-02.cc new file mode 100644 index 0000000..1616099 --- /dev/null +++ b/2015/puzzle-08-02.cc @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include + +std::string escape(std::string const &s) { + std::string escaped; + escaped += '"'; + for (auto c : s) { + if (c == '\\' || c == '"') { + escaped += '\\'; + } + escaped += c; + } + escaped += '"'; + return escaped; +} + +int main(int argc, char **argv) { + unsigned len = 0; + + // Parse the input + for (std::string line; std::getline(std::cin, line);) { + std::string escaped = escape(line); + len += escaped.length() - line.length(); + std::cout << line << ": " << line.length() << " memory bytes, " + << escaped.length() << " escaped bytes, difference: " + << escaped.length() - line.length() << "\n"; + } + + std::cout << len << "\n"; + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-09-01.cc b/2015/puzzle-09-01.cc new file mode 100644 index 0000000..6976508 --- /dev/null +++ b/2015/puzzle-09-01.cc @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using Weight = unsigned; +using Node = std::string; +using Edge = std::pair; +using Nodes = std::set; +using Edges = std::map; + +struct Graph { + void add_edge(std::string const &s) { + static const std::regex re("(.+) to (.+) = (\\d+)"); + std::smatch m; + if (!std::regex_search(s, m, re)) { + std::cout << "Failed to match: " << s << "\n"; + assert(false); + return; + } + auto n1 = m.str(1); + auto n2 = m.str(2); + auto w = std::stoul(m.str(3)); + auto edge = std::make_pair(n1, n2); + auto back_edge = std::make_pair(n2, n1); + + nodes_.insert(n1); + nodes_.insert(n2); + weights_.insert({edge, w}); + weights_.insert({back_edge, w}); + std::cout << n1 << " <-> " << n2 << " weight: " << w << "\n"; + } + + Weight solve_tsp() const { + Weight min_weight = ~0U; + std::vector nodes(nodes_.begin(), nodes_.end()); + std::sort(nodes.begin(), nodes.end()); + do { + Weight current_weight = 0; + std::cout << "\r" << nodes[0]; + for (std::size_t i = 1; i < nodes.size(); ++i) { + std::cout << " <- " << nodes[i] << std::flush; + Edge e = std::make_pair(nodes[i - 1], nodes[i]); + auto it = weights_.find(e); + assert(it != weights_.end()); + current_weight += it->second; + } + std::cout << " " << std::flush; + if (current_weight < min_weight) { + min_weight = current_weight; + std::cout << "\nNew minimum weight: " << min_weight << "\n"; + } + } while (std::next_permutation(nodes.begin(), nodes.end())); + std::cout << "\n"; + + return min_weight; + } + + Nodes nodes_; + Edges weights_; +}; + +int main(int argc, char **argv) { + Graph g; + + // Parse the input + for (std::string line; std::getline(std::cin, line);) { + g.add_edge(line); + } + + auto w = g.solve_tsp(); + std::cout << "Minimum weight = " << w << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-09-02.cc b/2015/puzzle-09-02.cc new file mode 100644 index 0000000..b58a9e8 --- /dev/null +++ b/2015/puzzle-09-02.cc @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using Weight = unsigned; +using Node = std::string; +using Edge = std::pair; +using Nodes = std::set; +using Edges = std::map; + +struct Graph { + void add_edge(std::string const &s) { + static const std::regex re("(.+) to (.+) = (\\d+)"); + std::smatch m; + if (!std::regex_search(s, m, re)) { + std::cout << "Failed to match: " << s << "\n"; + assert(false); + return; + } + auto n1 = m.str(1); + auto n2 = m.str(2); + auto w = std::stoul(m.str(3)); + auto edge = std::make_pair(n1, n2); + auto back_edge = std::make_pair(n2, n1); + + nodes_.insert(n1); + nodes_.insert(n2); + weights_.insert({edge, w}); + weights_.insert({back_edge, w}); + std::cout << n1 << " <-> " << n2 << " weight: " << w << "\n"; + } + + Weight solve_tsp() const { + Weight min_weight = ~0U; + visit_all_perms([&min_weight](Weight w) { + if (w < min_weight) { + std::cout << "\nNew minimum weight: " << w << "\n"; + min_weight = w; + } + }); + return min_weight; + } + + Weight solve_max_tsp() const { + Weight max_weight = 0; + visit_all_perms([&max_weight](Weight w) { + if (w > max_weight) { + std::cout << "\nNew maximum weight: " << w << "\n"; + max_weight = w; + } + }); + return max_weight; + } + +private: + template void visit_all_perms(Fn fn) const { + std::vector nodes(nodes_.begin(), nodes_.end()); + std::sort(nodes.begin(), nodes.end()); + do { + Weight current_weight = 0; + std::cout << "\r" << nodes[0]; + for (std::size_t i = 1; i < nodes.size(); ++i) { + std::cout << " <- " << nodes[i] << std::flush; + Edge e = std::make_pair(nodes[i - 1], nodes[i]); + auto it = weights_.find(e); + assert(it != weights_.end()); + current_weight += it->second; + } + std::cout << " " << std::flush; + fn(current_weight); + } while (std::next_permutation(nodes.begin(), nodes.end())); + std::cout << "\n"; + } + Nodes nodes_; + Edges weights_; +}; + +int main(int argc, char **argv) { + Graph g; + + // Parse the input + for (std::string line; std::getline(std::cin, line);) { + g.add_edge(line); + } + + auto w = g.solve_tsp(); + std::cout << "Minimum weight = " << w << "\n"; + w = g.solve_max_tsp(); + std::cout << "Maximum weight = " << w << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-10-01.cc b/2015/puzzle-10-01.cc new file mode 100644 index 0000000..cf07dc0 --- /dev/null +++ b/2015/puzzle-10-01.cc @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +std::string look_and_say(std::string const &s) { + std::string result; + for (std::string::size_type i = 0; i < s.length();) { + unsigned num = 0; + char c = s[i]; + while (s[i] == c) { + ++i; + ++num; + } + result += std::to_string(num) + c; + } + + return result; +} + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + std::cout << "Application 0, length = " << line.length() << ": " << line + << "\n"; + for (int i = 1; i < 41; ++i) { + line = look_and_say(line); + std::cout << "Application " << i << ", length = " << line.length() + << "\n"; + } + std::cout << "Length: " << line.length() << "\n"; + } + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-10-02.cc b/2015/puzzle-10-02.cc new file mode 100644 index 0000000..3c9211b --- /dev/null +++ b/2015/puzzle-10-02.cc @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +std::string look_and_say(std::string const &s) { + std::string result; + for (std::string::size_type i = 0; i < s.length();) { + unsigned num = 0; + char c = s[i]; + while (s[i] == c) { + ++i; + ++num; + } + result += std::to_string(num) + c; + } + + return result; +} + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + std::cout << "Application 0, length = " << line.length() << ": " << line + << "\n"; + for (int i = 1; i < 51; ++i) { + line = look_and_say(line); + std::cout << "Application " << i << ", length = " << line.length() + << "\n"; + } + std::cout << "Length: " << line.length() << "\n"; + } + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-11-01.cc b/2015/puzzle-11-01.cc new file mode 100644 index 0000000..c4ba99b --- /dev/null +++ b/2015/puzzle-11-01.cc @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +bool illegal_char(char c) { return c == 'i' || c == 'l' || c == 'o'; } + +void pre_advance_password(std::string &s) { + std::string::size_type pos = 0; + while (pos < s.length() && !illegal_char(s[pos])) { + ++pos; + } + if (pos == s.length()) { + return; + } + ++s[pos++]; + while (pos < s.length()) { + s[pos++] = 'a'; + } +} + +void advance_password(std::string &s) { + auto pos = s.length() - 1; + while (true) { + if (s[pos] == 'z') { + assert(pos != 0); + s[pos] = 'a'; + pos -= 1; + } else { + ++s[pos]; + if (illegal_char(s[pos])) { + ++s[pos]; + } + return; + } + } +} + +bool valid_password(std::string const &s) { + unsigned double_count = 0; + bool run = false; + char last2 = '\0'; + char last = '\0'; + for (auto c : s) { + if (c == last && last2 != c) { + ++double_count; + } else if (c == last + 1 && c == last2 + 2) { + run = true; + } + last2 = last; + last = c; + } + + return double_count >= 2 && run; +} + +std::string next_password(std::string const &s) { + std::string result = s; + pre_advance_password(result); + do { + advance_password(result); + } while (!valid_password(result)); + return result; +} + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + std::string next = next_password(line); + std::cout << "Current password: " << line << "; Next password: " << next + << "\n"; + } + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-11-02.cc b/2015/puzzle-11-02.cc new file mode 100644 index 0000000..5722a11 --- /dev/null +++ b/2015/puzzle-11-02.cc @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +bool illegal_char(char c) { return c == 'i' || c == 'l' || c == 'o'; } + +void pre_advance_password(std::string &s) { + std::string::size_type pos = 0; + while (pos < s.length() && !illegal_char(s[pos])) { + ++pos; + } + if (pos == s.length()) { + return; + } + ++s[pos++]; + while (pos < s.length()) { + s[pos++] = 'a'; + } +} + +void advance_password(std::string &s) { + auto pos = s.length() - 1; + while (true) { + if (s[pos] == 'z') { + assert(pos != 0); + s[pos] = 'a'; + pos -= 1; + } else { + ++s[pos]; + if (illegal_char(s[pos])) { + ++s[pos]; + } + return; + } + } +} + +bool valid_password(std::string const &s) { + unsigned double_count = 0; + bool run = false; + char last2 = '\0'; + char last = '\0'; + for (auto c : s) { + if (c == last && last2 != c) { + ++double_count; + } else if (c == last + 1 && c == last2 + 2) { + run = true; + } + last2 = last; + last = c; + } + + return double_count >= 2 && run; +} + +std::string next_password(std::string const &s) { + std::string result = s; + pre_advance_password(result); + do { + advance_password(result); + } while (!valid_password(result)); + return result; +} + +int main(int argc, char **argv) { + for (std::string line; std::getline(std::cin, line);) { + std::string next = next_password(line); + std::string next2 = next_password(next); + std::cout << "Current password: " << line << "; Next password: " << next + << "; Subsequent password: " << next2 << "\n"; + } + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-12-01.cc b/2015/puzzle-12-01.cc new file mode 100644 index 0000000..e22ec4a --- /dev/null +++ b/2015/puzzle-12-01.cc @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int parse_numbers(std::string const &s) { + static const std::regex re("-?\\d+"); + std::string left = s; + std::smatch m; + int acc = 0; + while (std::regex_search(left, m, re)) { + acc += std::stoi(m.str(0)); + left = m.suffix(); + } + + return acc; +} + +int main(int argc, char **argv) { + int acc = 0; + for (std::string line; std::getline(std::cin, line);) { + acc += parse_numbers(line); + } + + std::cout << "Accumulated value: " << acc << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-12-02.cc b/2015/puzzle-12-02.cc new file mode 100644 index 0000000..3691ff1 --- /dev/null +++ b/2015/puzzle-12-02.cc @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int do_parse(std::string const &s, std::string::size_type &pos) { + int result = 0; + bool ignore = false; + + while (pos < s.size()) { + if (s[pos] == '{') { + ++pos; + result += do_parse(s, pos); + } else if (s[pos] == '[') { + ++pos; + result += do_parse(s, pos); + } else if (s[pos] == '}') { + ++pos; + return ignore ? 0 : result; + } else if (s[pos] == ']') { + ++pos; + return result; + } else if (s[pos] == '"') { + ++pos; + auto e = s.find('"', pos); + assert(e != std::string::npos); + std::string v = s.substr(pos, e - pos); + if (v == "red") { + ignore = true; + } + pos = e + 1; + } else if (std::isdigit(s[pos]) || s[pos] == '-') { + std::size_t len = 0; + result += std::stoi(s.substr(pos), &len); + pos += len; + } else { + assert(s[pos] == ',' || s[pos] == ':'); + ++pos; + } + } + + return result; +} + +int parse_numbers(std::string const &s) { + std::string::size_type pos = 0; + int result = do_parse(s, pos); + assert(pos == s.size()); + return result; +} + +int main(int argc, char **argv) { + int acc = 0; + for (std::string line; std::getline(std::cin, line);) { + acc += parse_numbers(line); + } + + std::cout << "Accumulated value: " << acc << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-13-01.cc b/2015/puzzle-13-01.cc new file mode 100644 index 0000000..a7a3e35 --- /dev/null +++ b/2015/puzzle-13-01.cc @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Graph { + using Node = std::string; + using Weight = int; + using Nodes = std::set; + using NodeList = std::vector; + using Edge = std::pair; + using EdgeWeights = std::map; + + void add_edge(std::string const &s) { + static const std::regex re( + "(\\w+) would (gain|lose) (\\d+) happiness units? " + "by sitting next to (\\w+)."); + std::smatch m; + if (std::regex_search(s, m, re)) { + nodes_.insert(m.str(1)); + nodes_.insert(m.str(4)); + int delta = std::stoi(m.str(3)); + if (m.str(2) == "lose") { + delta = -delta; + } + weights_.insert({{m.str(1), m.str(4)}, delta}); + } else { + assert(false); + } + } + + Weight max_happiness() const { + int max_happiness = INT_MIN; + NodeList nl(nodes_.begin(), nodes_.end()); + std::sort(nl.begin(), nl.end()); + do { + std::cout << "\r"; + for (auto const &s : nl) { + std::cout << s << " "; + } + int h = happiness(nl); + std::cout << "Happiness = " << h << " "; + if (h > max_happiness) { + std::cout << "\n"; + max_happiness = h; + } + } while (std::next_permutation(nl.begin(), nl.end())); + + std::cout << "\n"; + return max_happiness; + } + + int happiness(NodeList const &nl) const { + int h = 0; + h += weights_.find(std::make_pair(nl[nl.size() - 1], nl[0]))->second; + h += weights_.find(std::make_pair(nl[0], nl[nl.size() - 1]))->second; + for (std::size_t off = 1; off < nl.size(); ++off) { + h += weights_.find(std::make_pair(nl[off - 1], nl[off]))->second; + h += weights_.find(std::make_pair(nl[off], nl[off - 1]))->second; + } + return h; + } + + Nodes nodes_; + EdgeWeights weights_; +}; + +int main(int argc, char **argv) { + Graph g; + for (std::string line; std::getline(std::cin, line);) { + g.add_edge(line); + } + + auto h = g.max_happiness(); + std::cout << "Max happiness: " << h << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-13-02.cc b/2015/puzzle-13-02.cc new file mode 100644 index 0000000..16fc1e9 --- /dev/null +++ b/2015/puzzle-13-02.cc @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Graph { + Graph() { nodes_.insert("self"); } + using Node = std::string; + using Weight = int; + using Nodes = std::set; + using NodeList = std::vector; + using Edge = std::pair; + using EdgeWeights = std::map; + + void add_edge(std::string const &s) { + static const std::regex re( + "(\\w+) would (gain|lose) (\\d+) happiness units? " + "by sitting next to (\\w+)."); + std::smatch m; + if (std::regex_search(s, m, re)) { + nodes_.insert(m.str(1)); + weights_.insert({{m.str(1), "self"}, 0}); + weights_.insert({{"self", m.str(1)}, 0}); + nodes_.insert(m.str(4)); + int delta = std::stoi(m.str(3)); + if (m.str(2) == "lose") { + delta = -delta; + } + weights_.insert({{m.str(1), m.str(4)}, delta}); + } else { + assert(false); + } + } + + Weight max_happiness() const { + int max_happiness = INT_MIN; + NodeList nl(nodes_.begin(), nodes_.end()); + std::sort(nl.begin(), nl.end()); + do { + std::cout << "\r"; + for (auto const &s : nl) { + std::cout << s << " "; + } + int h = happiness(nl); + std::cout << "Happiness = " << h << " "; + if (h > max_happiness) { + std::cout << "\n"; + max_happiness = h; + } + } while (std::next_permutation(nl.begin(), nl.end())); + + std::cout << "\n"; + return max_happiness; + } + + int happiness(NodeList const &nl) const { + int h = 0; + h += weights_.find(std::make_pair(nl[nl.size() - 1], nl[0]))->second; + h += weights_.find(std::make_pair(nl[0], nl[nl.size() - 1]))->second; + for (std::size_t off = 1; off < nl.size(); ++off) { + h += weights_.find(std::make_pair(nl[off - 1], nl[off]))->second; + h += weights_.find(std::make_pair(nl[off], nl[off - 1]))->second; + } + return h; + } + + Nodes nodes_; + EdgeWeights weights_; +}; + +int main(int argc, char **argv) { + Graph g; + for (std::string line; std::getline(std::cin, line);) { + g.add_edge(line); + } + + auto h = g.max_happiness(); + std::cout << "Max happiness: " << h << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-14-01.cc b/2015/puzzle-14-01.cc new file mode 100644 index 0000000..d2f10a3 --- /dev/null +++ b/2015/puzzle-14-01.cc @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Distance = unsigned long; + +Distance distance(std::string const &s, unsigned t) { + static const std::regex re("(\\w+) can fly (\\d+) km/s for (\\d+) seconds?, " + "but then must rest for (\\d+) seconds?."); + std::smatch m; + if (std::regex_search(s, m, re)) { + unsigned fly_speed = std::stoul(m.str(2)); + unsigned fly_time = std::stoul(m.str(3)); + unsigned rest_time = std::stoul(m.str(4)); + + // Period and number of them + unsigned period = fly_time + rest_time; + unsigned periods = t / period; + + // How far did we fly in the complete periods. + Distance result = (fly_speed * fly_time * periods); + + // And the remainder distance + t -= periods * period; + t = std::min(t, fly_time); + result += t * fly_speed; + + std::cout << m.str(1) << "(" << fly_speed << ", " << fly_time << ", " + << rest_time << ") = " << result << "\n"; + return result; + } else { + assert(false); + } +} + +int main(int argc, char **argv) { + Distance max_d = 0; + for (std::string line; std::getline(std::cin, line);) { + max_d = std::max(max_d, distance(line, 2503)); + } + + std::cout << "Max distance: " << max_d << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-14-02.cc b/2015/puzzle-14-02.cc new file mode 100644 index 0000000..f1c31d8 --- /dev/null +++ b/2015/puzzle-14-02.cc @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Distance = unsigned long; +using Time = unsigned long; +using Speed = unsigned long; + +struct Reindeer; +using ReindeerSet = std::set; + +struct Reindeer { + Reindeer(std::string const &s) { + static const std::regex re( + "(\\w+) can fly (\\d+) km/s for (\\d+) seconds?, " + "but then must rest for (\\d+) seconds?."); + std::smatch m; + if (std::regex_search(s, m, re)) { + name_ = m.str(1); + speed_ = std::stoul(m.str(2)); + fly_time_ = std::stoul(m.str(3)); + rest_time_ = std::stoul(m.str(4)); + } else { + assert(false); + } + } + + bool operator<(Reindeer const &rhs) const noexcept { + return name_ < rhs.name_; + } + + std::string const &name() const { return name_; } + + Distance distance(Time t) const { // Period and number of them + Time period = fly_time_ + rest_time_; + unsigned periods = t / period; + + // How far did we fly in the complete periods. + Distance result = (speed_ * fly_time_ * periods); + + // And the remainder distance + t -= periods * period; + t = std::min(t, fly_time_); + result += t * speed_; + + return result; + } + + std::string name_; + Speed speed_; + Time fly_time_; + Time rest_time_; +}; + +int main(int argc, char **argv) { + ReindeerSet reindeer; + + for (std::string line; std::getline(std::cin, line);) { + reindeer.insert(Reindeer(line)); + } + + std::map score; + for (unsigned t = 1; t < 2504; ++t) { + auto it = + std::max_element(reindeer.begin(), reindeer.end(), + [t](Reindeer const &lhs, Reindeer const &rhs) -> bool { + return lhs.distance(t) < rhs.distance(t); + }); + auto [iit, success] = score.insert({it->name(), 1}); + if (!success) { + iit->second++; + } + } + + auto it = std::max_element(score.begin(), score.end(), + [](auto const &lhs, auto const &rhs) -> bool { + return lhs.second < rhs.second; + }); + std::cout << it->first << " wins with a score of " << it->second << "\n"; + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-15-01.cc b/2015/puzzle-15-01.cc new file mode 100644 index 0000000..73cd569 --- /dev/null +++ b/2015/puzzle-15-01.cc @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Score = long; +using PropertyMap = std::map; + +struct Ingredient { + explicit Ingredient(std::string const &s) { + auto colon = s.find(':'); + name_ = s.substr(0, colon); + auto suffix = s.substr(colon + 1); + std::cout << name_ << "\n"; + while (!suffix.empty()) { + static const std::regex re(",? (\\w+) (-?\\d+)"); + std::smatch m; + if (std::regex_search(suffix, m, re)) { + auto [it, success] = + properties_.insert({m.str(1), std::stol(m.str(2))}); + assert(success); + std::cout << " " << it->first << ": " << it->second << "\n"; + suffix = m.suffix(); + } else { + assert(false); + } + } + } + + bool operator<(Ingredient const &rhs) const noexcept { + return name_ < rhs.name_; + } + + std::string const &name() const noexcept { return name_; } + + void update_score(Score amount, PropertyMap &totals) const { + for (auto const &kv : properties_) { + if (kv.first == "calories") { + continue; + } + auto [it, success] = totals.insert({kv.first, amount * kv.second}); + if (!success) { + it->second += amount * kv.second; + } + } + } + +private: + std::string name_; + PropertyMap properties_; +}; + +struct Ingredients { + void add_ingredient(std::string const &s) { + ingredients_.push_back(Ingredient(s)); + } + + Score best_combination(Score amount) const { + PropertyMap totals; + return best_combination(amount, ingredients_.begin(), 0UL, totals); + } + +private: + Score best_combination(Score amount, + std::vector::const_iterator it, + Score best_score, PropertyMap &totals) const { + it->update_score(amount, totals); + auto it2 = it; + ++it2; + if (it2 == ingredients_.end()) { + auto score = calculate_score(totals); + best_score = std::max(best_score, score); + it->update_score(-amount, totals); + return best_score; + } + + for (auto allocation = amount - 1; allocation > 0; --allocation) { + it->update_score(-1, totals); + best_score = + std::max(best_score, best_combination(amount - allocation, it2, + best_score, totals)); + } + it->update_score(-1, totals); + + return best_score; + } + + Score calculate_score(PropertyMap const &totals) const { + Score r = 1; + for (auto const &kv : totals) { + if (kv.first == "calories") { + continue; + } + if (kv.second < 0) { + return 0; + } + r *= kv.second; + } + + return r; + } + + std::vector ingredients_; +}; + +int main(int argc, char **argv) { + Ingredients ingredients; + + std::string line; + while (std::getline(std::cin, line)) { + ingredients.add_ingredient(line); + } + + auto r = ingredients.best_combination(100); + std::cout << "Solution: " << r << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-15-02.cc b/2015/puzzle-15-02.cc new file mode 100644 index 0000000..2ba1a5f --- /dev/null +++ b/2015/puzzle-15-02.cc @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Score = long; +using PropertyMap = std::map; + +struct Ingredient { + explicit Ingredient(std::string const &s) { + auto colon = s.find(':'); + name_ = s.substr(0, colon); + auto suffix = s.substr(colon + 1); + std::cout << name_ << "\n"; + while (!suffix.empty()) { + static const std::regex re(",? (\\w+) (-?\\d+)"); + std::smatch m; + if (std::regex_search(suffix, m, re)) { + auto [it, success] = + properties_.insert({m.str(1), std::stol(m.str(2))}); + assert(success); + std::cout << " " << it->first << ": " << it->second << "\n"; + suffix = m.suffix(); + } else { + assert(false); + } + } + } + + bool operator<(Ingredient const &rhs) const noexcept { + return name_ < rhs.name_; + } + + std::string const &name() const noexcept { return name_; } + + void update_score(Score amount, PropertyMap &totals) const { + for (auto const &kv : properties_) { + auto [it, success] = totals.insert({kv.first, amount * kv.second}); + if (!success) { + it->second += amount * kv.second; + } + } + } + +private: + std::string name_; + PropertyMap properties_; +}; + +struct Ingredients { + void add_ingredient(std::string const &s) { + ingredients_.push_back(Ingredient(s)); + } + + Score best_combination(Score amount) const { + PropertyMap totals; + return best_combination(amount, ingredients_.begin(), 0UL, totals); + } + +private: + Score best_combination(Score amount, + std::vector::const_iterator it, + Score best_score, PropertyMap &totals) const { + it->update_score(amount, totals); + auto it2 = it; + ++it2; + if (it2 == ingredients_.end()) { + auto score = calculate_score(totals); + best_score = std::max(best_score, score); + it->update_score(-amount, totals); + return best_score; + } + + for (auto allocation = amount - 1; allocation > 0; --allocation) { + it->update_score(-1, totals); + best_score = + std::max(best_score, best_combination(amount - allocation, it2, + best_score, totals)); + } + it->update_score(-1, totals); + + return best_score; + } + + Score calculate_score(PropertyMap const &totals) const { + Score r = 1; + Score calories = 0; + for (auto const &kv : totals) { + if (kv.first == "calories") { + calories += kv.second; + continue; + } + + if (kv.second < 0) { + return 0; + } + + r *= kv.second; + } + + return calories != 500 ? -1 : r; + } + + std::vector ingredients_; +}; + +int main(int argc, char **argv) { + Ingredients ingredients; + + std::string line; + while (std::getline(std::cin, line)) { + ingredients.add_ingredient(line); + } + + auto r = ingredients.best_combination(100); + std::cout << "Solution: " << r << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-16-01.cc b/2015/puzzle-16-01.cc new file mode 100644 index 0000000..6025793 --- /dev/null +++ b/2015/puzzle-16-01.cc @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using InfoMap = std::map; + +static const InfoMap the_real_aunt_sue({{"children", 3}, + {"cats", 7}, + {"samoyeds", 2}, + {"pomeranians", 3}, + {"akitas", 0}, + {"vizslas", 0}, + {"goldfish", 5}, + {"trees", 3}, + {"cars", 2}, + {"perfumes", 1}}); + +bool matches_sue(std::string const &s) { + assert(s.substr(0, 4) == "Sue "); + std::size_t pos = 4; + std::size_t len = 0; + unsigned long id = std::stoul(s.substr(4), &len); + std::cout << "Sue " << id << ": "; + pos += len; + assert(s[pos] == ':'); + ++pos; + while (pos < s.size()) { + if (s[pos] == ' ' || s[pos] == ',') { + ++pos; + } else { + auto colon = s.find(':', pos); + assert(colon != std::string::npos); + std::string name = s.substr(pos, colon - pos); + pos = colon + 1; + while (pos < s.size() && s[pos] == ' ') { + ++pos; + } + auto amount = std::stoul(s.substr(pos), &len); + pos += len; + auto it = the_real_aunt_sue.find(name); + std::cout << " " << name << "=" << amount; + assert(it != the_real_aunt_sue.end()); + if (it->second != amount) { + std::cout << " (NO!)\n"; + return false; + } + } + } + std::cout << "(YES!)\n"; + return true; +} + +int main(int argc, char **argv) { + std::string line; + while (std::getline(std::cin, line)) { + if (matches_sue(line)) { + std::cout << "MATCH: " << line << "\n"; + return 0; + } + } + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-16-02.cc b/2015/puzzle-16-02.cc new file mode 100644 index 0000000..e01aa19 --- /dev/null +++ b/2015/puzzle-16-02.cc @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Comparison = std::function; +using Info = std::pair; +using InfoMap = std::map; + +bool Equality(unsigned long a, unsigned long b) { return a == b; } +bool GreaterThan(unsigned long a, unsigned long b) { return a > b; } +bool LessThan(unsigned long a, unsigned long b) { return a < b; } + +static const InfoMap the_real_aunt_sue({{"children", {3, Equality}}, + {"cats", {7, GreaterThan}}, + {"samoyeds", {2, Equality}}, + {"pomeranians", {3, LessThan}}, + {"akitas", {0, Equality}}, + {"vizslas", {0, Equality}}, + {"goldfish", {5, LessThan}}, + {"trees", {3, GreaterThan}}, + {"cars", {2, Equality}}, + {"perfumes", {1, Equality}}}); + +bool matches_sue(std::string const &s) { + assert(s.substr(0, 4) == "Sue "); + std::size_t pos = 4; + std::size_t len = 0; + unsigned long id = std::stoul(s.substr(4), &len); + std::cout << "Sue " << id << ": "; + pos += len; + assert(s[pos] == ':'); + ++pos; + while (pos < s.size()) { + if (s[pos] == ' ' || s[pos] == ',') { + ++pos; + } else { + auto colon = s.find(':', pos); + assert(colon != std::string::npos); + std::string name = s.substr(pos, colon - pos); + pos = colon + 1; + while (pos < s.size() && s[pos] == ' ') { + ++pos; + } + auto amount = std::stoul(s.substr(pos), &len); + pos += len; + auto it = the_real_aunt_sue.find(name); + std::cout << " " << name << "=" << amount; + assert(it != the_real_aunt_sue.end()); + if (!it->second.second(amount, it->second.first)) { + std::cout << " (NO!)\n"; + return false; + } + } + } + std::cout << "(YES!)\n"; + return true; +} + +int main(int argc, char **argv) { + std::string line; + while (std::getline(std::cin, line)) { + if (matches_sue(line)) { + std::cout << "MATCH: " << line << "\n"; + return 0; + } + } + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-17-01.cc b/2015/puzzle-17-01.cc new file mode 100644 index 0000000..d448616 --- /dev/null +++ b/2015/puzzle-17-01.cc @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Quantity = unsigned long; +constexpr Quantity total = 150; + +using Quantities = std::vector; + +unsigned count_combinations(Quantities::const_iterator it, + Quantities::const_iterator end, Quantity amount, + unsigned depth = 0) { + // We have no room for this container + std::cout << std::string(depth, ' ') << *it << ": " << amount << "\n"; + if (amount < *it) { + return 0; + } + + // Matched + if (amount == *it) { + return 1; + } + + amount -= *it; + auto result = 0; + while (++it != end) { + result += count_combinations(it, end, amount, depth + 2); + } + return result; +} + +unsigned count_combinations(Quantities const &containers) { + unsigned result = 0; + for (auto it = containers.begin(); it != containers.end(); ++it) { + result += count_combinations(it, containers.end(), total); + } + return result; +} + +int main(int argc, char **argv) { + std::string line; + Quantities containers; + while (std::getline(std::cin, line)) { + containers.push_back(std::stoul(line)); + } + + // Sort containers into reverse order + std::sort(containers.begin(), containers.end()); + std::reverse(containers.begin(), containers.end()); + + unsigned result = count_combinations(containers); + std::cout << "Solution: " << result << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-17-02.cc b/2015/puzzle-17-02.cc new file mode 100644 index 0000000..08b295a --- /dev/null +++ b/2015/puzzle-17-02.cc @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Quantity = unsigned long; +constexpr Quantity total = 150; + +using Quantities = std::vector; + +template +unsigned count_combinations(Quantities::const_iterator it, + Quantities::const_iterator end, Quantity amount, + unsigned depth, Fn1 base_result, + unsigned init_addend, Fn2 adder) { + // We have no room for this container + std::cout << std::string(depth, ' ') << *it << ": " << amount; + if (amount < *it) { + std::cout << " - Not big enough.\n"; + return init_addend; + } + + // Matched + if (amount == *it) { + std::cout << " - Filled - result = " << base_result(depth) << "\n"; + return base_result(depth); + } + + std::cout << " - Recursing: initial result = " << init_addend << "\n"; + amount -= *it; + auto result = init_addend; + while (++it != end) { + auto child_score = count_combinations(it, end, amount, depth + 1, + base_result, init_addend, adder); + result = adder(result, child_score); + } + std::cout << std::string(depth, ' ') << "Recursion result: " << result + << "\n"; + return result; +} + +template +unsigned count_combinations(Quantities const &containers, Quantity amount, + Fn1 base_result, unsigned init_addend, Fn2 adder) { + unsigned result = init_addend; + for (auto it = containers.begin(); it != containers.end(); ++it) { + result = adder(result, count_combinations(it, containers.end(), total, 0, + base_result, init_addend, adder)); + } + return result; +} + +unsigned find_shortest_combination(Quantities const &containers) { + return count_combinations( + containers, total, [](unsigned depth) { return depth; }, UINT_MAX, + [](unsigned current, unsigned child_score) { + return std::min(current, child_score); + }); +} + +unsigned count_min_length_combinations(Quantities const &containers, + unsigned expected_depth) { + return count_combinations( + containers, total, + [expected_depth](unsigned depth) { return depth == expected_depth; }, 0, + [](unsigned current, unsigned child_score) { + return current + child_score; + }); +} + +int main(int argc, char **argv) { + std::string line; + Quantities containers; + while (std::getline(std::cin, line)) { + containers.push_back(std::stoul(line)); + } + + // Sort containers into reverse order + std::sort(containers.begin(), containers.end()); + std::reverse(containers.begin(), containers.end()); + unsigned short_depth = find_shortest_combination(containers); + std::cout << "Shortest length result: " << short_depth << "\n"; + unsigned result = count_min_length_combinations(containers, short_depth); + std::cout << "Solution: " << result << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-18-01.cc b/2015/puzzle-18-01.cc new file mode 100644 index 0000000..9d3f2e3 --- /dev/null +++ b/2015/puzzle-18-01.cc @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ConwayState { + ConwayState(std::size_t width, std::string const &s) + : width_(width), state_(width_ * width_, 0) { + assert(s.size() == width_ * width_); + + for (std::size_t i = 0; i < state_.size(); ++i) { + if (s[i] == '#') { + flip(i); + } + } + } + + ConwayState next_state() const { + ConwayState next(*this); + for (std::size_t i = 0; i < state_.size(); ++i) { + if (state_[i] == (2 | active_) || state_[i] == (3 | active_)) { + continue; + } + if (state_[i] == 3 || (state_[i] & active_) != 0) { + next.flip(i); + } + } + + return next; + } + + std::size_t num_active() const { + return std::accumulate(state_.begin(), state_.end(), std::size_t(0), + [](std::size_t current, unsigned char info) { + return current + ((info & active_) != 0); + }); + } + +private: + void flip(std::size_t idx) { + state_[idx] = state_[idx] ^ active_; + int delta = ((state_[idx] & active_) == active_) ? 1 : -1; + std::size_t row = idx / width_; + std::size_t col = idx % width_; + for (std::size_t r = std::max(std::size_t(1), row) - 1; + r < std::min(width_, row + 2); ++r) { + for (std::size_t c = std::max(std::size_t(1), col) - 1; + c < std::min(width_, col + 2); ++c) { + if (r == row && c == col) { + continue; + } + state_[r * width_ + c] = state_[r * width_ + c] + delta; + } + } + } + + std::size_t width_; + std::vector state_; + + static constexpr unsigned char active_ = 0x80; + friend std::ostream &operator<<(std::ostream &os, ConwayState const &state); +}; + +std::ostream &operator<<(std::ostream &os, ConwayState const &state) { + std::size_t c = 0; + for (auto s : state.state_) { + os << (s & ConwayState::active_ ? '#' : '.'); + os << (s & ~ConwayState::active_); + ++c; + if (c == state.width_) { + os << '\n'; + c = 0; + } + } + return os; +} + +int main(int argc, char **argv) { + std::string line; + std::size_t width = 0; + std::string init; + while (std::getline(std::cin, line)) { + if (width == 0) { + width = line.size(); + } + assert(width == line.size()); + init += line; + } + ConwayState conway(width, init); + for (unsigned i = 0; i < 100; ++i) { + conway = conway.next_state(); + } + std::cout << "Solution: " << conway.num_active() << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-18-02.cc b/2015/puzzle-18-02.cc new file mode 100644 index 0000000..4ef8def --- /dev/null +++ b/2015/puzzle-18-02.cc @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ConwayState { + ConwayState(std::size_t width, std::string const &s) + : width_(width), state_(width_ * width_, 0) { + assert(s.size() == width_ * width_); + + flip(0); + flip(width_ - 1); + flip(width_ * (width_ - 1)); + flip(width_ * width_ - 1); + + for (std::size_t i = 0; i < state_.size(); ++i) { + if (s[i] == '#') { + flip(i); + } + } + } + + ConwayState next_state() const { + ConwayState next(*this); + for (std::size_t i = 0; i < state_.size(); ++i) { + if (i == 0 || i == width_ - 1 || i == width_ * (width_ - 1) || + i == width_ * width_ - 1) { + continue; + } + if (state_[i] == (2 | active_) || state_[i] == (3 | active_)) { + continue; + } + if (state_[i] == 3 || (state_[i] & active_) != 0) { + next.flip(i); + } + } + + return next; + } + + std::size_t num_active() const { + return std::accumulate(state_.begin(), state_.end(), std::size_t(0), + [](std::size_t current, unsigned char info) { + return current + ((info & active_) != 0); + }); + } + +private: + void flip(std::size_t idx) { + state_[idx] = state_[idx] ^ active_; + int delta = ((state_[idx] & active_) == active_) ? 1 : -1; + std::size_t row = idx / width_; + std::size_t col = idx % width_; + for (std::size_t r = std::max(std::size_t(1), row) - 1; + r < std::min(width_, row + 2); ++r) { + for (std::size_t c = std::max(std::size_t(1), col) - 1; + c < std::min(width_, col + 2); ++c) { + if (r == row && c == col) { + continue; + } + state_[r * width_ + c] = state_[r * width_ + c] + delta; + } + } + } + + std::size_t width_; + std::vector state_; + + static constexpr unsigned char active_ = 0x80; + friend std::ostream &operator<<(std::ostream &os, ConwayState const &state); +}; + +std::ostream &operator<<(std::ostream &os, ConwayState const &state) { + std::size_t c = 0; + for (auto s : state.state_) { + os << (s & ConwayState::active_ ? '#' : '.'); + os << (s & ~ConwayState::active_); + ++c; + if (c == state.width_) { + os << '\n'; + c = 0; + } + } + return os; +} + +int main(int argc, char **argv) { + std::string line; + std::size_t width = 0; + std::string init; + while (std::getline(std::cin, line)) { + if (width == 0) { + width = line.size(); + } + assert(width == line.size()); + init += line; + } + ConwayState conway(width, init); + for (unsigned i = 0; i < 100; ++i) { + conway = conway.next_state(); + } + std::cout << "Solution: " << conway.num_active() << "\n"; + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-19-01.cc b/2015/puzzle-19-01.cc new file mode 100644 index 0000000..d5e783b --- /dev/null +++ b/2015/puzzle-19-01.cc @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + std::multimap replacements; + bool collecting_replacements = true; + std::string line; + while (std::getline(std::cin, line)) { + if (line.empty()) { + collecting_replacements = false; + } else if (collecting_replacements) { + auto sep = line.find(" => "); + replacements.insert({line.substr(0, sep), line.substr(sep + 4)}); + } else { + std::set new_molecules; + for (unsigned pos = 0; pos < line.size(); ++pos) { + auto [it, ite] = replacements.equal_range(line.substr(pos, 1)); + while (it != ite) { + new_molecules.insert(line.substr(0, pos) + it->second + + line.substr(pos + 1)); + ++it; + } + if (pos < line.size() - 1) { + auto [it, ite] = replacements.equal_range(line.substr(pos, 2)); + while (it != ite) { + new_molecules.insert(line.substr(0, pos) + it->second + + line.substr(pos + 2)); + ++it; + } + } + } + std::cout << line << "\n"; + std::cout << "Solution: " << new_molecules.size() << "\n"; + } + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-19-02.cc b/2015/puzzle-19-02.cc new file mode 100644 index 0000000..d38c739 --- /dev/null +++ b/2015/puzzle-19-02.cc @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +std::size_t dedup(Map const &replacements, std::string &molecule) { + std::size_t steps = 0; + for (auto const &kv : replacements) { + if (kv.first != kv.second + kv.second) { + continue; + } + std::size_t pos = std::string::npos; + do { + pos = molecule.find(kv.first); + if (pos != std::string::npos) { + molecule = molecule.substr(0, pos) + kv.second + + molecule.substr(pos + kv.first.size()); + ++steps; + } + } while (pos != std::string::npos); + } + + return steps; +} + +#if 0 +std::size_t +simple_find(std::unordered_map const &replacements, + std::string &molecule) { + std::size_t steps = 0; + bool changed = true; + + while (!molecule.empty() && molecule != "e" && changed) { + changed = false; + for (auto const &kv : replacements) { + std::size_t pos = 0; + while (pos != std::string::npos) { + pos = molecule.find(kv.first, pos); + if (pos != std::string::npos) { + auto nm = molecule.substr(0, pos) + kv.second + + molecule.substr(pos + kv.first.size()); + bool good = true; + for (auto const &kv2 : replacements) { + auto left = + std::max(kv2.first.size() - 1, pos) + 1 - kv2.first.size(); + if (nm.substr(left, kv2.first.size() * 2 + kv.second.size() - 2) + .find(kv2.first) != std::string::npos) { + good = false; + break; + } + } + if (good) { + ++steps; + // std::cout << "Step: " << steps << ": " << molecule << " => " << + // nm + // << "\n"; + molecule = nm; + changed = true; + } + ++pos; + } + } + } + } + + return steps; +} + +std::size_t +find_molecule(std::unordered_map const &replacements, + std::string const &m) { + std::string molecule = m; + std::size_t steps = 0; + steps += dedup(replacements, molecule); + steps += simple_find(replacements, molecule); + + std::unordered_map current_molecules = { + {molecule, steps}}; + while (!current_molecules.empty() && + current_molecules.find("e") == current_molecules.end()) { + std::unordered_map new_molecules; + + std::size_t mi = 0; + for (auto const &m : current_molecules) { + ++mi; + std::string molecule = m.first; + std::size_t steps = m.second; + ++steps; + for (auto const &kv : replacements) { + std::size_t pos = 0; + while (pos != std::string::npos) { + pos = molecule.find(kv.first, pos); + if (pos != std::string::npos) { + bool good = false; + auto nm = molecule.substr(0, pos) + kv.second + + molecule.substr(pos + kv.first.size()); + for (auto const &kv2 : replacements) { + auto left = std::max(kv2.first.size(), pos) - kv2.first.size(); + if (nm.substr(left, kv2.first.size() * 2 + kv.second.size()) + .find(kv2.first) != std::string::npos) { + good = true; + break; + } + } + if (good) { + auto ns = steps; + ns += dedup(replacements, nm); + ns += simple_find(replacements, nm); + std::cout << '\r' << steps << ": " << mi << " " << kv.first + << " => " + << kv.second; //<< " ==> " + // << nm << "\n"; + auto [it, success] = new_molecules.insert({nm, ns}); + if (!success) { + it->second = std::min(it->second, ns); + } + } + ++pos; + } + } + } + } + + current_molecules = std::move(new_molecules); + std::cout << "\nStep " << 1000000 << " : " << current_molecules.size() + << " modulecules.\n" + << std::flush; + } + std::cout << "\n"; + + return current_molecules.find("e")->second; +} +#endif + +template +bool is_good_replacement(Map const &replacements, std::string const &nm, + std::size_t pos, std::size_t replace_len) { + for (auto const &kv : replacements) { + auto left = std::max(kv.first.size(), pos) - kv.first.size(); + if (nm.substr(left, kv.first.size() * 2 + replace_len).find(kv.first) != + std::string::npos) { + return true; + } + } + return false; +} + +template +std::size_t find_molecule(Map &replacements, std::string const &molecule) { + std::random_device rd; + std::mt19937 g(rd()); + while (true) { + std::string m = molecule; + bool changed = true; + std::shuffle(replacements.begin(), replacements.end(), g); + std::size_t steps = 0; + do { + changed = false; + for (auto const &kv : replacements) { + auto pos = m.find(kv.first); + if (pos != std::string::npos) { + m = m.substr(0, pos) + kv.second + m.substr(pos + kv.first.length()); + std::cout << "\r" << steps << " "; + ++steps; + changed = true; + if (m == "e") { + std::cout << '\n'; + return steps; + } + } + } + std::cout << '\n'; + } while (changed); + } +} + +int main(int argc, char **argv) { + std::vector> replacements; + bool collecting_replacements = true; + std::string line; + while (std::getline(std::cin, line)) { + if (line.empty()) { + collecting_replacements = false; + } else if (collecting_replacements) { + auto sep = line.find(" => "); + replacements.push_back({line.substr(sep + 4), line.substr(0, sep)}); + } else { + std::cout << line << "\n"; + auto time = find_molecule(replacements, line); + std::cout << "Solution: " << time << "\n"; + } + } + + return 0; +} \ No newline at end of file diff --git a/2015/puzzle-20-01.cc b/2015/puzzle-20-01.cc new file mode 100644 index 0000000..f25e167 --- /dev/null +++ b/2015/puzzle-20-01.cc @@ -0,0 +1,37 @@ +#include +#include +#include + +int main() { + std::string line; + std::getline(std::cin, line); + + auto target{std::stoul(line)}; + + /* This is a really stupid way to do this in terms of effort, but it produces the right answer and doesn't involve + * thinking too much: + * + * n is the current house we're at. + */ + for (auto n{2UL}; true; ++n) { + auto amt{0UL}; + /* Find the number of presents delivered to house `n`. We do this by walking through all numbers <= sqrt(n) and + * seeing if they are a factor. If so we add presents for that number (i) and also (n/i), being careful not to + * double count for square roots. This reduces the amount of work we have to do significantly. + */ + for (auto i{1UL}; i <= (n / i); ++i) { + if (n % i == 0) { + amt += i * 10; + if (i != n / i) { + amt += (n / i) * 10; + } + } + } + if (amt >= target) { + std::cout << "Target: " << target << " met at: " << n << " with amount: " << amt << '\n'; + return EXIT_SUCCESS; + } + } + + return EXIT_FAILURE; +} \ No newline at end of file diff --git a/2015/puzzle-20-02.cc b/2015/puzzle-20-02.cc new file mode 100644 index 0000000..e7a37ac --- /dev/null +++ b/2015/puzzle-20-02.cc @@ -0,0 +1,42 @@ +#include +#include +#include + +int main() { + std::string line; + std::getline(std::cin, line); + + auto target{std::stoul(line)}; + + /* This is a really stupid way to do this in terms of effort, but it produces the right answer and doesn't involve + * thinking too much: + * + * n is the current house we're at. + */ + for (auto n{2UL}; true; ++n) { + auto amt{0UL}; + /* Find the number of presents delivered to house `n`. We do this by walking through all numbers <= sqrt(n) and + * seeing if they are a factor. If so we add presents for that number (i) and also (n/i), being careful not to + * double count for square roots. This reduces the amount of work we have to do significantly. + * + * For the second part we also check to ensure we've not at the 51st delivery or greater for this elf. + */ + for (auto i{1UL}; i <= (n / i); ++i) { + if (n % i == 0) { + auto i2{n / i}; + if (i2 <= 50) { + amt += i * 11; + } + if (i <= 50 && i != i2) { + amt += i2 * 11; + } + } + } + if (amt >= target) { + std::cout << "Target: " << target << " met at: " << n << " with amount: " << amt << '\n'; + return EXIT_SUCCESS; + } + } + + return EXIT_FAILURE; +} \ No newline at end of file diff --git a/2015/puzzle-21-01.cc b/2015/puzzle-21-01.cc new file mode 100644 index 0000000..92f495c --- /dev/null +++ b/2015/puzzle-21-01.cc @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include + +using namespace std::string_literals; + +struct Item { + std::string name_; + unsigned long cost_; + unsigned long damage_; + unsigned long armour_; + + bool operator==(Item const& rhs) const { return name_ == rhs.name_; } +}; + +struct Person { + Person(unsigned long hp, unsigned long damage, unsigned long armour) : name_("Enemy"), hp_(hp), + cost_(std::numeric_limits::max()), + damage_(damage), + armour_(armour) {} + + Person(Item const &weapon, Item const &armour, Item const &left_ring, Item const &right_ring) : + name_("Player: "s + weapon.name_ + ", "s + armour.name_ + ", "s + left_ring.name_ + ", " + + right_ring.name_), + hp_(100), + cost_(weapon.cost_ + armour.cost_ + left_ring.cost_ + right_ring.cost_), + damage_(weapon.damage_ + armour.damage_ + left_ring.damage_ + right_ring.damage_), + armour_(weapon.armour_ + armour.armour_ + left_ring.armour_ + right_ring.armour_) {} + + unsigned get_attacked(Person const &attacker) { + auto damage{(attacker.damage_ > armour_) ? attacker.damage_ - armour_ : 1}; + hp_ = (hp_ > damage) ? hp_ - damage : 0; + return hp_; + } + + std::string name_; + unsigned long hp_; + unsigned long cost_; + unsigned long damage_; + unsigned long armour_; +}; + +unsigned long get_value(std::string const &begin) { + std::string line; + if (!std::getline(std::cin, line)) { + std::cerr << "Missing line in input\n"; + std::exit(1); + } + if (line.substr(0, begin.length()) != begin) { + std::cerr << "Line doesn't begin with: " << begin << '\n'; + std::exit(1); + } + return std::stoul(line.substr(begin.length())); +} + +bool me_beats_enemy(Person me, Person enemy) { + while (true) { + enemy.get_attacked(me); + if (enemy.hp_ == 0) { return true; } + me.get_attacked(enemy); + if (me.hp_ == 0) { return false; } + } +} + +int main() { + const std::string hp_begin{"Hit Points: "}; + const std::string d_begin{"Damage: "}; + const std::string a_begin{"Armor: "}; + + const std::vector weapons{ + {"Dagger", 8, 4, 0}, + {"Shortsword", 10, 5, 0}, + {"Warhammer", 25, 6, 0}, + {"Longsword", 40, 7, 0}, + {"Greataxe", 74, 8, 0}, + }; + + const std::vector armours{ + {"Nothing", 0, 0, 0}, + {"Leather", 13, 0, 1}, + {"Chainmail", 31, 0, 2}, + {"Splintmail", 53, 0, 3}, + {"Bandedmail", 75, 0, 4}, + {"Platedmail", 102, 0, 5}, + }; + + const std::vector rings{ + {"Empty", 0, 0, 0}, + {"Damage +1", 25, 1, 0}, + {"Damage +2", 50, 2, 0}, + {"Damage +3", 100, 3, 0}, + {"Defense +1", 20, 0, 1}, + {"Defense +2", 40, 0, 2}, + {"Defense +3", 80, 0, 3}, + }; + + + auto enemy_hp{get_value(hp_begin)}; + auto enemy_damage{get_value(d_begin)}; + auto enemy_armour{get_value(a_begin)}; + + Person best_result{0, 0, 0}; + Person enemy{enemy_hp, enemy_damage, enemy_armour}; + + for (auto const &weapon: weapons) { + if (weapon.cost_ > best_result.cost_) { continue; } + for (auto const &armour: armours) { + if (weapon.cost_ + armour.cost_ > best_result.cost_) { continue; } + for (auto const &left_ring: rings) { + if (weapon.cost_ + armour.cost_ + left_ring.cost_ > best_result.cost_) { continue; } + for (auto const &right_ring: rings) { + if (weapon.cost_ + armour.cost_ + left_ring.cost_ + right_ring.cost_ > + best_result.cost_) { continue; } + if (left_ring == right_ring) continue; + Person me{weapon, armour, left_ring, right_ring}; + if (me_beats_enemy(me, enemy) && me.cost_ < best_result.cost_) { + best_result = me; + } + } + } + } + } + + std::cout << "Best person: " << best_result.name_ << " at cost of " << best_result.cost_ << '\n'; +} \ No newline at end of file diff --git a/2015/puzzle-21-02.cc b/2015/puzzle-21-02.cc new file mode 100644 index 0000000..55f0733 --- /dev/null +++ b/2015/puzzle-21-02.cc @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include + +using namespace std::string_literals; + +struct Item { + std::string name_; + unsigned long cost_; + unsigned long damage_; + unsigned long armour_; + + bool operator==(Item const& rhs) const { return name_ == rhs.name_; } +}; + +struct Person { + Person(unsigned long hp, unsigned long damage, unsigned long armour) : name_("Enemy"), hp_(hp), + cost_(std::numeric_limits::max()), + damage_(damage), + armour_(armour) {} + + Person(Item const &weapon, Item const &armour, Item const &left_ring, Item const &right_ring) : + name_("Player: "s + weapon.name_ + ", "s + armour.name_ + ", "s + left_ring.name_ + ", " + + right_ring.name_), + hp_(100), + cost_(weapon.cost_ + armour.cost_ + left_ring.cost_ + right_ring.cost_), + damage_(weapon.damage_ + armour.damage_ + left_ring.damage_ + right_ring.damage_), + armour_(weapon.armour_ + armour.armour_ + left_ring.armour_ + right_ring.armour_) {} + + unsigned get_attacked(Person const &attacker) { + auto damage{(attacker.damage_ > armour_) ? attacker.damage_ - armour_ : 1}; + hp_ = (hp_ > damage) ? hp_ - damage : 0; + return hp_; + } + + std::string name_; + unsigned long hp_; + unsigned long cost_; + unsigned long damage_; + unsigned long armour_; +}; + +unsigned long get_value(std::string const &begin) { + std::string line; + if (!std::getline(std::cin, line)) { + std::cerr << "Missing line in input\n"; + std::exit(1); + } + if (line.substr(0, begin.length()) != begin) { + std::cerr << "Line doesn't begin with: " << begin << '\n'; + std::exit(1); + } + return std::stoul(line.substr(begin.length())); +} + +bool me_beats_enemy(Person me, Person enemy) { + while (true) { + enemy.get_attacked(me); + if (enemy.hp_ == 0) { return true; } + me.get_attacked(enemy); + if (me.hp_ == 0) { return false; } + } +} + +int main() { + const std::string hp_begin{"Hit Points: "}; + const std::string d_begin{"Damage: "}; + const std::string a_begin{"Armor: "}; + + const std::vector weapons{ + {"Dagger", 8, 4, 0}, + {"Shortsword", 10, 5, 0}, + {"Warhammer", 25, 6, 0}, + {"Longsword", 40, 7, 0}, + {"Greataxe", 74, 8, 0}, + }; + + const std::vector armours{ + {"Nothing", 0, 0, 0}, + {"Leather", 13, 0, 1}, + {"Chainmail", 31, 0, 2}, + {"Splintmail", 53, 0, 3}, + {"Bandedmail", 75, 0, 4}, + {"Platedmail", 102, 0, 5}, + }; + + const std::vector rings{ + {"Empty", 0, 0, 0}, + {"Damage +1", 25, 1, 0}, + {"Damage +2", 50, 2, 0}, + {"Damage +3", 100, 3, 0}, + {"Defense +1", 20, 0, 1}, + {"Defense +2", 40, 0, 2}, + {"Defense +3", 80, 0, 3}, + }; + + + auto enemy_hp{get_value(hp_begin)}; + auto enemy_damage{get_value(d_begin)}; + auto enemy_armour{get_value(a_begin)}; + + Person best_result{0, 0, 0}; + Person worst_result{0, 0, 0}; + worst_result.cost_ = 0; + Person enemy{enemy_hp, enemy_damage, enemy_armour}; + + for (auto const &weapon: weapons) { + for (auto const &armour: armours) { + for (auto const &left_ring: rings) { + for (auto const &right_ring: rings) { + if (left_ring == right_ring) continue; + Person me{weapon, armour, left_ring, right_ring}; + if (me_beats_enemy(me, enemy)) { + if (me.cost_ < best_result.cost_) { + best_result = me; + } + } + else if (me.cost_ > worst_result.cost_) { + worst_result = me; + } + } + } + } + } + + std::cout << "Best person: " << best_result.name_ << " at cost of " << best_result.cost_ << '\n'; + std::cout << "Worst person: " << worst_result.name_ << " at cost of " << worst_result.cost_ << '\n'; +} \ No newline at end of file diff --git a/2015/puzzle-22-01.cc b/2015/puzzle-22-01.cc new file mode 100644 index 0000000..27cb609 --- /dev/null +++ b/2015/puzzle-22-01.cc @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include + +using namespace std::string_literals; + +struct State { + State(unsigned long enemy_hp, unsigned long enemy_damage) : enemy_hp_(enemy_hp), enemy_damage_(enemy_damage) {} + + unsigned long my_hp_{50}; + unsigned long my_mana_{500}; + unsigned long my_armour_{0}; + unsigned long enemy_hp_; + unsigned long enemy_damage_; + unsigned long shield_counter_{0}; + unsigned long poison_counter_{0}; + unsigned long recharge_counter_{0}; + unsigned long cost_{0}; + + bool operator==(State const &rhs) const { + return my_hp_ == rhs.my_hp_ && my_mana_ == rhs.my_mana_ && my_armour_ == rhs.my_armour_ && + enemy_hp_ == rhs.enemy_hp_ && enemy_damage_ == rhs.enemy_damage_ && + shield_counter_ == rhs.shield_counter_ && poison_counter_ == rhs.poison_counter_ && + recharge_counter_ == rhs.recharge_counter_; + } + + bool apply_effects() { + if (shield_counter_ > 0) { + if (--shield_counter_ == 0) { + my_armour_ -= 7; + } + } + if (poison_counter_ > 0) { + hit_enemy(3); + --poison_counter_; + } + if (recharge_counter_ > 0) { + my_mana_ += 101; + --recharge_counter_; + } + return enemy_hp_ == 0; + } + + void hit_enemy(unsigned long damage) { + enemy_hp_ = (damage > enemy_hp_) ? 0 : enemy_hp_ - damage; + } + + void hit_me() { + auto amt{my_armour_ >= enemy_damage_ ? 1 : enemy_damage_ - my_armour_}; + my_hp_ = (amt > my_hp_) ? 0 : my_hp_ - amt; + } + + void spend_mana(unsigned long amt) { + assert(my_mana_ >= amt); + my_mana_ -= amt; + cost_ += amt; + } + + void enemy_turn() { + if (!apply_effects()) { + hit_me(); + } + } + + [[nodiscard]] State magic_missile() const { + State next{*this}; + next.spend_mana(53); + next.hit_enemy(4); + next.enemy_turn(); + return next; + } + + [[nodiscard]] State drain() const { + State next{*this}; + next.spend_mana(73); + next.hit_enemy(2); + next.my_hp_ += 2; + next.enemy_turn(); + return next; + } + + [[nodiscard]] State shield() const { + State next{*this}; + assert(shield_counter_ == 0); + next.spend_mana(113); + next.my_armour_ += 7; + next.shield_counter_ = 6; + next.enemy_turn(); + return next; + } + + [[nodiscard]] State poison() const { + State next{*this}; + assert(poison_counter_ == 0); + next.spend_mana(173); + next.poison_counter_ = 6; + next.enemy_turn(); + return next; + } + + [[nodiscard]] State recharge() const { + State next{*this}; + assert(recharge_counter_ == 0); + next.spend_mana(229); + next.recharge_counter_ = 5; + next.enemy_turn(); + return next; + } +}; + +unsigned long get_value(std::string const &begin) { + std::string line; + if (!std::getline(std::cin, line)) { + std::cerr << "Missing line in input\n"; + std::exit(1); + } + if (line.substr(0, begin.length()) != begin) { + std::cerr << "Line doesn't begin with: " << begin << '\n'; + std::exit(1); + } + return std::stoul(line.substr(begin.length())); +} + +/** \brief Add \a state to \a states. + * + * @param states Vector of states + * @param state State to add. + * + * If there is already an element in \a states that is the same as \a state we don't add a new state, instead we just + * update the cost to the minimum. + */ +void add_state(std::vector &states, State const &state) { + auto it = std::find(states.begin(), states.end(), state); + if (it == states.end()) { + states.push_back(state); + } else if (it->cost_ > state.cost_) { + it->cost_ = state.cost_; + } +} + +int main() { + const std::string hp_begin{"Hit Points: "}; + const std::string d_begin{"Damage: "}; + + auto enemy_hp{get_value(hp_begin)}; + auto enemy_damage{get_value(d_begin)}; + + /* States - list of states we've generated. */ + std::vector states; + states.emplace_back(enemy_hp, enemy_damage); + + /* Cost of the cheapest winning state so far. */ + auto best_cost{std::numeric_limits::max()}; + + while (!states.empty()) { + /* Get the lowest cost element in the list of states and process that. */ + auto it = std::min_element(states.begin(), states.end(), + [](State const &lhs, State const &rhs) { return lhs.cost_ < rhs.cost_; }); + State candidate{*it}; + states.erase(it); + if (candidate.cost_ >= best_cost) { + /* Because we've searched for the minimum element above we know that all future candidates are going to + * cost more than the current best cost for winning - so just stop here. */ + break; + } + + /* Someone has died so this is a winning state - deal with it. */ + if (candidate.my_hp_ == 0 || candidate.enemy_hp_ == 0) { + if (candidate.enemy_hp_ == 0 && candidate.cost_ < best_cost) { + best_cost = candidate.cost_; + } + continue; + } + +#if 0 + /* Part two's addition. */ + if (--candidate.my_hp_ == 0) { + continue; + } +#endif + + /* Apply effects at start of our turn. If someone dies push this state back on the queue. */ + if (candidate.apply_effects()) { + states.push_back(candidate); + } + + /* Handle each of our options (followed by the enemy's turn). Making sure that when we add the state we + * don't duplicate ourselves. + */ + if (candidate.my_mana_ >= 53) { + add_state(states, candidate.magic_missile()); + } + + if (candidate.my_mana_ >= 73) { + add_state(states, candidate.drain()); + } + + if (candidate.my_mana_ >= 113 && candidate.shield_counter_ == 0) { + add_state(states, candidate.shield()); + } + + if (candidate.my_mana_ >= 173 && candidate.poison_counter_ == 0) { + add_state(states, candidate.poison()); + } + + if (candidate.my_mana_ >= 229 && candidate.recharge_counter_ == 0) { + add_state(states, candidate.recharge()); + } + } + + std::cout << "Best cost " << best_cost << "\n"; + return 1; +} \ No newline at end of file diff --git a/2015/puzzle-22-02.cc b/2015/puzzle-22-02.cc new file mode 100644 index 0000000..7ceb59f --- /dev/null +++ b/2015/puzzle-22-02.cc @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include + +using namespace std::string_literals; + +struct State { + State(unsigned long enemy_hp, unsigned long enemy_damage) : enemy_hp_(enemy_hp), enemy_damage_(enemy_damage) {} + + unsigned long my_hp_{50}; + unsigned long my_mana_{500}; + unsigned long my_armour_{0}; + unsigned long enemy_hp_; + unsigned long enemy_damage_; + unsigned long shield_counter_{0}; + unsigned long poison_counter_{0}; + unsigned long recharge_counter_{0}; + unsigned long cost_{0}; + + bool operator==(State const &rhs) const { + return my_hp_ == rhs.my_hp_ && my_mana_ == rhs.my_mana_ && my_armour_ == rhs.my_armour_ && + enemy_hp_ == rhs.enemy_hp_ && enemy_damage_ == rhs.enemy_damage_ && + shield_counter_ == rhs.shield_counter_ && poison_counter_ == rhs.poison_counter_ && + recharge_counter_ == rhs.recharge_counter_; + } + + bool apply_effects() { + if (shield_counter_ > 0) { + if (--shield_counter_ == 0) { + my_armour_ -= 7; + } + } + if (poison_counter_ > 0) { + hit_enemy(3); + --poison_counter_; + } + if (recharge_counter_ > 0) { + my_mana_ += 101; + --recharge_counter_; + } + return enemy_hp_ == 0; + } + + void hit_enemy(unsigned long damage) { + enemy_hp_ = (damage > enemy_hp_) ? 0 : enemy_hp_ - damage; + } + + void hit_me() { + auto amt{my_armour_ >= enemy_damage_ ? 1 : enemy_damage_ - my_armour_}; + my_hp_ = (amt > my_hp_) ? 0 : my_hp_ - amt; + } + + void spend_mana(unsigned long amt) { + assert(my_mana_ >= amt); + my_mana_ -= amt; + cost_ += amt; + } + + void enemy_turn() { + if (!apply_effects()) { + hit_me(); + } + } + + [[nodiscard]] State magic_missile() const { + State next{*this}; + next.spend_mana(53); + next.hit_enemy(4); + next.enemy_turn(); + return next; + } + + [[nodiscard]] State drain() const { + State next{*this}; + next.spend_mana(73); + next.hit_enemy(2); + next.my_hp_ += 2; + next.enemy_turn(); + return next; + } + + [[nodiscard]] State shield() const { + State next{*this}; + assert(shield_counter_ == 0); + next.spend_mana(113); + next.my_armour_ += 7; + next.shield_counter_ = 6; + next.enemy_turn(); + return next; + } + + [[nodiscard]] State poison() const { + State next{*this}; + assert(poison_counter_ == 0); + next.spend_mana(173); + next.poison_counter_ = 6; + next.enemy_turn(); + return next; + } + + [[nodiscard]] State recharge() const { + State next{*this}; + assert(recharge_counter_ == 0); + next.spend_mana(229); + next.recharge_counter_ = 5; + next.enemy_turn(); + return next; + } +}; + +unsigned long get_value(std::string const &begin) { + std::string line; + if (!std::getline(std::cin, line)) { + std::cerr << "Missing line in input\n"; + std::exit(1); + } + if (line.substr(0, begin.length()) != begin) { + std::cerr << "Line doesn't begin with: " << begin << '\n'; + std::exit(1); + } + return std::stoul(line.substr(begin.length())); +} + +/** \brief Add \a state to \a states. + * + * @param states Vector of states + * @param state State to add. + * + * If there is already an element in \a states that is the same as \a state we don't add a new state, instead we just + * update the cost to the minimum. + */ +void add_state(std::vector &states, State const &state) { + auto it = std::find(states.begin(), states.end(), state); + if (it == states.end()) { + states.push_back(state); + } else if (it->cost_ > state.cost_) { + it->cost_ = state.cost_; + } +} + +int main() { + const std::string hp_begin{"Hit Points: "}; + const std::string d_begin{"Damage: "}; + + auto enemy_hp{get_value(hp_begin)}; + auto enemy_damage{get_value(d_begin)}; + + /* States - list of states we've generated. */ + std::vector states; + states.emplace_back(enemy_hp, enemy_damage); + + /* Cost of the cheapest winning state so far. */ + auto best_cost{std::numeric_limits::max()}; + + while (!states.empty()) { + /* Get the lowest cost element in the list of states and process that. */ + auto it = std::min_element(states.begin(), states.end(), + [](State const &lhs, State const &rhs) { return lhs.cost_ < rhs.cost_; }); + State candidate{*it}; + states.erase(it); + if (candidate.cost_ >= best_cost) { + /* Because we've searched for the minimum element above we know that all future candidates are going to + * cost more than the current best cost for winning - so just stop here. */ + break; + } + + /* Someone has died so this is a winning state - deal with it. */ + if (candidate.my_hp_ == 0 || candidate.enemy_hp_ == 0) { + if (candidate.enemy_hp_ == 0 && candidate.cost_ < best_cost) { + best_cost = candidate.cost_; + } + continue; + } + + /* Part two's addition - we lose health at the start of our turn. */ + if (--candidate.my_hp_ == 0) { + continue; + } + + /* Apply effects at start of our turn. If someone dies push this state back on the queue. */ + if (candidate.apply_effects()) { + states.push_back(candidate); + } + + /* Handle each of our options (followed by the enemy's turn). Making sure that when we add the state we + * don't duplicate ourselves. + */ + if (candidate.my_mana_ >= 53) { + add_state(states, candidate.magic_missile()); + } + + if (candidate.my_mana_ >= 73) { + add_state(states, candidate.drain()); + } + + if (candidate.my_mana_ >= 113 && candidate.shield_counter_ == 0) { + add_state(states, candidate.shield()); + } + + if (candidate.my_mana_ >= 173 && candidate.poison_counter_ == 0) { + add_state(states, candidate.poison()); + } + + if (candidate.my_mana_ >= 229 && candidate.recharge_counter_ == 0) { + add_state(states, candidate.recharge()); + } + } + + std::cout << "Best cost " << best_cost << "\n"; + return 1; +} \ No newline at end of file diff --git a/2015/puzzle-23-01.cc b/2015/puzzle-23-01.cc new file mode 100644 index 0000000..21a1d04 --- /dev/null +++ b/2015/puzzle-23-01.cc @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include + +enum class Op { + hlf, tpl, inc, jmp, jie, jio +}; +enum class Reg { + a, b +}; + +struct Instr { + static Op get_opcode(std::string const &str) { + static std::map ops{ + {"hlf ", Op::hlf}, + {"tpl ", Op::tpl}, + {"inc ", Op::inc}, + {"jmp ", Op::jmp}, + {"jie ", Op::jie}, + {"jio ", Op::jio} + }; + auto it = ops.find(str.substr(0, 4)); + assert(it != ops.end()); + return it->second; + } + + static Reg get_register(std::string const &str) { + assert(str.size() >= 1); + if (str[0] == 'a') { return Reg::a; } + else if (str[0] == 'b') { return Reg::b; } + else { abort(); } + } + + static long get_offset(std::string const &str) { + return std::stol(str); + } + + explicit Instr(std::string const &str) { + op_ = get_opcode(str); + switch (op_) { + case Op::hlf: + case Op::tpl: + case Op::inc: + reg_ = get_register(str.substr(4)); + pc_add_ = 1; + assert(str.length() == 5); + break; + case Op::jmp: + reg_ = Reg::a; + pc_add_ = get_offset(str.substr(4)); + break; + case Op::jie: + case Op::jio: + reg_ = get_register(str.substr(4)); + assert(str.at(5) == ','); + assert(str.at(6) == ' '); + pc_add_ = get_offset(str.substr(7)); + break; + default: + abort(); + } + } + + Op op_; + Reg reg_; + long pc_add_; +}; + +struct State { + unsigned long a_{0}; + unsigned long b_{0}; + unsigned long pc_{0}; + + unsigned long& reg(Reg r) { + switch (r) { + case Reg::a: return a_; + case Reg::b: return b_; + default: abort(); + } + } + + void execute(Instr const &instruction) { + switch (instruction.op_) { + case Op::hlf: + reg(instruction.reg_) /= 2; + pc_ += instruction.pc_add_; + break; + case Op::tpl: + reg(instruction.reg_) *= 3; + pc_ += instruction.pc_add_; + break; + case Op::inc: + reg(instruction.reg_) += 1; + pc_ += instruction.pc_add_; + break; + case Op::jmp: + pc_ += instruction.pc_add_; + break; + case Op::jie: + if (reg(instruction.reg_) % 2 == 0) pc_ += instruction.pc_add_; + else pc_ += 1; + break; + case Op::jio: + if (reg(instruction.reg_) == 1) pc_ += instruction.pc_add_; + else pc_ += 1; + break; + default: + abort(); + } + } +}; + +int main() { + std::vector instructions; + std::string line; + while (std::getline(std::cin, line)) { + instructions.emplace_back(line); + } + + State state; + while (state.pc_ < instructions.size()) { + state.execute(instructions[state.pc_]); + } + std::cout << "a = " << state.a_ << "\nb = " << state.b_ << '\n'; +} \ No newline at end of file diff --git a/2015/puzzle-23-02.cc b/2015/puzzle-23-02.cc new file mode 100644 index 0000000..cc821fe --- /dev/null +++ b/2015/puzzle-23-02.cc @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include + +enum class Op { + hlf, tpl, inc, jmp, jie, jio +}; +enum class Reg { + a, b +}; + +struct Instr { + static Op get_opcode(std::string const &str) { + static std::map ops{ + {"hlf ", Op::hlf}, + {"tpl ", Op::tpl}, + {"inc ", Op::inc}, + {"jmp ", Op::jmp}, + {"jie ", Op::jie}, + {"jio ", Op::jio} + }; + auto it = ops.find(str.substr(0, 4)); + assert(it != ops.end()); + return it->second; + } + + static Reg get_register(std::string const &str) { + assert(str.size() >= 1); + if (str[0] == 'a') { return Reg::a; } + else if (str[0] == 'b') { return Reg::b; } + else { abort(); } + } + + static long get_offset(std::string const &str) { + return std::stol(str); + } + + explicit Instr(std::string const &str) { + op_ = get_opcode(str); + switch (op_) { + case Op::hlf: + case Op::tpl: + case Op::inc: + reg_ = get_register(str.substr(4)); + pc_add_ = 1; + assert(str.length() == 5); + break; + case Op::jmp: + reg_ = Reg::a; + pc_add_ = get_offset(str.substr(4)); + break; + case Op::jie: + case Op::jio: + reg_ = get_register(str.substr(4)); + assert(str.at(5) == ','); + assert(str.at(6) == ' '); + pc_add_ = get_offset(str.substr(7)); + break; + default: + abort(); + } + } + + Op op_; + Reg reg_; + long pc_add_; +}; + +struct State { + unsigned long a_{1}; + unsigned long b_{0}; + unsigned long pc_{0}; + + unsigned long& reg(Reg r) { + switch (r) { + case Reg::a: return a_; + case Reg::b: return b_; + default: abort(); + } + } + + void execute(Instr const &instruction) { + switch (instruction.op_) { + case Op::hlf: + reg(instruction.reg_) /= 2; + pc_ += instruction.pc_add_; + break; + case Op::tpl: + reg(instruction.reg_) *= 3; + pc_ += instruction.pc_add_; + break; + case Op::inc: + reg(instruction.reg_) += 1; + pc_ += instruction.pc_add_; + break; + case Op::jmp: + pc_ += instruction.pc_add_; + break; + case Op::jie: + if (reg(instruction.reg_) % 2 == 0) pc_ += instruction.pc_add_; + else pc_ += 1; + break; + case Op::jio: + if (reg(instruction.reg_) == 1) pc_ += instruction.pc_add_; + else pc_ += 1; + break; + default: + abort(); + } + } +}; + +int main() { + std::vector instructions; + std::string line; + while (std::getline(std::cin, line)) { + instructions.emplace_back(line); + } + + State state; + while (state.pc_ < instructions.size()) { + state.execute(instructions[state.pc_]); + } + std::cout << "a = " << state.a_ << "\nb = " << state.b_ << '\n'; +} \ No newline at end of file diff --git a/2015/puzzle-24-01.cc b/2015/puzzle-24-01.cc new file mode 100644 index 0000000..ad6fbb6 --- /dev/null +++ b/2015/puzzle-24-01.cc @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct State { + std::set weights_; + + explicit State(unsigned long w) { weights_.insert(w); } + + std::size_t cost() const { return weights_.size(); } + + unsigned long qe() const { + return std::accumulate(weights_.begin(), weights_.end(), 1UL, + [](unsigned long l, unsigned long w) { return l * w; }); + } + + unsigned long weight() const { + return std::accumulate(weights_.begin(), weights_.end(), 0UL, + [](unsigned long l, unsigned long w) { return l + w; }); + } + + bool operator==(State const &rhs) const { return weights_ == rhs.weights_; } + + bool operator<(State const &rhs) const { + if (cost() < rhs.cost()) { return true; } + if (cost() == rhs.cost()) { return qe() < rhs.qe(); } + return false; + } +}; + +int main() { + std::vector weights; + std::string line; + while (std::getline(std::cin, line)) { + weights.emplace_back(std::stoul(line)); + } + + std::set states; + unsigned long target_weight; + for (auto w: weights) { + states.insert(State{w}); + target_weight += w; + } + target_weight /= 3; + + while (!states.empty()) { + auto it = states.begin(); + State current(*it); + states.erase(it); + + if (current.weight() == target_weight) { + std::cout << "Cost " << current.cost() << ", qe " << current.qe() << '\n'; + return 0; + } + if (current.weight() > target_weight) { + continue; + } + + for (auto w: weights) { + if (current.weights_.find(w) == current.weights_.end()) { + auto next{current}; + next.weights_.insert(w); + if (states.find(next) == states.end()) { + states.insert(next); + } + } + } + } + + std::cout << "It went wrong somewhere!\n"; + return 1; +} \ No newline at end of file diff --git a/2015/puzzle-24-02.cc b/2015/puzzle-24-02.cc new file mode 100644 index 0000000..86248ff --- /dev/null +++ b/2015/puzzle-24-02.cc @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct State { + std::set weights_; + + explicit State(unsigned long w) { weights_.insert(w); } + + std::size_t cost() const { return weights_.size(); } + + unsigned long qe() const { + return std::accumulate(weights_.begin(), weights_.end(), 1UL, + [](unsigned long l, unsigned long w) { return l * w; }); + } + + unsigned long weight() const { + return std::accumulate(weights_.begin(), weights_.end(), 0UL, + [](unsigned long l, unsigned long w) { return l + w; }); + } + + bool operator==(State const &rhs) const { return weights_ == rhs.weights_; } + + bool operator<(State const &rhs) const { + if (cost() < rhs.cost()) { return true; } + if (cost() == rhs.cost()) { return qe() < rhs.qe(); } + return false; + } +}; + +int main() { + std::vector weights; + std::string line; + while (std::getline(std::cin, line)) { + weights.emplace_back(std::stoul(line)); + } + + std::set states; + unsigned long target_weight; + for (auto w: weights) { + states.insert(State{w}); + target_weight += w; + } + target_weight /= 4; + + while (!states.empty()) { + auto it = states.begin(); + State current(*it); + states.erase(it); + + if (current.weight() == target_weight) { + std::cout << "Cost " << current.cost() << ", qe " << current.qe() << '\n'; + return 0; + } + if (current.weight() > target_weight) { + continue; + } + + for (auto w: weights) { + if (current.weights_.find(w) == current.weights_.end()) { + auto next{current}; + next.weights_.insert(w); + if (states.find(next) == states.end()) { + states.insert(next); + } + } + } + } + + std::cout << "It went wrong somewhere!\n"; + return 1; +} \ No newline at end of file diff --git a/2015/puzzle-25-01.cc b/2015/puzzle-25-01.cc new file mode 100644 index 0000000..08d980a --- /dev/null +++ b/2015/puzzle-25-01.cc @@ -0,0 +1,49 @@ +#include +#include +#include + +std::uint64_t lcg(std::uint64_t val) +{ + return (val * 252533) % 33554393; +} + +int main() +{ + std::string line; + std::getline(std::cin, line); + + using namespace std::string_literals; + auto line_begin{"To continue, please consult the code grid in the manual. Enter the code at row "s}; + auto line_middle{", column "s}; + auto line_end{"."s}; + + assert(line.substr(0, line_begin.size()) == line_begin); + line = line.substr(line_begin.size()); + std::size_t idx = 0; + unsigned long target_row = std::stoul(line, &idx); + line = line.substr(idx); + assert(line.substr(0, line_middle.size()) == line_middle); + line = line.substr(line_middle.size()); + unsigned long target_column = std::stoul(line, &idx); + line = line.substr(idx); + assert(line == line_end); + + unsigned long row{1}; + unsigned long column{1}; + std::uint64_t num{20151125}; + while (row != target_row || column != target_column) { + if (row == 1) { + row = column + 1; + column = 1; + } + else { + --row; + ++column; + } + num = lcg(num); + } + + std::cout << "Target row " << target_row << " column " << target_column << '\n'; + std::cout << "Row " << row << " column " << column << " num " << num << '\n'; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-01-01.cc b/2020/puzzle-01-01.cc new file mode 100644 index 0000000..0fbba19 --- /dev/null +++ b/2020/puzzle-01-01.cc @@ -0,0 +1,18 @@ +#include +#include +#include + +int main(int argc, char **argv) { + std::vector vals; + for (std::string line; std::getline(std::cin, line);) { + int val = std::stoi(line, nullptr, 10); + for (auto v : vals) { + if (v + val == 2020) { + std::cout << v << " * " << val << " = " << v * val << "\n"; + } + } + vals.push_back(val); + } + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-01-02.cc b/2020/puzzle-01-02.cc new file mode 100644 index 0000000..2698640 --- /dev/null +++ b/2020/puzzle-01-02.cc @@ -0,0 +1,24 @@ +#include +#include +#include + +int main(int argc, char **argv) { + std::vector vals; + for (std::string line; std::getline(std::cin, line);) { + int val = std::stoi(line, nullptr, 10); + auto size = vals.size(); + for (std::size_t i = 0; i < size; ++i) { + int64_t v1 = vals[i]; + for (std::size_t j = 0; j < i; ++j) { + int64_t v2 = vals[j]; + if (v1 + v2 + val == 2020) { + std::cout << v1 << " * " << v2 << " * " << val << " = " + << v1 * v2 * std::int64_t(val) << "\n"; + } + } + } + vals.push_back(val); + } + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-02-01.cc b/2020/puzzle-02-01.cc new file mode 100644 index 0000000..4a15b28 --- /dev/null +++ b/2020/puzzle-02-01.cc @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +struct PasswordChecker { + PasswordChecker(std::string const &s) { + std::size_t pos = 0; + min_ = std::stoul(s, &pos, 10); + assert(s[pos] == '-'); + std::string s2 = s.substr(pos + 1); + max_ = std::stoul(s2, &pos, 10); + assert(s2[pos] == ' '); + c_ = s2[pos + 1]; + assert(s2[pos + 2] == ':'); + assert(s2[pos + 3] == ' '); + password_ = s2.substr(pos + 4); + } + + bool is_valid() const { + std::string::size_type count = 0; + for (auto c : password_) { + if (c == c_) { + ++count; + } + } + + return (count >= min_) && (count <= max_); + } + + std::string::size_type min_; + std::string::size_type max_; + std::string::value_type c_; + std::string password_; +}; + +int main(int argc, char **argv) { + unsigned count = 0; + for (std::string line; std::getline(std::cin, line);) { + PasswordChecker check(line); + if (check.is_valid()) { + ++count; + } else { + std::cout << "IN"; + } + std::cout << "VALID: " << line << "\n"; + } + + std::cout << "Number of valid: " << count << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-02-02.cc b/2020/puzzle-02-02.cc new file mode 100644 index 0000000..79d8416 --- /dev/null +++ b/2020/puzzle-02-02.cc @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +struct PasswordChecker { + PasswordChecker(std::string const &s) { + std::size_t pos = 0; + pos1_ = std::stoul(s, &pos, 10); + assert(s[pos] == '-'); + std::string s2 = s.substr(pos + 1); + pos2_ = std::stoul(s2, &pos, 10); + assert(s2[pos] == ' '); + c_ = s2[pos + 1]; + assert(s2[pos + 2] == ':'); + assert(s2[pos + 3] == ' '); + password_ = s2.substr(pos + 4); + } + + bool is_valid() const { + std::string::size_type count = 0; + if (password_.at(pos1_ - 1) == c_) { + ++count; + } + if (password_.at(pos2_ - 1) == c_) { + ++count; + } + return count == 1; + } + + std::string::size_type pos1_; // 1 based + std::string::size_type pos2_; // 1 based + std::string::value_type c_; + std::string password_; +}; + +int main(int argc, char **argv) { + unsigned count = 0; + for (std::string line; std::getline(std::cin, line);) { + PasswordChecker check(line); + if (check.is_valid()) { + ++count; + } else { + std::cout << "IN"; + } + std::cout << "VALID: " << line << "\n"; + } + + std::cout << "Number of valid: " << count << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-03-01.cc b/2020/puzzle-03-01.cc new file mode 100644 index 0000000..36b02cd --- /dev/null +++ b/2020/puzzle-03-01.cc @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +std::string rotate(std::string const &s, std::string::size_type amount) { + auto length = s.length(); + return (s + s).substr(amount % length, length); +} + +int main(int argc, char **argv) { + unsigned count = 0; + std::string::size_type rotation = 0; + for (std::string line; std::getline(std::cin, line);) { + auto rotated = rotate(line, rotation); + rotation += 3; + count += (rotated[0] == '#'); + } + + std::cout << "Number of trees: " << count << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-03-02.cc b/2020/puzzle-03-02.cc new file mode 100644 index 0000000..a3e2148 --- /dev/null +++ b/2020/puzzle-03-02.cc @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +std::string rotate(std::string const &s, std::string::size_type amount) { + auto length = s.length(); + return (s + s).substr(amount % length, length); +} + +int main(int argc, char **argv) { + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + int right = atoi(argv[1]); + int down = atoi(argv[2]); + if (right <= 0) { + std::cerr << "Right is less than 1\n"; + return 1; + } + if (down <= 0) { + std::cerr << "Down is less than 1\n"; + return 1; + } + + unsigned count = 0; + int row = -1; + std::string::size_type rotation = 0; + for (std::string line; std::getline(std::cin, line);) { + ++row; + if ((row % down) != 0) { + continue; + } + auto rotated = rotate(line, rotation); + rotation += right; + count += (rotated[0] == '#'); + } + + std::cout << count << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-04-01.cc b/2020/puzzle-04-01.cc new file mode 100644 index 0000000..3ac1688 --- /dev/null +++ b/2020/puzzle-04-01.cc @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +using StringSet = std::set; + +namespace { +StringSet parse_line(std::string const &str) { + StringSet result; + std::string::size_type pos = 0; + + while (pos != std::string::npos) { + while (pos < str.length() && str.at(pos) == ' ') { + ++pos; + } + auto end_pos = str.find(' ', pos); + auto colon_pos = str.find(':', pos); + assert(colon_pos != std::string::npos); + assert(colon_pos < end_pos); + auto len = colon_pos - pos; + result.insert(str.substr(pos, len)); + pos = end_pos; + } + return result; +} + +const StringSet mandatory{"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"}; +const StringSet optional{"cid"}; + +bool is_valid_passport(StringSet const &mandatory_found) { + return mandatory_found.size() == mandatory.size(); +} +} // namespace + +int main(int argc, char **argv) { + + StringSet mandatory_found; + StringSet optional_found; + unsigned valid = 0; + for (std::string line; std::getline(std::cin, line);) { + if (!line.empty()) { + auto keys = parse_line(line); + for (auto const &key : keys) { + std::cout << "Key " << key << " is "; + if (mandatory.find(key) != mandatory.end()) { + auto [it, success] = mandatory_found.insert(key); + std::cout << "mandatory"; + if (!success) { + std::cout << " (duplicate)"; + } + } else if (optional.find(key) != optional.end()) { + auto [it, success] = optional_found.insert(key); + std::cout << "optional"; + if (!success) { + std::cout << " (duplicate)"; + } + } else { + std::cout << "unexpected"; + } + std::cout << "\n"; + } + } else { + valid += is_valid_passport(mandatory_found); + mandatory_found.clear(); + optional_found.clear(); + } + } + valid += is_valid_passport(mandatory_found); + + std::cout << valid << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-04-02.cc b/2020/puzzle-04-02.cc new file mode 100644 index 0000000..08cad89 --- /dev/null +++ b/2020/puzzle-04-02.cc @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include + +// Various types that are useful +using StringSet = std::set; +using StringMap = std::map; +using ValidateFn = std::function; +using ValidateMap = std::map; + +namespace { +/** \brief Parse a string into a map of key/value pairs. + * \param str String to parse + * \return Map + * + * \a str is expected to look like a space separated list of pairs where each + * pair is of the form 'key:value'. + */ +StringMap parse_line(std::string const &str) { + StringMap result; + std::string::size_type pos = 0; + + while (pos != std::string::npos) { + while (pos < str.length() && str.at(pos) == ' ') { + ++pos; + } + auto end_pos = str.find(' ', pos); + auto colon_pos = str.find(':', pos); + assert(colon_pos != std::string::npos); + assert(colon_pos < end_pos); + auto key_len = colon_pos - pos; + auto value_len = (end_pos == std::string::npos) + ? std::string::npos + : (end_pos - (colon_pos + 1)); + auto key = str.substr(pos, key_len); + auto value = str.substr(colon_pos + 1, value_len); + result.insert({key, value}); + pos = end_pos; + } + + return result; +} + +/** \brief Validate a year + * \param s String to validate + * \param min Minimum year to accept + * \param max Maximum year to accept + * \return True if \a s is in range [min, max] + */ +bool validate_year(std::string const &s, unsigned min, unsigned max) { + if (s.length() != 4) { + return false; + } + std::size_t pos = 0; + auto yr = std::stoul(s, &pos, 10); + if (pos != s.length()) { + return false; + } + return yr >= min && yr <= max; +} + +/// Validate byr field +bool validate_byr(std::string const &s) { return validate_year(s, 1920, 2002); } + +/// Validate iyr field +bool validate_iyr(std::string const &s) { return validate_year(s, 2010, 2020); } + +/// Validate eyr field +bool validate_eyr(std::string const &s) { return validate_year(s, 2020, 2030); } + +/// Validate hgt field +bool validate_hgt(std::string const &s) { + std::size_t pos = 0; + auto hgt = std::stoul(s, &pos, 10); + if (pos != s.length() - 2) { + return false; + } + if (s[pos] == 'c' && s[pos + 1] == 'm') { + return hgt >= 150 && hgt <= 193; + } else if (s[pos] == 'i' && s[pos + 1] == 'n') { + return hgt >= 59 && hgt <= 76; + } + return false; +} + +/** \brief Validate a string contains valid characters only + * \param s String to check + * \param len Required length of string + * \param cs Valid characters + * \return True iff we pass validation + */ +bool validate_chars(std::string const &s, std::string::size_type len, + std::string const &cs) { + if (s.length() != len) { + return false; + } + for (auto c : s) { + if (cs.find(c) == std::string::npos) { + return false; + } + } + + return true; +} + +/// Validate hcl field +bool validate_hcl(std::string const &s) { + if (s.length() != 7) { + return false; + } + if (s[0] != '#') { + return false; + } + return validate_chars(s.substr(1), 6, "0123456789abcdef"); +} + +/// Validate ecl field +bool validate_ecl(std::string const &s) { + static const StringSet valid = {"amb", "blu", "brn", "gry", + "grn", "hzl", "oth"}; + return valid.find(s) != valid.end(); +} + +/// Validate pid field +bool validate_pid(std::string const &s) { + return validate_chars(s, 9, "0123456789"); +} + +/// Validate cid field +bool validate_cid(std::string const &s) { return true; } + +/// Check if a passport is valid +/// +/// A passport is valid if it contains all mandatory fields, and passes +/// validation on all fields. +bool is_valid_passport(StringMap const &found) { + static const StringSet mandatory{"byr", "iyr", "eyr", "hgt", + "hcl", "ecl", "pid"}; + static const ValidateMap validators{ + {"byr", validate_byr}, {"iyr", validate_iyr}, {"eyr", validate_eyr}, + {"hgt", validate_hgt}, {"hcl", validate_hcl}, {"ecl", validate_ecl}, + {"pid", validate_pid}, {"cid", validate_cid}, + }; + + unsigned mandatory_found = 0; + for (auto const &kv : found) { + if (mandatory.find(kv.first) != mandatory.end()) { + ++mandatory_found; + } + auto validator = validators.find(kv.first); + if (validator == validators.end()) { + std::cout << "Found invalid key: " << kv.first << "\n"; + } else { + auto valid = validator->second(kv.second); + if (!valid) { + std::cout << "Invalid value for key: " << kv.first << ": " << kv.second + << "\n"; + return false; + } + } + } + + return mandatory_found == mandatory.size(); +} +} // namespace + +int main(int argc, char **argv) { + + StringMap found; + unsigned valid = 0; + for (std::string line; std::getline(std::cin, line);) { + if (!line.empty()) { + auto keys = parse_line(line); + found.insert(keys.begin(), keys.end()); + } else { + valid += is_valid_passport(found); + found.clear(); + } + } + valid += is_valid_passport(found); + + std::cout << valid << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-05-01.cc b/2020/puzzle-05-01.cc new file mode 100644 index 0000000..14a32fb --- /dev/null +++ b/2020/puzzle-05-01.cc @@ -0,0 +1,60 @@ +#include +#include +#include + +/** \brief Return the position described by BSP in s. + * \tparam L2 log2 of the max position. + * \tparam LOWER character which means use lower half. + * \tparam UPPER character which means use upper half. + * \param s String to parse, must be L2 characters long + * \return Position described by s. + */ +template +unsigned find_pos(std::string const &s) { + assert(s.length() == L2); + unsigned low = 0; + unsigned high = 1 << L2; + unsigned width = 1 << (L2 - 1); + for (auto c : s) { + assert(high != low); + assert(width != 0); + if (c == LOWER) { + high = low + width; + } else if (c == UPPER) { + low = low + width; + } else { + assert(false); + } + width >>= 1; + } + + assert(low == high - 1); + return low; +} + +template struct Seat { + Seat(std::string const &s) { + assert(s.length() == L2R + L2C); + row_ = find_pos(s.substr(0, L2R)); + col_ = find_pos(s.substr(L2R, L2C)); + std::cout << s << ": row " << row_ << ", column " << col_ << ", seat ID " + << id() << ".\n"; + } + + unsigned id() const noexcept { return row_ * (1 << L2C) + col_; } + + unsigned row_; + unsigned col_; +}; + +int main(int argc, char **argv) { + unsigned max_id = 0; + + for (std::string line; std::getline(std::cin, line);) { + Seat<7, 3> seat(line); + max_id = std::max(max_id, seat.id()); + } + + std::cout << "Max ID: " << max_id << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-05-02.cc b/2020/puzzle-05-02.cc new file mode 100644 index 0000000..5f12954 --- /dev/null +++ b/2020/puzzle-05-02.cc @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +/** \brief Return the position described by BSP in s. + * \tparam L2 log2 of the max position. + * \tparam LOWER character which means use lower half. + * \tparam UPPER character which means use upper half. + * \param s String to parse, must be L2 characters long + * \return Position described by s. + * + * \subsection After the fact commentary: + * + * We don't need to keep a record of high at all so the original solution + * was over enthusiastic. + */ +template +unsigned find_pos(std::string const &s) { + assert(s.length() == L2); + unsigned low = 0; + unsigned width = 1 << (L2 - 1); + + for (auto c : s) { + assert(width != 0); + if (c == UPPER) { + low = low + width; + } else { + assert(c == LOWER); + } + + width >>= 1; + } + + assert(width == 0); + return low; +} + +/** \brief A seat + * \tparam L2R log 2 number of rows + * \tparam L2C log 2 number of columns + */ +template struct Seat { + + /** \brief Construct the seat. + * \param s String representing seat location, must be L2R + L2C chars. + * + * First L2R characters must be 'F' and 'B' representing row, last L2C + * characters must by 'L', and 'R' representing column. + * + * \brief After the fact commentary: + * + * Should have used initialiser lists. + */ + Seat(std::string const &s) + : row_(find_pos(s.substr(0, L2R))), + col_(find_pos(s.substr(L2R, L2C))) { + assert(s.length() == L2R + L2C); + std::cout << s << ": row " << row_ << ", column " << col_ << ", seat ID " + << id() << ".\n"; + } + + /** Get seat ID. */ + unsigned id() const noexcept { return row_ * (1 << L2C) + col_; } + + unsigned row_; ///< Seat row + unsigned col_; ///< Seat column +}; + +/** \brief main + * + * \subsection After the fact commentary: + * + * Could have initialised the array better. + */ +int main(int argc, char **argv) { + constexpr unsigned l2r = 7; ///< Log 2 number of rows + constexpr unsigned l2c = 3; ///< Log 2 number of columns + constexpr unsigned max_id = 1 << (l2r + l2c); ///< Max ID. + std::array id_present = {}; ///< Is the ID present? + + // Read the lines in marking IDs that are present + for (std::string line; std::getline(std::cin, line);) { + Seat seat(line); + assert(seat.id() < max_id); + id_present[seat.id()] = true; + } + + // We have an empty seat if it's ID is not present, but the IDs at +/-1 are. + for (unsigned id = 1; id < id_present.size() - 1; ++id) { + if (!id_present[id] && id_present[id - 1] && id_present[id + 1]) { + std::cout << "Empty seat at: " << id << "\n"; + } + } + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-06-01.cc b/2020/puzzle-06-01.cc new file mode 100644 index 0000000..d4c4ee5 --- /dev/null +++ b/2020/puzzle-06-01.cc @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +// Merge two strings of answers, preserving order and ensuring uniqueness of +// each answer. +std::string merge_answers(std::string const &a, std::string const &b) { + std::string result; + std::string::size_type pos_a = 0; + std::string::size_type pos_b = 0; + while (pos_a < a.length() && pos_b < b.length()) { + if (a[pos_a] < b[pos_b]) { + result += a[pos_a]; + ++pos_a; + } else if (b[pos_b] < a[pos_a]) { + result += b[pos_b]; + ++pos_b; + } else { + assert(a[pos_a] == b[pos_b]); + result += a[pos_a]; + ++pos_a; + ++pos_b; + } + } + result += a.substr(pos_a); + result += b.substr(pos_b); + std::cout << "Merged " << a << " and " << b << " to " << result << "\n"; + return result; +} + +int main(int argc, char **argv) { + + std::string current_answers; + unsigned count = 0; + for (std::string line; std::getline(std::cin, line);) { + if (!line.empty()) { + std::sort(line.begin(), line.end()); + current_answers = merge_answers(current_answers, line); + } else { + std::cout << "Length of " << current_answers << " = " + << current_answers.length() << "\n"; + count += current_answers.length(); + current_answers.clear(); + } + } + std::cout << "Length of " << current_answers << " = " + << current_answers.length() << "\n"; + count += current_answers.length(); + + std::cout << "Total length = " << count << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-06-02.cc b/2020/puzzle-06-02.cc new file mode 100644 index 0000000..0c279be --- /dev/null +++ b/2020/puzzle-06-02.cc @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +using OccuranceMap = std::map; + +struct GroupAnswers { + void add_answers(std::string const &a) { + // Increase group size + ++group_size_; + + // Add answers + for (auto c : a) { + auto it = map_.find(c); + if (it == map_.end()) { + map_.insert({c, 1}); + } else { + ++(it->second); + } + } + } + + // Count the number of answered questions answered by everyone. + unsigned all_answer_count() const noexcept { + unsigned count = 0; + for (auto kv : map_) { + if (kv.second == group_size_) { + ++count; + } + } + + return count; + } + + void clear() { + map_.clear(); + group_size_ = 0; + } + + OccuranceMap map_; + unsigned group_size_ = 0; +}; + +int main(int argc, char **argv) { + GroupAnswers answers; + unsigned count = 0; + for (std::string line; std::getline(std::cin, line);) { + if (!line.empty()) { + answers.add_answers(line); + } else { + count += answers.all_answer_count(); + answers.clear(); + } + } + count += answers.all_answer_count(); + + std::cout << "Total length = " << count << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-07-01.cc b/2020/puzzle-07-01.cc new file mode 100644 index 0000000..f01aae6 --- /dev/null +++ b/2020/puzzle-07-01.cc @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include + +// This code is horrible - my only excuse is I wrote it before breakfast + +using Node = std::string; +using Nodes = std::map; +using Edge = std::pair; +using Weight = unsigned; +using EdgeWeights = std::map; +using NodeQueue = std::list; + +std::ostream &operator<<(std::ostream &os, NodeQueue const &q) { + std::string p = ""; + os << "["; + for (auto const &n : q) { + os << p << n; + p = ", "; + } + os << "]"; + return os; +} + +struct BagGraph { + BagGraph() { nodes_.insert({"", false}); } + + void add_edges(std::string const &s) { + static const auto prefix_re = std::regex("([a-z ]+) bags? contain "); + static const auto suffix_re = std::regex("(\\d)+ ([a-z ]+) bags?[,.]\\s*"); + static const auto empty_str = std::string("no other bags."); + std::smatch prefix_m; + if (!std::regex_search(s, prefix_m, prefix_re)) { + std::cout << "Failed to match: " << s << "\n"; + assert(false); + return; + } + + std::string n2 = prefix_m.str(1); + nodes_.insert({n2, false}); + std::string suffix = prefix_m.suffix(); + while (!suffix.empty()) { + if (suffix == empty_str) { + suffix = ""; + Edge e = std::make_pair("", n2); + edges_.insert({e, 0}); + std::cout << " -> " << n2 << ": 0\n"; + } else { + std::smatch suffix_m; + if (!std::regex_search(suffix, suffix_m, suffix_re)) { + std::cout << "Failed to match: " << suffix << "\n"; + assert(false); + return; + } + Weight w = std::stoul(suffix_m.str(1)); + Node n1 = suffix_m.str(2); + suffix = suffix_m.suffix(); + Edge e = std::make_pair(n1, n2); + nodes_.insert({n1, false}); + edges_.insert({e, w}); + std::cout << n1 << " -> " << n2 << ": " << w << "\n"; + } + } + } + + unsigned num_containers(Node const &n) { + NodeQueue queue; + // Initial population of queue: + populate_queue(queue, n); + std::cout << "Initial queue: " << queue << "\n"; + unsigned visited = 0; + + // Iterate over queue finding how + while (!queue.empty()) { + Node n1 = queue.front(); + std::cout << "Processing: " << n1 << "\n"; + queue.pop_front(); + if (!nodes_[n1]) { + nodes_[n1] = true; + populate_queue(queue, n1); + ++visited; + std::cout << "Updated queue: " << queue << "\n"; + } else { + std::cout << "Already visited\n"; + } + } + + return visited; + } + +private: + void populate_queue(NodeQueue &queue, Node const &n) { + for (auto const &kv : edges_) { + if (kv.first.first == n && !nodes_[kv.first.second]) { + queue.push_back(kv.first.second); + } + } + } + + EdgeWeights edges_; + Nodes nodes_; +}; + +int main(int argc, char **argv) { + BagGraph bg; + for (std::string line; std::getline(std::cin, line);) { + bg.add_edges(line); + } + + Node n = "shiny gold"; + auto visited = bg.num_containers(n); + std::cout << "Number of " << n << " containers " << visited << "\n"; + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-07-02.cc b/2020/puzzle-07-02.cc new file mode 100644 index 0000000..f105b06 --- /dev/null +++ b/2020/puzzle-07-02.cc @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include + +// This code is horrible - my only excuse is I wrote it before breakfast + +using Node = std::string; +using Edge = std::pair; +using Weight = unsigned; +using EdgeWeights = std::map; + +struct BagGraph { + void add_edges(std::string const &s) { + static const auto prefix_re = std::regex("([a-z ]+) bags? contain "); + static const auto suffix_re = std::regex("(\\d)+ ([a-z ]+) bags?[,.]\\s*"); + static const auto empty_str = std::string("no other bags."); + std::smatch prefix_m; + if (!std::regex_search(s, prefix_m, prefix_re)) { + std::cout << "Failed to match: " << s << "\n"; + assert(false); + return; + } + + std::string n1 = prefix_m.str(1); + std::string suffix = prefix_m.suffix(); + while (!suffix.empty()) { + if (suffix == empty_str) { + std::cout << n1 << " -> none: \n"; + suffix.clear(); + } else { + std::smatch suffix_m; + if (!std::regex_search(suffix, suffix_m, suffix_re)) { + std::cout << "Failed to match: " << suffix << "\n"; + assert(false); + return; + } + Weight w = std::stoul(suffix_m.str(1)); + Node n2 = suffix_m.str(2); + suffix = suffix_m.suffix(); + Edge e = std::make_pair(n1, n2); + edges_.insert({e, w}); + std::cout << n1 << " -> " << n2 << ": " << w << "\n"; + } + } + } + + Weight num_contained(Node const &n) { + // Get the number of bags including the node we've been asked for so the + // result is 1 less than the internal result + return num_contained1(n) - 1; + } + +private: + Weight num_contained1(Node const &n) { + Weight w = 0; + bool contains_something = false; + for (auto const &kv : edges_) { + if (kv.first.first == n) { + w += kv.second * num_contained1(kv.first.second); + contains_something = true; + } + } + return w + 1; + } + + EdgeWeights edges_; +}; + +int main(int argc, char **argv) { + BagGraph bg; + for (std::string line; std::getline(std::cin, line);) { + bg.add_edges(line); + } + + Node n = "shiny gold"; + auto contained = bg.num_contained(n); + std::cout << n << " contains " << contained << " bags.\n"; + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-08-01.cc b/2020/puzzle-08-01.cc new file mode 100644 index 0000000..7325511 --- /dev/null +++ b/2020/puzzle-08-01.cc @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include +#include + +enum class Opcode { Acc, Jmp, Nop }; +using Value = int; + +struct Instruction { + Instruction(std::string const &s) + : op_(to_opcode(s.substr(0, 3))), v_(to_value(s.substr(4))) {} + + Opcode opcode() const noexcept { return op_; } + Value value() const noexcept { return v_; } + +private: + Opcode to_opcode(std::string const &s) { + if (s == "acc") { + return Opcode::Acc; + } else if (s == "jmp") { + return Opcode::Jmp; + } else if (s == "nop") { + return Opcode::Nop; + } else { + assert(false); + return Opcode::Nop; + } + } + + Value to_value(std::string const &s) { + int sign = 0; + int v = 0; + std::size_t pos = 0; + if (s[0] == '+') { + sign = 1; + } else if (s[0] == '-') { + sign = -1; + } else { + assert(false); + } + v = std::stoi(s.substr(1), &pos); + assert(pos == s.length() - 1); + return v * sign; + } + + Opcode op_; + Value v_; +}; + +std::ostream &operator<<(std::ostream &os, Instruction const &i) { + switch (i.opcode()) { + case Opcode::Acc: + os << "acc"; + break; + case Opcode::Jmp: + os << "jmp"; + break; + case Opcode::Nop: + os << "nop"; + break; + default: + os << "UNK"; + break; + } + os << " "; + if (i.value() >= 0) { + os << "+"; + } + os << i.value(); + return os; +} +using Instructions = std::vector; + +struct VM { + VM() : pc_(0), acc_(0) {} + + void add_instruction(Instruction const &i) { + std::cout << i << "\n"; + instrs_.push_back(i); + } + + void execute() { + std::vector seen(instrs_.size(), false); + while (!seen[pc_]) { + assert(pc_ < instrs_.size()); + seen[pc_] = true; + execute(instrs_[pc_]); + ++pc_; + } + std::cout << "PC seen before: " << pc_ << "\n"; + } + + Value acc() const noexcept { return acc_; } + +private: + void execute(Instruction const &i) { + std::cout << pc_ << ": " << i; + switch (i.opcode()) { + case Opcode::Acc: + std::cout << "; acc = " << acc_ << " + " << i.value(); + acc_ += i.value(); + std::cout << " = " << acc_; + break; + case Opcode::Jmp: + std::cout << "; pc = " << pc_ << " + " << i.value(); + pc_ += i.value() - 1; + std::cout << " = " << pc_ + 1; + break; + case Opcode::Nop: + break; + } + std::cout << "\n"; + } + + Instructions instrs_; + std::size_t pc_; + Value acc_; +}; + +int main(int argc, char **argv) { + VM vm; + for (std::string line; std::getline(std::cin, line);) { + vm.add_instruction(Instruction(line)); + } + + vm.execute(); + std::cout << "Accumulator: " << vm.acc() << "\n"; + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-08-02.cc b/2020/puzzle-08-02.cc new file mode 100644 index 0000000..86aa921 --- /dev/null +++ b/2020/puzzle-08-02.cc @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include + +enum class Opcode { Acc, Jmp, Nop }; +using Value = int; + +struct Instruction { + Instruction(std::string const &s) + : op_(to_opcode(s.substr(0, 3))), v_(to_value(s.substr(4))) {} + + Opcode opcode() const noexcept { return op_; } + Value value() const noexcept { return v_; } + bool flip_jmp() { + switch (op_) { + case Opcode::Acc: + return false; + case Opcode::Jmp: + op_ = Opcode::Nop; + return true; + case Opcode::Nop: + op_ = Opcode::Jmp; + return true; + default: + assert(false); + } + } + +private: + Opcode to_opcode(std::string const &s) { + if (s == "acc") { + return Opcode::Acc; + } else if (s == "jmp") { + return Opcode::Jmp; + } else if (s == "nop") { + return Opcode::Nop; + } else { + assert(false); + return Opcode::Nop; + } + } + + Value to_value(std::string const &s) { + int sign = 0; + int v = 0; + std::size_t pos = 0; + if (s[0] == '+') { + sign = 1; + } else if (s[0] == '-') { + sign = -1; + } else { + assert(false); + } + v = std::stoi(s.substr(1), &pos); + assert(pos == s.length() - 1); + return v * sign; + } + + Opcode op_; + Value v_; +}; + +std::ostream &operator<<(std::ostream &os, Instruction const &i) { + switch (i.opcode()) { + case Opcode::Acc: + os << "acc"; + break; + case Opcode::Jmp: + os << "jmp"; + break; + case Opcode::Nop: + os << "nop"; + break; + default: + os << "UNK"; + break; + } + os << " "; + if (i.value() >= 0) { + os << "+"; + } + os << i.value(); + return os; +} +using Instructions = std::vector; + +struct VM { + VM() : pc_(0), acc_(0) {} + + void add_instruction(Instruction const &i) { + std::cout << i << "\n"; + instrs_.push_back(i); + } + + bool execute() { + std::vector seen(instrs_.size(), false); + acc_ = 0; + pc_ = 0; + while (!seen[pc_]) { + assert(pc_ < instrs_.size()); + seen[pc_] = true; + execute(instrs_[pc_]); + ++pc_; + if (pc_ == instrs_.size()) { + std::cout << "Terminated\n"; + return true; + } + } + std::cout << "PC seen before: " << pc_ << "\n"; + return false; + } + + void find_fix() { + for (std::size_t pos = 0; pos < instrs_.size(); ++pos) { + if (instrs_[pos].flip_jmp()) { + if (execute()) { + std::cout << "Success at instruction " << pos << ": " << instrs_[pos] + << "\n"; + return; + } + instrs_[pos].flip_jmp(); + } + } + } + + Value acc() const noexcept { return acc_; } + +private: + void execute(Instruction const &i) { + std::cout << pc_ << ": " << i; + switch (i.opcode()) { + case Opcode::Acc: + std::cout << "; acc = " << acc_ << " + " << i.value(); + acc_ += i.value(); + std::cout << " = " << acc_; + break; + case Opcode::Jmp: + std::cout << "; pc = " << pc_ << " + " << i.value(); + pc_ += i.value() - 1; + std::cout << " = " << pc_ + 1; + break; + case Opcode::Nop: + break; + } + std::cout << "\n"; + } + + Instructions instrs_; + std::size_t pc_; + Value acc_; +}; + +int main(int argc, char **argv) { + VM vm; + for (std::string line; std::getline(std::cin, line);) { + vm.add_instruction(Instruction(line)); + } + + vm.find_fix(); + std::cout << "Accumulator: " << vm.acc() << "\n"; + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-09-01.cc b/2020/puzzle-09-01.cc new file mode 100644 index 0000000..371dedf --- /dev/null +++ b/2020/puzzle-09-01.cc @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include +#include + +template struct Buffer { + Buffer() {} + + bool add_value(std::string const &s) { + unsigned long i = std::stoul(s); + auto size = buf_.size(); + if (size == L && !valid(i)) { + return false; + } + if (size == L) { + buf_.pop_front(); + } + buf_.push_back(i); + return true; + } + +private: + bool valid(unsigned long v) const { + assert(buf_.size() == L); + for (auto i = buf_.begin(); i != buf_.end(); ++i) { + auto j = i; + for (++j; j != buf_.end(); ++j) { + if (v == *i + *j) { + std::cout << "v = " + << " " << *i << " + " << *j << "\n"; + return true; + } + } + } + return false; + } + + std::list buf_; +}; + +int main(int argc, char **argv) { + Buffer<25> buf; + for (std::string line; std::getline(std::cin, line);) { + if (!buf.add_value(line)) { + std::cout << "Invalid: " << line << "\n"; + return 0; + } + } + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-09-02.cc b/2020/puzzle-09-02.cc new file mode 100644 index 0000000..c00302a --- /dev/null +++ b/2020/puzzle-09-02.cc @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include + +template struct Buffer { + Buffer() {} + + bool add_value(std::string const &s) { + unsigned long i = std::stoul(s); + bool result = true; + auto size = buf_.size(); + if (size >= L) { + result = valid(i); + } + buf_.push_back(i); + return result; + } + + unsigned long get_weakness(unsigned long v) { + std::size_t sum_begin = 0; + std::size_t sum_end = 0; + unsigned long sum = 0; + while (sum != v) { + if (sum < v) { + assert(sum_end != buf_.size()); + sum += buf_[sum_end]; + ++sum_end; + } + if (sum > v) { + assert(sum_begin != buf_.size()); + sum -= buf_[sum_begin]; + ++sum_begin; + } + } + std::cout << "sum_begin = " << sum_begin << " sum_end = " << sum_end + << "\n"; + std::vector vec(buf_.begin() + sum_begin, + buf_.begin() + sum_end); + std::sort(vec.begin(), vec.end()); + return vec[0] + vec[vec.size() - 1]; + } + +private: + bool valid(unsigned long v) const { + assert(buf_.size() >= L); + for (auto i = buf_.size() - 25; i != buf_.size(); ++i) { + for (auto j = i + 1; j != buf_.size(); ++j) { + if (v == buf_[i] + buf_[j]) { + return true; + } + } + } + return false; + } + + std::vector buf_; +}; + +int main(int argc, char **argv) { + Buffer<25> buf; + unsigned long invalid = 0; + for (std::string line; std::getline(std::cin, line);) { + if (!buf.add_value(line)) { + if (invalid == 0) { + invalid = std::stoul(line); + } + } + } + + std::cout << "First Invalid: " << invalid << "\n"; + std::cout << "Weakness: " << buf.get_weakness(invalid) << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-10-01.cc b/2020/puzzle-10-01.cc new file mode 100644 index 0000000..d9681f5 --- /dev/null +++ b/2020/puzzle-10-01.cc @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using Jolt = unsigned long; + +int main(int argc, char **argv) { + std::vector jolts; + for (std::string line; std::getline(std::cin, line);) { + jolts.push_back(std::stoul(line)); + } + + std::sort(jolts.begin(), jolts.end()); + std::array gaps = {}; + + Jolt last = 0; + for (auto j : jolts) { + ++gaps[j - last]; + last = j; + } + // and the gap to our voltage + ++gaps[3]; + + std::cout << gaps[1] << " * " << gaps[3] << " = " << gaps[1] * gaps[3] + << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-10-02.cc b/2020/puzzle-10-02.cc new file mode 100644 index 0000000..7a1db8b --- /dev/null +++ b/2020/puzzle-10-02.cc @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using Jolt = unsigned long; + +Jolt count_valid_combinations(std::vector const &jolts) { + std::vector valid(jolts.size(), 0); + valid[jolts.size() - 1] = 1; + std::size_t i = jolts.size() - 1; + do { + auto i2 = i; + --i; + + valid[i] = 0; + while (i2 < jolts.size() && jolts[i2] < jolts[i] + 4) { + valid[i] += valid[i2]; + ++i2; + } + std::cout << jolts[i] << ": " << valid[i] << "\n"; + } while (i > 0); + return valid[0]; +} + +int main(int argc, char **argv) { + std::vector jolts; + for (std::string line; std::getline(std::cin, line);) { + jolts.push_back(std::stoul(line)); + } + + jolts.push_back(0); + std::sort(jolts.begin(), jolts.end()); + jolts.push_back(jolts[jolts.size() - 1] + 3); + + auto combinations = count_valid_combinations(jolts); + std::cout << "Number of valid combinations: " << combinations << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-11-01.cc b/2020/puzzle-11-01.cc new file mode 100644 index 0000000..be29d4c --- /dev/null +++ b/2020/puzzle-11-01.cc @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using Array = std::vector; + +char next_state(Array const &a, std::size_t row, std::size_t col) { + unsigned occupied = 0; + if (row > 0) { + if (col > 0) { + occupied += (a[row - 1][col - 1] == '#'); + } + occupied += a[row - 1][col] == '#'; + if (col < a[row - 1].size() - 1) { + occupied += (a[row - 1][col + 1] == '#'); + } + } + if (col > 0) { + occupied += (a[row][col - 1] == '#'); + } + if (col < a[row].size() - 1) { + occupied += (a[row][col + 1] == '#'); + } + if (row < a.size() - 1) { + if (col > 0) { + occupied += (a[row + 1][col - 1] == '#'); + } + occupied += a[row + 1][col] == '#'; + if (col < a[row + 1].size() - 1) { + occupied += (a[row + 1][col + 1] == '#'); + } + } + + if (a[row][col] == 'L' && occupied == 0) { + return '#'; + } else if (a[row][col] == '#' && occupied >= 4) { + return 'L'; + } else { + return a[row][col]; + } +} + +unsigned long count_occupied(Array const &a) { + unsigned long count = 0; + for (auto const &row : a) { + for (auto col : row) { + count += (col == '#'); + } + } + + return count; +} + +unsigned long run_to_steady_state(Array const &a) { + Array current = a; + bool changed = true; + unsigned i = 0; + while (changed) { + std::cout << "------ iter = " << i++ << "\n"; + changed = false; + Array next; + for (std::size_t row = 0; row < current.size(); ++row) { + std::string s = ""; + for (std::size_t col = 0; col < current[row].size(); ++col) { + char c = next_state(current, row, col); + s.push_back(c); + if (c != current[row][col]) { + changed = true; + } + } + next.push_back(s); + std::cout << s << "\n"; + } + current = next; + } + + return count_occupied(current); +} +int main(int argc, char **argv) { + Array array; + + for (std::string line; std::getline(std::cin, line);) { + array.push_back(line); + std::cout << line << "\n"; + } + + unsigned long result = run_to_steady_state(array); + + std::cout << "Number of occupied states: " << result << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-11-02.cc b/2020/puzzle-11-02.cc new file mode 100644 index 0000000..a60a795 --- /dev/null +++ b/2020/puzzle-11-02.cc @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using Array = std::vector; + +unsigned search(Array const &a, std::size_t row, std::size_t col, int rd, + int cd) { + int cr = (int)row; + int cc = (int)col; + while (true) { + cr += rd; + cc += cd; + if (cr == row && cc == col) { + return 0; + } + if (cr < 0 || cr >= a.size()) { + return 0; + } + if (cc < 0 || cc >= a[cr].size()) { + return 0; + } + if (a[cr][cc] == '#') { + return 1; + } + if (a[cr][cc] == 'L') { + return 0; + } + } + + assert(false); +} + +char next_state(Array const &a, std::size_t row, std::size_t col) { + unsigned occupied = 0; + for (int row_delta = -1; row_delta < 2; ++row_delta) { + for (int col_delta = -1; col_delta < 2; ++col_delta) { + occupied += search(a, row, col, row_delta, col_delta); + } + } + + if (a[row][col] == 'L' && occupied == 0) { + return '#'; + } else if (a[row][col] == '#' && occupied >= 5) { + return 'L'; + } else { + return a[row][col]; + } +} + +unsigned long count_occupied(Array const &a) { + unsigned long count = 0; + for (auto const &row : a) { + for (auto col : row) { + count += (col == '#'); + } + } + + return count; +} + +unsigned long run_to_steady_state(Array const &a) { + Array current = a; + bool changed = true; + unsigned i = 0; + while (changed) { + std::cout << "------ iter = " << i++ << "\n"; + changed = false; + Array next; + for (std::size_t row = 0; row < current.size(); ++row) { + std::string s = ""; + for (std::size_t col = 0; col < current[row].size(); ++col) { + char c = next_state(current, row, col); + s.push_back(c); + if (c != current[row][col]) { + changed = true; + } + } + next.push_back(s); + std::cout << s << "\n"; + } + current = next; + } + + return count_occupied(current); +} +int main(int argc, char **argv) { + Array array; + + for (std::string line; std::getline(std::cin, line);) { + array.push_back(line); + std::cout << line << "\n"; + } + + unsigned long result = run_to_steady_state(array); + + std::cout << "Number of occupied states: " << result << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-12-01.cc b/2020/puzzle-12-01.cc new file mode 100644 index 0000000..68cac96 --- /dev/null +++ b/2020/puzzle-12-01.cc @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +enum class Heading { North = 'N', East = 'E', South = 'S', West = 'W' }; + +struct Position { + Position() : x_(0), y_(0), head_(Heading::East) {} + + void move(std::string const &s) { + char act = s[0]; + int dist = std::stoi(s.substr(1)); + + switch (act) { + case 'N': + case 'E': + case 'S': + case 'W': + move(dist, static_cast(act)); + break; + case 'F': + move(dist, head_); + break; + case 'L': + rotate_left(dist); + break; + case 'R': + rotate_left(360 - dist); + break; + default: + assert(false); + } + + std::cout << s << ": (" << x_ << ", " << y_ << ")\n"; + } + + void move(int dist, Heading head) { + switch (head) { + case Heading::North: + y_ += dist; + break; + case Heading::East: + x_ += dist; + break; + case Heading::South: + y_ -= dist; + break; + case Heading::West: + x_ -= dist; + break; + default: + assert(false); + break; + } + } + + void rotate_left(int deg) { + /* We want the rang [-180, 180]. */ + while (deg < -180) { + deg += 360; + } + while (deg > 180) { + deg -= 360; + } + + if (deg == -90) { + switch (head_) { + case Heading::North: + head_ = Heading::East; + break; + case Heading::East: + head_ = Heading::South; + break; + case Heading::South: + head_ = Heading::West; + break; + case Heading::West: + head_ = Heading::North; + break; + default: + assert(false); + } + } else if (deg == 90) { + switch (head_) { + case Heading::North: + head_ = Heading::West; + break; + case Heading::East: + head_ = Heading::North; + break; + case Heading::South: + head_ = Heading::East; + break; + case Heading::West: + head_ = Heading::South; + break; + default: + assert(false); + } + } else if (deg == 180 || deg == -180) { + switch (head_) { + case Heading::North: + head_ = Heading::South; + break; + case Heading::East: + head_ = Heading::West; + break; + case Heading::South: + head_ = Heading::North; + break; + case Heading::West: + head_ = Heading::East; + break; + default: + assert(false); + } + } else if (deg != 0) { + assert(false); + } + } + + int distance() const noexcept { return std::abs(x_) + std::abs(y_); } + + int x_; + int y_; + Heading head_; +}; + +int main(int argc, char **argv) { + Position pos; + + for (std::string line; std::getline(std::cin, line);) { + pos.move(line); + } + + auto dist = pos.distance(); + std::cout << "Distance: " << dist << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-12-02.cc b/2020/puzzle-12-02.cc new file mode 100644 index 0000000..db88d44 --- /dev/null +++ b/2020/puzzle-12-02.cc @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +enum class Heading { North = 'N', East = 'E', South = 'S', West = 'W' }; + +struct Position { + Position() : x_(0), y_(0), wx_(10), wy_(1), head_(Heading::East) {} + + void move(std::string const &s) { + char act = s[0]; + int dist = std::stoi(s.substr(1)); + + switch (act) { + case 'N': + case 'E': + case 'S': + case 'W': + move_way(dist, static_cast(act)); + break; + case 'F': + x_ += wx_ * dist; + y_ += wy_ * dist; + break; + case 'L': + rotate_left(dist); + break; + case 'R': + rotate_left(360 - dist); + break; + default: + assert(false); + } + + std::cout << s << ": Pos = (" << x_ << ", " << y_ << "), Way = (" << wx_ + << ", " << wy_ << ")\n"; + } + + void move_way(int dist, Heading head) { + switch (head) { + case Heading::North: + wy_ += dist; + break; + case Heading::East: + wx_ += dist; + break; + case Heading::South: + wy_ -= dist; + break; + case Heading::West: + wx_ -= dist; + break; + default: + assert(false); + break; + } + } + + void rotate_left(int deg) { + /* We want the rang [-180, 180]. */ + while (deg < -180) { + deg += 360; + } + while (deg > 180) { + deg -= 360; + } + + if (deg == -90) { + int px = wx_; + int py = wy_; + wx_ = py; + wy_ = -px; + } else if (deg == 90) { + int px = wx_; + int py = wy_; + wx_ = -py; + wy_ = px; + } else if (deg == 180 || deg == -180) { + wx_ = -wx_; + wy_ = -wy_; + } else if (deg != 0) { + assert(false); + } + } + + int distance() const noexcept { return std::abs(x_) + std::abs(y_); } + + int x_; + int y_; + int wx_; + int wy_; + Heading head_; +}; + +int main(int argc, char **argv) { + Position pos; + + for (std::string line; std::getline(std::cin, line);) { + pos.move(line); + } + + auto dist = pos.distance(); + std::cout << "Distance: " << dist << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-13-01.cc b/2020/puzzle-13-01.cc new file mode 100644 index 0000000..d5c5f01 --- /dev/null +++ b/2020/puzzle-13-01.cc @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Time = unsigned long; + +int main(int argc, char **argv) { + Time arrival; + Time closest = ULONG_MAX; + Time closest_id = 0; + std::string line; + std::getline(std::cin, line); + arrival = std::stoul(line); + std::cout << "Arrival time: " << arrival << "\n"; + + std::string buses; + std::getline(std::cin, buses); + std::string::size_type pos = 0; + + while (pos < buses.size()) { + if (buses[pos] == ',' || buses[pos] == 'x') { + ++pos; + continue; + } + + std::string::size_type len = 0; + Time depart = std::stoul(buses.substr(pos), &len); + pos += len; + auto next_departure_in = depart - (arrival % depart); + if (next_departure_in == depart) { + next_departure_in = 0; + } + std::cout << "Bus #" << depart + << " previous departure: " << (arrival / depart) * depart + << ", next departure in: " << next_departure_in << "\n"; + if (next_departure_in < closest) { + closest = next_departure_in; + closest_id = depart; + } + } + + std::cout << "Next departure # " << closest_id << " in " << closest + << " minutes.\n"; + std::cout << "Result = " << closest * closest_id << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-13-02.cc b/2020/puzzle-13-02.cc new file mode 100644 index 0000000..606dbde --- /dev/null +++ b/2020/puzzle-13-02.cc @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Time = unsigned long; +using IDPair = std::pair; +using IDVector = std::vector; + +int main(int argc, char **argv) { + std::string line; + std::getline(std::cin, line); // Ignore first line + + std::string buses; + std::getline(std::cin, buses); + std::string::size_type pos = 0; + Time offset = 0; + IDVector ids; + + while (pos < buses.size()) { + if (buses[pos] == ',') { + ++pos; + continue; + } else if (buses[pos] == 'x') { + ++offset; + ++pos; + continue; + } + + std::string::size_type len = 0; + Time depart = std::stoul(buses.substr(pos), &len); + std::cout << "Bus #" << depart << " at offset " << offset << "\n"; + ids.push_back(std::make_pair(depart, offset)); + pos += len; + ++offset; + } + + // Make assumption that all bus IDs are co-prime. + Time t = 0; + Time t_incr = 1; + for (auto const &bus : ids) { + while ((t + bus.second) % bus.first != 0) { + t += t_incr; + } + std::cout << "Bus #" << bus.first << " at offset " << bus.second + << " t = " << t << "\n"; + t_incr *= bus.first; + } + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-14-01.cc b/2020/puzzle-14-01.cc new file mode 100644 index 0000000..da83414 --- /dev/null +++ b/2020/puzzle-14-01.cc @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using Data = std::uint64_t; +using Mem = std::vector; + +struct Computer { + Computer() : set_(0), clear_(0), mem_() {} + + void add_instruction(std::string const &s) { + static const std::regex re("mem\\[(\\d+)\\] = (\\d+)"); + std::smatch m; + + if (s.substr(0, 4) == "mask") { + set_ = 0; + clear_ = 0; + for (auto c : s.substr(7)) { + set_ <<= 1; + clear_ <<= 1; + set_ |= (c == '1'); + clear_ |= (c != '0'); + } + std::cout << "mask set: " << std::hex << set_ << "\n"; + std::cout << "mask clear: " << std::hex << clear_ << "\n"; + } else if (std::regex_search(s, m, re)) { + Data loc = std::stoul(m.str(1)); + Data value = std::stoul(m.str(2)); + std::cout << "mem[" << std::dec << loc << " = (" << value << " | " << set_ + << ") & " << clear_ << " = "; + value = (value | set_) & clear_; + std::cout << value << "\n"; + if (mem_.size() <= loc) { + mem_.resize(loc + 1, 0); + } + mem_[loc] = value; + } + } + + Data mem_sum() const noexcept { + Data r = 0; + for (auto v : mem_) { + r += v; + } + return r; + } + + Data set_; + Data clear_; + Mem mem_; +}; + +int main(int argc, char **argv) { + Computer c; + + std::string line; + while (std::getline(std::cin, line)) { + c.add_instruction(line); + } + + std::cout << "Memory sum: " << std::dec << c.mem_sum() << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-14-02.cc b/2020/puzzle-14-02.cc new file mode 100644 index 0000000..5c2b646 --- /dev/null +++ b/2020/puzzle-14-02.cc @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using Data = std::uint64_t; +using Mem = std::map; + +struct Computer { + + void add_instruction(std::string const &s) { + static const std::regex re("mem\\[(\\d+)\\] = (\\d+)"); + std::smatch m; + + if (s.substr(0, 4) == "mask") { + std::string mask = s.substr(7); + std::reverse(mask.begin(), mask.end()); + set_ = 0; + bits_.clear(); + std::size_t bit = 0; + std::cout << "Bitmask: floating bits: "; + for (auto c : mask) { + if (c == '1') { + set_ |= Data(1) << bit; + + } else if (c == 'X') { + bits_.push_back(std::size_t(1) << bit); + std::cout << bit << " (" << (std::size_t(1) << bit) << ") "; + + } else { + assert(c == '0'); + } + ++bit; + } + assert(bit == mask.size()); + std::cout << " set: 0x" << std::hex << set_ << std::dec << "\n"; + } else if (std::regex_search(s, m, re)) { + Data loc = std::stoul(m.str(1)); + Data value = std::stoul(m.str(2)); + set_mem(loc, value); + } + } + + void set_mem(Data loc, Data value) { + std::cout << "Writing: " << value << " to base loc " << std::hex << loc + << " which floats to:"; + for (auto b : bits_) { + loc &= ~b; + } + for (std::size_t i = 0; i < (1 << bits_.size()); ++i) { + auto l = loc | set_; + for (std::size_t j = 0; j < bits_.size(); ++j) { + if (i & (std::size_t(1) << j)) { + l |= bits_[j]; + } + } + std::cout << " " << l; + auto [it, success] = mem_.insert({l, value}); + if (!success) { + it->second = value; + } + } + std::cout << std::dec << "\n"; + } + + Data mem_sum() const noexcept { + Data r = 0; + for (auto v : mem_) { + r += v.second; + } + return r; + } + + Data set_; + std::vector bits_; + Mem mem_; +}; + +int main(int argc, char **argv) { + Computer c; + + std::string line; + while (std::getline(std::cin, line)) { + c.add_instruction(line); + } + + std::cout << "Memory sum: " << std::dec << c.mem_sum() << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-15-01.cc b/2020/puzzle-15-01.cc new file mode 100644 index 0000000..e7df5a6 --- /dev/null +++ b/2020/puzzle-15-01.cc @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using NumMap = std::map; + +int add_to_map(NumMap &nums, int num, int turn) { + auto [it, success] = nums.insert({num, turn}); + if (success) { + // std::cout << "Turn " << turn << ": " << num << " (new)\n"; + return 0; + } else { + int r = turn - it->second; + // std::cout << "Turn " << turn << ": " << num << " (previous seen turn " + // << it->second << " dist = " << r << ")\n"; + it->second = turn; + return r; + } +} + +int run(std::string const &s, int num_turns) { + NumMap seen; + std::size_t pos = 0; + std::size_t len; + int next_num = 0; + int turn = 1; + while (pos < s.size()) { + int num = std::stoi(s.substr(pos), &len); + pos += len; + while (pos < s.size() && s[pos] == ',') { + ++pos; + } + next_num = add_to_map(seen, num, turn); + ++turn; + } + + while (turn < num_turns) { + next_num = add_to_map(seen, next_num, turn); + ++turn; + } + + return next_num; +} + +int main(int argc, char **argv) { + std::string line; + while (std::getline(std::cin, line)) { + int r = run(line, 2020); + std::cout << "2020'th number: " << r << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-15-02.cc b/2020/puzzle-15-02.cc new file mode 100644 index 0000000..cf6d799 --- /dev/null +++ b/2020/puzzle-15-02.cc @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using NumMap = std::unordered_map; + +int add_to_map(NumMap &nums, int num, int turn) { + auto [it, success] = nums.insert({num, turn}); + if (success) { + // std::cout << "Turn " << turn << ": " << num << " (new)\n"; + return 0; + } else { + int r = turn - it->second; + // std::cout << "Turn " << turn << ": " << num << " (previous seen turn " + // << it->second << " dist = " << r << ")\n"; + it->second = turn; + return r; + } +} + +int run(std::string const &s, int num_turns) { + NumMap seen; + std::size_t pos = 0; + std::size_t len; + int next_num = 0; + int turn = 1; + while (pos < s.size()) { + int num = std::stoi(s.substr(pos), &len); + pos += len; + while (pos < s.size() && s[pos] == ',') { + ++pos; + } + next_num = add_to_map(seen, num, turn); + ++turn; + } + + while (turn < num_turns) { + next_num = add_to_map(seen, next_num, turn); + ++turn; + } + + return next_num; +} + +int main(int argc, char **argv) { + std::string line; + while (std::getline(std::cin, line)) { + int r = run(line, 30000000); + std::cout << "30000000th number: " << r << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-16-01.cc b/2020/puzzle-16-01.cc new file mode 100644 index 0000000..cd4d74f --- /dev/null +++ b/2020/puzzle-16-01.cc @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using ValidVector = std::vector; + +struct Classifier { + void add_ranges(std::string const &s) { + static const std::regex re("(\\w+): (\\d+)-(\\d+) or (\\d+)-(\\d+)"); + std::smatch m; + if (std::regex_search(s, m, re)) { + std::cout << m.str(1) << "valid = "; + add_range(m.str(2), m.str(3)); + std::cout << ", "; + add_range(m.str(4), m.str(5)); + std::cout << "\n"; + } + } + + void add_range(std::string const &lows, std::string const &highs) { + int low = std::stoi(lows); + int high = std::stoi(highs); + ++high; + if (vecs_.size() < high) { + vecs_.resize(high, false); + } + std::cout << "[" << low << ", " << high << "]"; + while (low < high) { + vecs_[low] = true; + ++low; + } + } + + int count_errors(std::string const &s) { + std::string::size_type pos = 0; + int result = 0; + while (pos < s.size()) { + std::size_t len = 0; + int num = std::stoi(s.substr(pos), &len); + if (!vecs_[num]) { + result += num; + std::cout << "Invalid: " << num << "\n"; + } + pos += len; + while (pos < s.size() && s[pos] == ',') { + ++pos; + } + } + + return result; + } + ValidVector vecs_; +}; + +enum class State { Permitted, Your, Nearby }; + +int main(int argc, char **argv) { + std::string line; + + State s = State::Permitted; + Classifier c; + int e = 0; + while (std::getline(std::cin, line)) { + if (line == "your ticket:") { + s = State::Your; + } else if (line == "nearby tickets:") { + s = State::Nearby; + } else if (line == "") { + /* nothing */; + } else if (s == State::Permitted) { + c.add_ranges(line); + } else if (s == State::Nearby) { + e += c.count_errors(line); + } + } + + std::cout << "Error count: " << e << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-16-02.cc b/2020/puzzle-16-02.cc new file mode 100644 index 0000000..6de01d3 --- /dev/null +++ b/2020/puzzle-16-02.cc @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct Classifier { + void add_ranges(std::string const &s) { + static const std::regex re("(.+): (\\d+)-(\\d+) or (\\d+)-(\\d+)"); + std::smatch m; + if (std::regex_search(s, m, re)) { + auto field = field_names_.size(); + field_names_.push_back(m.str(1)); + std::cout << m.str(1) << "valid = "; + add_range(m.str(2), m.str(3), field); + std::cout << ", "; + add_range(m.str(4), m.str(5), field); + std::cout << "\n"; + } + } + + void add_range(std::string const &lows, std::string const &highs, + std::size_t field) { + int low = std::stoi(lows); + int high = std::stoi(highs); + ++high; + unsigned flag = 1 << field; + assert(flag != 0); + if (num_to_valid_field_.size() < high) { + num_to_valid_field_.resize(high, 0); + } + std::cout << "[" << low << ", " << high << "]"; + while (low < high) { + num_to_valid_field_[low] |= flag; + ++low; + } + } + + void close() { + idx_to_valid_field_.resize(0); + idx_to_valid_field_.resize(field_names_.size(), + (1 << field_names_.size()) - 1); + } + + int count_errors(std::string const &s) const { + std::string::size_type pos = 0; + int result = 0; + while (pos < s.size()) { + std::size_t len = 0; + int num = std::stoi(s.substr(pos), &len); + if (num >= num_to_valid_field_.size() || num_to_valid_field_[num] == 0) { + result += num == 0 ? 1 : num; + std::cout << "Invalid: " << num << "\n"; + } + pos += len; + while (pos < s.size() && s[pos] == ',') { + ++pos; + } + } + + return result; + } + + void mark_valid(std::string const &s) { + std::string::size_type pos = 0; + unsigned idx = 0; + while (pos < s.size()) { + std::size_t len = 0; + int num = std::stoi(s.substr(pos), &len); + unsigned valid_fields = + num >= num_to_valid_field_.size() ? 0 : num_to_valid_field_[num]; + idx_to_valid_field_[idx] &= valid_fields; + pos += len; + while (pos < s.size() && s[pos] == ',') { + ++pos; + } + assert(idx_to_valid_field_[idx] != 0); + ++idx; + } + } + + void print_valid() const { + for (std::size_t idx = 0; idx < idx_to_valid_field_.size(); ++idx) { + std::cout << "Index " << idx << " valid for fields:"; + unsigned field = 0; + for (auto valid_fields = idx_to_valid_field_[idx]; valid_fields != 0; + valid_fields >>= 1) { + if (valid_fields & 1) { + std::cout << " " << field_names_[field]; + } + ++field; + } + std::cout << "\n"; + } + } + + void reduce() { + bool changed = true; + while (changed) { + changed = false; + for (unsigned idx = 0; idx < idx_to_valid_field_.size(); ++idx) { + auto valid_fields = idx_to_valid_field_[idx]; + if ((valid_fields & (valid_fields - 1)) == 0) { + std::cout << "Index " << idx << " can only be field " << valid_fields + << "\n"; + for (unsigned idx2 = 0; idx2 < idx_to_valid_field_.size(); ++idx2) { + if (idx == idx2) { + continue; + } + changed |= (idx_to_valid_field_[idx2] & valid_fields) != 0; + idx_to_valid_field_[idx2] &= ~valid_fields; + } + } + } + } + } + + unsigned long calculate_product(std::string const &s, + std::string const &prefix) { + std::vector values; + std::size_t pos = 0; + while (pos < s.size()) { + if (s[pos] == ',') { + ++pos; + } else { + std::size_t len; + values.push_back(std::stoi(s.substr(pos), &len)); + pos += len; + } + } + + unsigned long product = 1; + for (unsigned field = 0; field < field_names_.size(); ++field) { + if (field_names_[field].substr(0, prefix.size()) == prefix) { + unsigned idx = 0; + while (idx < idx_to_valid_field_.size() && + idx_to_valid_field_[idx] != (1 << field)) { + ++idx; + } + assert(idx < idx_to_valid_field_.size()); + product *= values[idx]; + } + } + + return product; + } + + std::vector num_to_valid_field_; + std::vector idx_to_valid_field_; + std::vector field_names_; +}; + +enum class State { Permitted, Your, Nearby }; + +int main(int argc, char **argv) { + std::string line; + + State s = State::Permitted; + Classifier c; + int e = 0; + std::string our_ticket; + while (std::getline(std::cin, line)) { + if (line == "your ticket:") { + s = State::Your; + } else if (line == "nearby tickets:") { + s = State::Nearby; + c.close(); + } else if (line == "") { + /* nothing */; + } else if (s == State::Permitted) { + c.add_ranges(line); + } else if (s == State::Your) { + our_ticket = line; + } else if (s == State::Nearby) { + auto e2 = c.count_errors(line); + e += e2; + if (e2 == 0) { + c.mark_valid(line); + } + } + } + + c.print_valid(); + c.reduce(); + c.print_valid(); + + std::cout << "Product: " << c.calculate_product(our_ticket, "departure ") + << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-17-01.cc b/2020/puzzle-17-01.cc new file mode 100644 index 0000000..9d37076 --- /dev/null +++ b/2020/puzzle-17-01.cc @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ConwayCubeState { + using Point = std::tuple; + using PointSet = std::set; + using NeighbourMap = std::map; + + ConwayCubeState() : next_row_(0) {} + void add_row(std::string const &s) { + for (int x = 0; x < s.size(); ++x) { + if (s[x] == '#') { + set_point(x, next_row_, 0); + } + } + ++next_row_; + } + + ConwayCubeState next_state() const { + ConwayCubeState next; + for (auto const &n : neighbours_) { + bool active = points_.find(n.first) != points_.end(); + if (active && (n.second == 2 || n.second == 3)) { + next.set_point(n.first); + } else if (!active && n.second == 3) { + next.set_point(n.first); + } + } + + return next; + } + + auto active() const { return points_.size(); } + +private: + void set_point(Point const &pt) { + set_point(std::get<0>(pt), std::get<1>(pt), std::get<2>(pt)); + } + + void set_point(int px, int py, int pz) { + points_.insert({px, py, pz}); + + for (int x = px - 1; x < px + 2; ++x) { + for (int y = py - 1; y < py + 2; ++y) { + for (int z = pz - 1; z < pz + 2; ++z) { + if (x == px && y == py && z == pz) { + continue; + } + auto [it, success] = neighbours_.insert({{x, y, z}, 1}); + if (!success) { + assert(it != neighbours_.end()); + it->second += 1; + } + } + } + } + } + + int next_row_; + PointSet points_; + NeighbourMap neighbours_; +}; + +int main(void) { + std::string line; + ConwayCubeState state; + while (std::getline(std::cin, line)) { + state.add_row(line); + std::cout << line << "\n"; + } + + std::cout << "Initial active count = " << state.active() << "\n"; + + for (unsigned i = 0; i < 6; ++i) { + state = state.next_state(); + std::cout << i + 1 << ": active count = " << state.active() << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-17-02.cc b/2020/puzzle-17-02.cc new file mode 100644 index 0000000..a35fa62 --- /dev/null +++ b/2020/puzzle-17-02.cc @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ConwayCubeState { + using Point = std::tuple; + using PointSet = std::set; + using NeighbourMap = std::map; + + ConwayCubeState() : next_row_(0) {} + void add_row(std::string const &s) { + for (int x = 0; x < s.size(); ++x) { + if (s[x] == '#') { + set_point(x, next_row_, 0, 0); + } + } + ++next_row_; + } + + ConwayCubeState next_state() const { + ConwayCubeState next; + for (auto const &n : neighbours_) { + bool active = points_.find(n.first) != points_.end(); + if (active && (n.second == 2 || n.second == 3)) { + next.set_point(n.first); + } else if (!active && n.second == 3) { + next.set_point(n.first); + } + } + + return next; + } + + auto active() const { return points_.size(); } + +private: + void set_point(Point const &pt) { + set_point(std::get<0>(pt), std::get<1>(pt), std::get<2>(pt), + std::get<3>(pt)); + } + + void set_point(int pw, int px, int py, int pz) { + points_.insert({pw, px, py, pz}); + + for (int w = pw - 1; w < pw + 2; ++w) { + for (int x = px - 1; x < px + 2; ++x) { + for (int y = py - 1; y < py + 2; ++y) { + for (int z = pz - 1; z < pz + 2; ++z) { + if (w == pw && x == px && y == py && z == pz) { + continue; + } + auto [it, success] = neighbours_.insert({{w, x, y, z}, 1}); + if (!success) { + assert(it != neighbours_.end()); + it->second += 1; + } + } + } + } + } + } + + int next_row_; + PointSet points_; + NeighbourMap neighbours_; +}; + +int main(void) { + std::string line; + ConwayCubeState state; + while (std::getline(std::cin, line)) { + state.add_row(line); + std::cout << line << "\n"; + } + + std::cout << "Initial active count = " << state.active() << "\n"; + + for (unsigned i = 0; i < 6; ++i) { + state = state.next_state(); + std::cout << i + 1 << ": active count = " << state.active() << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-18-01.cc b/2020/puzzle-18-01.cc new file mode 100644 index 0000000..9e0b37a --- /dev/null +++ b/2020/puzzle-18-01.cc @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum class Token : char { + Eof, + Number, + LParens = '(', + RParens = ')', + Add = '+', + Multiply = '*' +}; +using Value = unsigned long; + +struct Parser { + Parser(std::string const &s) : expr_(s), pos_(0) { skip_whitespace(); } + + Value evaluate() { return binop(); } + +private: + Value binop() { + auto value = primary(); + do { + if (peek() == Token::Add) { + chew(Token::Add); + value += primary(); + } else if (peek() == Token::Multiply) { + chew(Token::Multiply); + value *= primary(); + } else { + return value; + } + } while (true); + } + + Value primary() { + if (peek() == Token::LParens) { + chew(Token::LParens); + Value value = binop(); + chew(Token::RParens); + return value; + } else if (peek() == Token::Number) { + return chew_number(); + } else { + std::cout << "expr_ = " << expr_ << "\n"; + std::cout << "pos_ = " << pos_ << "\n"; + std::cout << "End = " << expr_.substr(pos_) << "\n"; + assert(false); + } + } + + Token peek() { + if (pos_ == expr_.size()) { + return Token::Eof; + } + switch (expr_[pos_]) { + case '(': + return Token::LParens; + case ')': + return Token::RParens; + case '+': + return Token::Add; + case '*': + return Token::Multiply; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return Token::Number; + default: + std::cout << "expr_ = " << expr_ << "\n"; + std::cout << "pos_ = " << pos_ << "\n"; + std::cout << "End = " << expr_.substr(pos_) << "\n"; + assert(false); + } + } + + void chew(Token tok) { + assert(peek() == tok); + switch (tok) { + case Token::LParens: + case Token::RParens: + case Token::Add: + case Token::Multiply: + ++pos_; + skip_whitespace(); + break; + default: + assert(false); + } + } + + void skip_whitespace() { + while (pos_ < expr_.size() && expr_[pos_] == ' ') { + ++pos_; + } + } + + Value chew_number() { + assert(peek() == Token::Number); + + std::size_t len = 0; + Value value = std::stoul(expr_.substr(pos_), &len); + pos_ += len; + skip_whitespace(); + return value; + } + + std::string expr_; + std::string::size_type pos_; +}; + +int main(void) { + std::string line; + Value result = 0; + while (std::getline(std::cin, line)) { + Parser p(line); + Value r = p.evaluate(); + std::cout << line << " = " << r << "\n"; + result += r; + } + + std::cout << "Sum: " << result << "\n"; + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-18-02.cc b/2020/puzzle-18-02.cc new file mode 100644 index 0000000..9cce34c --- /dev/null +++ b/2020/puzzle-18-02.cc @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum class Token : char { + Eof, + Number, + LParens = '(', + RParens = ')', + Add = '+', + Multiply = '*' +}; +using Value = unsigned long; + +struct Parser { + Parser(std::string const &s) : expr_(s), pos_(0) { skip_whitespace(); } + + Value evaluate() { return multop(); } + +private: + Value addop() { + auto value = primary(); + do { + if (peek() == Token::Add) { + chew(Token::Add); + value += primary(); + } else { + return value; + } + } while (true); + } + + Value multop() { + auto value = addop(); + do { + if (peek() == Token::Multiply) { + chew(Token::Multiply); + value *= addop(); + } else { + return value; + } + } while (true); + } + + Value primary() { + if (peek() == Token::LParens) { + chew(Token::LParens); + Value value = evaluate(); + chew(Token::RParens); + return value; + } else if (peek() == Token::Number) { + return chew_number(); + } else { + std::cout << "expr_ = " << expr_ << "\n"; + std::cout << "pos_ = " << pos_ << "\n"; + std::cout << "End = " << expr_.substr(pos_) << "\n"; + assert(false); + } + } + + Token peek() { + if (pos_ == expr_.size()) { + return Token::Eof; + } + switch (expr_[pos_]) { + case '(': + return Token::LParens; + case ')': + return Token::RParens; + case '+': + return Token::Add; + case '*': + return Token::Multiply; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return Token::Number; + default: + std::cout << "expr_ = " << expr_ << "\n"; + std::cout << "pos_ = " << pos_ << "\n"; + std::cout << "End = " << expr_.substr(pos_) << "\n"; + assert(false); + } + } + + void chew(Token tok) { + assert(peek() == tok); + switch (tok) { + case Token::LParens: + case Token::RParens: + case Token::Add: + case Token::Multiply: + ++pos_; + skip_whitespace(); + break; + default: + assert(false); + } + } + + void skip_whitespace() { + while (pos_ < expr_.size() && expr_[pos_] == ' ') { + ++pos_; + } + } + + Value chew_number() { + assert(peek() == Token::Number); + + std::size_t len = 0; + Value value = std::stoul(expr_.substr(pos_), &len); + pos_ += len; + skip_whitespace(); + return value; + } + + std::string expr_; + std::string::size_type pos_; +}; + +int main(void) { + std::string line; + Value result = 0; + while (std::getline(std::cin, line)) { + Parser p(line); + Value r = p.evaluate(); + std::cout << line << " = " << r << "\n"; + result += r; + } + + std::cout << "Sum: " << result << "\n"; + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-19-01.cc b/2020/puzzle-19-01.cc new file mode 100644 index 0000000..c16ec28 --- /dev/null +++ b/2020/puzzle-19-01.cc @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Matcher { + void add_rule(std::string const &s) { + std::size_t len = 0; + unsigned long id = std::stoul(s, &len); + assert(s[len] == ':'); + ++len; + while (s[len] == ' ') { + ++len; + } + if (rules_.size() <= id) { + rules_.resize(id + 1); + } + rules_[id] = s.substr(len); + } + + void calculate_regex() { re_.assign("^" + expand_rule(0) + "$"); } + + bool does_match(std::string const &s) const { + return std::regex_match(s, re_); + } + +private: + std::string expand_rule(std::size_t id) { + std::string re; + std::string const &rule = rules_[id]; + std::size_t pos = 0; + bool needs_brackets = false; + while (pos < rule.size()) { + if (rule[pos] == ' ') { + ++pos; + } else if (rule[pos] == '|') { + re += "|"; + needs_brackets = true; + ++pos; + } else if (rule[pos] == '"') { + ++pos; + while (pos < rule.size() && rule[pos] != '"') { + re += rule[pos]; + ++pos; + } + assert(pos < rule.size()); + assert(rule[pos] == '"'); + ++pos; + } else if (std::isdigit(rule[pos])) { + std::size_t len = 0; + std::size_t subid = std::stoul(rule.substr(pos), &len); + pos += len; + re += expand_rule(subid); + } else { + assert(false); + } + } + + if (needs_brackets) { + re = "(" + re + ")"; + } + std::cout << "Rule " << id << " expands to: " << re << "\n"; + return re; + } + + std::vector rules_; + std::regex re_; +}; + +int main(void) { + std::string line; + Matcher matcher; + bool adding_rules = true; + unsigned matches = 0; + while (std::getline(std::cin, line)) { + if (line.empty()) { + adding_rules = false; + matcher.calculate_regex(); + } else if (adding_rules) { + matcher.add_rule(line); + } else { + bool m = matcher.does_match(line); + std::cout << line << ": does " << (m ? "" : "not ") << "match\n"; + matches += m; + } + } + + std::cout << "Total number of matches: " << matches << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-19-02.cc b/2020/puzzle-19-02.cc new file mode 100644 index 0000000..09a554d --- /dev/null +++ b/2020/puzzle-19-02.cc @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Matcher { + void add_rule(std::string const &s) { + std::size_t len = 0; + unsigned long id = std::stoul(s, &len); + assert(s[len] == ':'); + ++len; + while (s[len] == ' ') { + ++len; + } + if (rules_.size() <= id) { + rules_.resize(id + 1); + } + rules_[id] = s.substr(len); + } + + void calculate_regex() { + re42_ = expand_rule(42); + re31_ = expand_rule(31); + } + + bool does_match(std::string const &s) const { + std::smatch m; + std::string begin = "^" + re42_ + re42_; + unsigned repeats = 1; + while (std::regex_search(s, m, std::regex(begin))) { + std::string end = re31_ + "$"; + std::string suffix = m.suffix(); + for (unsigned i = 0; i < repeats; ++i) { + if (std::regex_search(suffix, std::regex("^" + end))) { + return true; + } + end = re31_ + end; + } + begin += re42_; + ++repeats; + } + + return false; + } + +private: + std::string expand_rule(std::size_t id) { + std::string re; + std::string const &rule = rules_[id]; + std::size_t pos = 0; + bool needs_brackets = false; + while (pos < rule.size()) { + if (rule[pos] == ' ') { + ++pos; + } else if (rule[pos] == '|') { + re += "|"; + needs_brackets = true; + ++pos; + } else if (rule[pos] == '"') { + ++pos; + while (pos < rule.size() && rule[pos] != '"') { + re += rule[pos]; + ++pos; + } + assert(pos < rule.size()); + assert(rule[pos] == '"'); + ++pos; + } else if (std::isdigit(rule[pos])) { + std::size_t len = 0; + std::size_t subid = std::stoul(rule.substr(pos), &len); + pos += len; + re += expand_rule(subid); + } else { + assert(false); + } + } + + if (needs_brackets) { + re = "(" + re + ")"; + } + std::cout << "Rule " << id << " expands to: " << re << "\n"; + return re; + } + + std::vector rules_; + std::string re42_; + std::string re31_; +}; + +int main(void) { + std::string line; + Matcher matcher; + bool adding_rules = true; + unsigned matches = 0; + while (std::getline(std::cin, line)) { + if (line.empty()) { + adding_rules = false; + matcher.calculate_regex(); + } else if (adding_rules) { + matcher.add_rule(line); + } else { + bool m = matcher.does_match(line); + std::cout << line << ": does " << (m ? "" : "not ") << "match\n"; + matches += m; + } + } + + std::cout << "Total number of matches: " << matches << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-20-01.cc b/2020/puzzle-20-01.cc new file mode 100644 index 0000000..551f591 --- /dev/null +++ b/2020/puzzle-20-01.cc @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Id = unsigned long; +using Hash = unsigned long; + +enum Edge { Top, Left, Bottom, Right }; + +struct Picture { + Picture(std::string id, std::istream &is) : in_use_(false) { + assert(id.substr(0, 5) == "Tile "); + id_ = std::stoul(id.substr(5)); + std::string line; + while (std::getline(is, line)) { + if (line.empty()) { + return; + } + rows_.push_back(line); + } + } + + Picture(Picture const &) = delete; + Picture &operator=(Picture const &) = delete; + Picture(Picture &&) = default; + Picture &operator=(Picture &&) = default; + + void flip() { + for (auto &r : rows_) { + std::reverse(r.begin(), r.end()); + } + } + + void rotate() { + std::vector copy(rows_.size()); + for (auto const &r : rows_) { + std::size_t off = copy.size(); + assert(r.size() == copy.size()); + for (auto c : r) { + copy[--off] += c; + } + assert(off == 0); + } + rows_ = copy; + } + + Hash hash(Edge edge) const { + unsigned x = (edge == Edge::Right) ? rows_[0].size() - 1 : 0; + unsigned y = (edge == Edge::Bottom) ? rows_.size() - 1 : 0; + unsigned dx = (edge == Edge::Top || edge == Edge::Bottom) ? 1 : 0; + unsigned dy = (edge == Edge::Left || edge == Edge::Right) ? 1 : 0; + + Hash hash = 0; + for (unsigned i = 0; i < rows_.size(); ++i) { + hash <<= 1; + hash |= (rows_[y][x] == '#'); + y += dy; + x += dx; + } + + return hash; + } + + Id id() const noexcept { return id_; } + + bool operator<(Picture const &pict) const noexcept { return id_ < pict.id_; } + + bool operator==(Picture const &pict) const noexcept { + return id_ == pict.id_; + } + + bool in_use() const noexcept { return in_use_; } + void use() noexcept { in_use_ = true; } + void release() noexcept { in_use_ = false; } + +private: + Id id_; + std::vector rows_; + bool in_use_; +}; + +using Pictures = std::map; +using HashMap = std::multimap; +using Array = std::map, Id>; + +struct PictureArray { + void add(Picture &&pic) { + auto id = pic.id(); + auto [it, success] = pictures_.insert(std::make_pair(id, std::move(pic))); + assert(success); + + // Set up hash -> ID mapping + Picture &picture = it->second; + for (unsigned r = 0; r < 4; ++r) { + for (unsigned f = 0; f < 2; ++f) { + hash_map_.insert({picture.hash(Edge::Top), picture.id()}); + picture.flip(); + } + picture.rotate(); + } + } + + Id solve() { + assert(pictures_.size() == 9 || pictures_.size() == 144); + for (auto &kv : pictures_) { + if (try_position(0, 0, kv.second)) { + print_ids(); + return piece(0, 0).id() * piece(width() - 1, 0).id() * + piece(0, height() - 1).id() * + piece(width() - 1, height() - 1).id(); + } + } + + assert(false); + return 0; + } + +private: + bool try_position(unsigned x, unsigned y, Picture &pict) { + if (pict.in_use()) { + return false; + } + + auto [it, success] = array_.insert({{x, y}, pict.id()}); + if (!success) { + it->second = pict.id(); + } + pict.use(); + for (unsigned r = 0; r < 4; ++r) { + pict.rotate(); + for (unsigned f = 0; f < 2; ++f) { + pict.flip(); + + if (x > 0) { + if (piece(x - 1, y).hash(Edge::Right) != pict.hash(Edge::Left)) { + continue; + } + } + + if (y > 0) { + if (piece(x, y - 1).hash(Edge::Bottom) != pict.hash(Edge::Top)) { + continue; + } + } + + auto next_x = x + 1; + auto next_y = y; + Hash hash = pict.hash(Edge::Right); + + if (next_x == width()) { + next_x = 0; + next_y = y + 1; + hash = piece(0, y).hash(Edge::Bottom); + } + + if (next_y == height()) { + return true; + } + + auto [it, ite] = hash_map_.equal_range(hash); + while (it != ite) { + auto itp = pictures_.find(it->second); + assert(itp != pictures_.end()); + if (try_position(next_x, next_y, itp->second)) { + return true; + } + ++it; + } + } + } + pict.release(); + + return false; + } + + void print_ids() const { + for (unsigned y = 0; y < height(); ++y) { + for (unsigned x = 0; x < width(); ++x) { + std::cout << " " << piece(x, y).id(); + } + std::cout << "\n"; + } + } + + Picture const &piece(unsigned x, unsigned y) const { + auto const &it = array_.find({x, y}); + assert(it != array_.end()); + auto const &itp = pictures_.find(it->second); + assert(itp != pictures_.end()); + return itp->second; + } + + unsigned width() const noexcept { + if (pictures_.size() == 9) { + return 3; + } else if (pictures_.size() == 144) { + return 12; + } else { + assert(false); + } + + return 0; + } + + unsigned height() const noexcept { return width(); } + + Pictures pictures_; + HashMap hash_map_; + Array array_; +}; + +int main(void) { + PictureArray pictures_; + + std::string line; + while (std::getline(std::cin, line)) { + if (line.empty()) { + continue; + } + Picture pic(line, std::cin); + pictures_.add(std::move(pic)); + } + + auto solution = pictures_.solve(); + std::cout << "Product = " << solution << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-20-02.cc b/2020/puzzle-20-02.cc new file mode 100644 index 0000000..64feb27 --- /dev/null +++ b/2020/puzzle-20-02.cc @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Id = unsigned long; +using Hash = unsigned long; + +enum Edge { Top, Left, Bottom, Right }; + +struct PictureArray; + +struct Picture { + Picture(std::string id, std::istream &is) : in_use_(false) { + assert(id.substr(0, 5) == "Tile "); + id_ = std::stoul(id.substr(5)); + std::string line; + while (std::getline(is, line)) { + if (line.empty()) { + return; + } + rows_.push_back(line); + } + } + + Picture(PictureArray const &array); + + Picture(Picture const &) = delete; + Picture &operator=(Picture const &) = delete; + Picture(Picture &&) = default; + Picture &operator=(Picture &&) = default; + + void flip() { + for (auto &r : rows_) { + std::reverse(r.begin(), r.end()); + } + } + + void rotate() { + std::vector copy(rows_.size()); + for (auto const &r : rows_) { + std::size_t off = copy.size(); + assert(r.size() == copy.size()); + for (auto c : r) { + copy[--off] += c; + } + assert(off == 0); + } + rows_ = copy; + } + + Hash hash(Edge edge) const { + unsigned x = (edge == Edge::Right) ? rows_[0].size() - 1 : 0; + unsigned y = (edge == Edge::Bottom) ? rows_.size() - 1 : 0; + unsigned dx = (edge == Edge::Top || edge == Edge::Bottom) ? 1 : 0; + unsigned dy = (edge == Edge::Left || edge == Edge::Right) ? 1 : 0; + + Hash hash = 0; + for (unsigned i = 0; i < rows_.size(); ++i) { + hash <<= 1; + hash |= (rows_[y][x] == '#'); + y += dy; + x += dx; + } + + return hash; + } + + Id id() const noexcept { return id_; } + + bool operator<(Picture const &pict) const noexcept { return id_ < pict.id_; } + + bool operator==(Picture const &pict) const noexcept { + return id_ == pict.id_; + } + + bool in_use() const noexcept { return in_use_; } + void use() noexcept { in_use_ = true; } + void release() noexcept { in_use_ = false; } + + friend std::ostream &operator<<(std::ostream &os, Picture const &pict); + + unsigned find_monsters() { + for (unsigned r = 0; r < 4; ++r) { + rotate(); + for (unsigned f = 0; f < 2; ++f) { + flip(); + auto monster_count = find_monsters1(); + if (monster_count > 0) { + return monster_count; + } + } + } + + return 0; + } + + unsigned roughness() const { + unsigned rough = 0; + for (auto const &r : rows_) { + for (auto c : r) { + if (c == '#') { + ++rough; + } + } + } + return rough; + } + +private: + unsigned find_monsters1() { + // 0 1 + // 01234567890123456789 + // # + // # ## ## ### + // # # # # # # + static const std::string::size_type locs[] = {18, + std::string::npos, + 0, + 5, + 6, + 11, + 12, + 17, + 18, + 19, + std::string::npos, + 1, + 4, + 7, + 10, + 13, + 16, + std::string::npos, + std::string::npos}; + static const std::string::size_type mwidth = 20; + static const std::size_t mheight = 3; + + unsigned monster_count = 0; + for (std::size_t y = 0; y <= rows_.size() - mheight; ++y) { + for (std::string::size_type x = 0; x <= rows_[y].size() - mwidth; ++x) { + std::size_t cy = 0; + std::string::size_type const *cx = locs; + bool found = true; + while (cy < mheight) { + if (*cx == std::string::npos) { + ++cy; + } else if (rows_[y + cy][x + *cx] != '#') { + found = false; + break; + } + ++cx; + } + if (found) { + ++monster_count; + std::size_t cy = 0; + std::string::size_type const *cx = locs; + while (cy < mheight) { + if (*cx == std::string::npos) { + ++cy; + } else { + rows_[y + cy][x + *cx] = '*'; + } + ++cx; + } + } + } + } + + return monster_count; + } + +private: + Id id_; + std::vector rows_; + bool in_use_; +}; + +std::ostream &operator<<(std::ostream &os, Picture const &pict) { + for (auto const &r : pict.rows_) { + os << r << '\n'; + } + return os; +} + +using Pictures = std::map; +using HashMap = std::multimap; +using Array = std::map, Id>; + +struct PictureArray { + void add(Picture &&pic) { + auto id = pic.id(); + auto [it, success] = pictures_.insert(std::make_pair(id, std::move(pic))); + assert(success); + + // Set up hash -> ID mapping + Picture &picture = it->second; + for (unsigned r = 0; r < 4; ++r) { + for (unsigned f = 0; f < 2; ++f) { + hash_map_.insert({picture.hash(Edge::Top), picture.id()}); + picture.flip(); + } + picture.rotate(); + } + } + + Id solve() { + assert(pictures_.size() == 9 || pictures_.size() == 144); + for (auto &kv : pictures_) { + if (try_position(0, 0, kv.second)) { + print_ids(); + return piece(0, 0).id() * piece(width() - 1, 0).id() * + piece(0, height() - 1).id() * + piece(width() - 1, height() - 1).id(); + } + } + + assert(false); + return 0; + } + unsigned width() const noexcept { + if (pictures_.size() == 9) { + return 3; + } else if (pictures_.size() == 144) { + return 12; + } else { + assert(false); + } + + return 0; + } + + unsigned height() const noexcept { return width(); } + + Picture const &piece(unsigned x, unsigned y) const { + auto const &it = array_.find({x, y}); + assert(it != array_.end()); + auto const &itp = pictures_.find(it->second); + assert(itp != pictures_.end()); + return itp->second; + } + +private: + bool try_position(unsigned x, unsigned y, Picture &pict) { + if (pict.in_use()) { + return false; + } + + auto [it, success] = array_.insert({{x, y}, pict.id()}); + if (!success) { + it->second = pict.id(); + } + pict.use(); + for (unsigned r = 0; r < 4; ++r) { + pict.rotate(); + for (unsigned f = 0; f < 2; ++f) { + pict.flip(); + + if (x > 0) { + if (piece(x - 1, y).hash(Edge::Right) != pict.hash(Edge::Left)) { + continue; + } + } + + if (y > 0) { + if (piece(x, y - 1).hash(Edge::Bottom) != pict.hash(Edge::Top)) { + continue; + } + } + + auto next_x = x + 1; + auto next_y = y; + Hash hash = pict.hash(Edge::Right); + + if (next_x == width()) { + next_x = 0; + next_y = y + 1; + hash = piece(0, y).hash(Edge::Bottom); + } + + if (next_y == height()) { + return true; + } + + auto [it, ite] = hash_map_.equal_range(hash); + while (it != ite) { + auto itp = pictures_.find(it->second); + assert(itp != pictures_.end()); + if (try_position(next_x, next_y, itp->second)) { + return true; + } + ++it; + } + } + } + pict.release(); + + return false; + } + + void print_ids() const { + for (unsigned y = 0; y < height(); ++y) { + for (unsigned x = 0; x < width(); ++x) { + std::cout << " " << piece(x, y).id(); + } + std::cout << "\n"; + } + } + + Pictures pictures_; + HashMap hash_map_; + Array array_; +}; + +Picture::Picture(PictureArray const &array) : id_(0), in_use_(false) { + for (unsigned y = 0; y < array.height(); ++y) { + auto ybase = rows_.size(); + for (unsigned x = 0; x < array.width(); ++x) { + auto &pict = array.piece(x, y); + for (auto py = 1; py < pict.rows_.size() - 1; ++py) { + auto yidx = ybase + py - 1; + if (rows_.size() <= yidx) { + rows_.resize(yidx + 1); + } + rows_[yidx] += pict.rows_[py].substr(1, pict.rows_[py].size() - 2); + } + } + } + assert(rows_.size() == array.height() * 8); + assert(rows_[0].size() == array.width() * 8); +} + +int main(void) { + PictureArray pictures_; + + std::string line; + while (std::getline(std::cin, line)) { + if (line.empty()) { + continue; + } + Picture pic(line, std::cin); + pictures_.add(std::move(pic)); + } + + auto solution = pictures_.solve(); + std::cout << "Product = " << solution << "\n"; + + Picture merged(pictures_); + auto monster_count = merged.find_monsters(); + std::cout << merged; + std::cout << "Number of monsters: " << monster_count << "\n"; + std::cout << "Roughness: " << merged.roughness() << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-21-01.cc b/2020/puzzle-21-01.cc new file mode 100644 index 0000000..81429e5 --- /dev/null +++ b/2020/puzzle-21-01.cc @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Ingredient = std::string; +using Allergen = std::string; +using Ingredients = std::set; +using Allergens = std::set; +using IngredientInfo = std::pair; + +using IngredientMap = std::map; +using AllergenMap = std::map; + +class IngredientParser { +public: + void add_recipe(std::string const &s) { + auto it = s.begin(); + Ingredients i; + while (it != s.end()) { + auto ite = std::find(it, s.end(), ' '); + auto ingredient = std::string(it, ite); + while (ite != s.end() && *ite == ' ') { + ++ite; + } + it = ite; + if (ingredient == "(contains") { + break; + } else { + i.insert(ingredient); + auto [iit, success] = + ingredients_.insert({ingredient, {1, Allergens()}}); + if (!success) { + iit->second.first++; + } + } + } + + while (it != s.end()) { + auto ite = std::find_if( + it, s.end(), [](char c) -> bool { return c == ',' || c == ')'; }); + auto allergen = std::string(it, ite); + ++ite; + while (ite != s.end() && *ite == ' ') { + ++ite; + } + it = ite; + auto [insert_it, success] = allergens_.insert({allergen, i}); + if (!success) { + Ingredients a; + std::set_intersection(i.begin(), i.end(), insert_it->second.begin(), + insert_it->second.end(), + std::inserter(a, a.end())); + insert_it->second = a; + } + } + } + + unsigned clean_ingredients() { + for (auto const &kv : allergens_) { + auto allergen = kv.first; + std::cout << "Allergen " << allergen << ":"; + for (auto const &i : kv.second) { + std::cout << " " << i; + auto it = ingredients_.find(i); + assert(it != ingredients_.end()); + it->second.second.insert(allergen); + } + std::cout << "\n"; + } + + unsigned count = 0; + for (auto const &i : ingredients_) { + if (i.second.second.size() == 0) { + std::cout << i.first << " is not an allergen, appears " + << i.second.first << ".\n"; + count += i.second.first; + } + } + + return count; + } + +private: + IngredientMap ingredients_; + AllergenMap allergens_; +}; + +int main(void) { + std::string line; + IngredientParser parser; + while (std::getline(std::cin, line)) { + parser.add_recipe(line); + } + + auto clean = parser.clean_ingredients(); + std::cout << "Number of clean ingredients: " << clean << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-21-02.cc b/2020/puzzle-21-02.cc new file mode 100644 index 0000000..e1b8cbd --- /dev/null +++ b/2020/puzzle-21-02.cc @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Ingredient = std::string; +using Allergen = std::string; +using Ingredients = std::set; +using Allergens = std::set; +using IngredientInfo = std::pair; + +using IngredientMap = std::map; +using AllergenMap = std::map; + +class IngredientParser { +public: + void add_recipe(std::string const &s) { + auto it = s.begin(); + Ingredients i; + while (it != s.end()) { + auto ite = std::find(it, s.end(), ' '); + auto ingredient = std::string(it, ite); + while (ite != s.end() && *ite == ' ') { + ++ite; + } + it = ite; + if (ingredient == "(contains") { + break; + } else { + i.insert(ingredient); + auto [iit, success] = + ingredients_.insert({ingredient, {1, Allergens()}}); + if (!success) { + iit->second.first++; + } + } + } + + while (it != s.end()) { + auto ite = std::find_if( + it, s.end(), [](char c) -> bool { return c == ',' || c == ')'; }); + auto allergen = std::string(it, ite); + ++ite; + while (ite != s.end() && *ite == ' ') { + ++ite; + } + it = ite; + auto [insert_it, success] = allergens_.insert({allergen, i}); + if (!success) { + Ingredients a; + std::set_intersection(i.begin(), i.end(), insert_it->second.begin(), + insert_it->second.end(), + std::inserter(a, a.end())); + insert_it->second = a; + } + } + } + + unsigned clean_ingredients() { + for (auto const &kv : allergens_) { + auto allergen = kv.first; + std::cout << "Allergen " << allergen << ":"; + for (auto const &i : kv.second) { + std::cout << " " << i; + auto it = ingredients_.find(i); + assert(it != ingredients_.end()); + it->second.second.insert(allergen); + } + std::cout << "\n"; + } + + unsigned count = 0; + for (auto const &i : ingredients_) { + if (i.second.second.size() == 0) { + std::cout << i.first << " is not an allergen, appears " + << i.second.first << ".\n"; + count += i.second.first; + } + } + + return count; + } + + std::string dangerous_ingredients() { + bool changed = true; + Ingredients i; + while (changed) { + changed = false; + for (auto const &kv : allergens_) { + if (kv.second.size() == 1) { + auto it = kv.second.begin(); + Ingredient const &ing = *it; + auto [iit, success] = i.insert(ing); + if (!success) { + continue; + } + changed = true; + std::cout << "Allergen " << kv.first << " is in ingredient " << ing + << "\n"; + for (auto &kv2 : allergens_) { + if (kv2.second.size() != 1) { + kv2.second.erase(ing); + } + } + } + } + } + + std::string result; + for (auto const &kv : allergens_) { + result += ","; + result += *kv.second.begin(); + } + + return result.substr(1); + } + +private: + IngredientMap ingredients_; + AllergenMap allergens_; +}; + +int main(void) { + std::string line; + IngredientParser parser; + while (std::getline(std::cin, line)) { + parser.add_recipe(line); + } + + auto clean = parser.clean_ingredients(); + std::cout << "Number of clean ingredients: " << clean << "\n"; + + auto dangerous = parser.dangerous_ingredients(); + std::cout << "Dangerous ingredients: " << dangerous << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-22-01.cc b/2020/puzzle-22-01.cc new file mode 100644 index 0000000..4e313ad --- /dev/null +++ b/2020/puzzle-22-01.cc @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Card = unsigned; +using Cards = std::list; +using Score = unsigned long; + +struct Player { + Player(std::istream &is) { + std::string line; + if (!std::getline(is, name_)) { + assert(false); + } + while (std::getline(is, line)) { + if (line == "") { + break; + } + cards_.push_back(std::stoul(line)); + } + } + + bool empty() const { return cards_.empty(); } + Card front() const { return cards_.front(); } + void pop_front() { cards_.pop_front(); } + void push_back(Card c) { cards_.push_back(c); } + + Score score() const { + Score r = 0; + unsigned idx = 1; + for (auto it = cards_.rbegin(); it != cards_.rend(); ++it) { + r += idx * *it; + ++idx; + } + return r; + } + +private: + std::string name_; + Cards cards_; +}; + +Score play_game(Player &p1, Player &p2) { + while (!p1.empty() && !p2.empty()) { + Card c1 = p1.front(); + Card c2 = p2.front(); + p1.pop_front(); + p2.pop_front(); + if (c1 > c2) { + p1.push_back(c1); + p1.push_back(c2); + } else if (c1 < c2) { + p2.push_back(c2); + p2.push_back(c1); + } + } + + return p1.score() + p2.score(); +} + +int main(void) { + Player player1(std::cin); + Player player2(std::cin); + + Score final_score = play_game(player1, player2); + std::cout << "Final score: " << final_score << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-22-02.cc b/2020/puzzle-22-02.cc new file mode 100644 index 0000000..135032c --- /dev/null +++ b/2020/puzzle-22-02.cc @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Card = unsigned; +using Cards = std::list; +using Score = unsigned long; +using Hash = std::string; + +struct Player { + Player(std::istream &is) { + std::string line; + if (!std::getline(is, name_)) { + assert(false); + } + while (std::getline(is, line)) { + if (line == "") { + break; + } + cards_.push_back(std::stoul(line)); + } + } + + bool empty() const { return cards_.empty(); } + Card front() const { return cards_.front(); } + void pop_front() { cards_.pop_front(); } + void push_back(Card c) { cards_.push_back(c); } + std::size_t size() const { return cards_.size(); } + + Score score() const { + Score r = 0; + unsigned idx = 1; + for (auto it = cards_.rbegin(); it != cards_.rend(); ++it) { + r += idx * *it; + ++idx; + } + return r; + } + + Hash hash() const { + std::string r = name_; + for (auto c : cards_) { + assert(c > 0); + assert(c < 128); + r += (char)c; + } + + return r; + } + + Player(Player const &prev, Card size) : name_(prev.name_ + "-") { + auto it = prev.cards_.begin(); + for (Card i = 0; i < size; ++i) { + cards_.push_back(*it++); + } + } + + friend std::ostream &operator<<(std::ostream &os, Player const &p); + +private: + std::string name_; + Cards cards_; +}; + +std::ostream &operator<<(std::ostream &os, Player const &p) { + os << p.name_; + for (auto c : p.cards_) { + os << " " << c; + } + return os; +} + +// False = p1 wins, true = p2 wins. +bool play_game(Player &p1, Player &p2) { + std::set hashes; + + while (!p1.empty() && !p2.empty()) { + // Ensure we've not seen this configuration before. + auto [it, success] = hashes.insert(p1.hash() + p2.hash()); + if (!success) { + return false; + } + + Card c1 = p1.front(); + Card c2 = p2.front(); + p1.pop_front(); + p2.pop_front(); + if (p1.size() >= c1 && p2.size() >= c2) { + Player sp1(p1, c1); + Player sp2(p2, c2); + bool result = play_game(sp1, sp2); + if (!result) { + p1.push_back(c1); + p1.push_back(c2); + } else { + p2.push_back(c2); + p2.push_back(c1); + } + } else if (c1 > c2) { + p1.push_back(c1); + p1.push_back(c2); + } else if (c1 < c2) { + p2.push_back(c2); + p2.push_back(c1); + } else { + assert(false); + } + } + + return p1.empty(); +} + +int main(void) { + Player player1(std::cin); + Player player2(std::cin); + + play_game(player1, player2); + Score final_score = player1.score() + player2.score(); + std::cout << "Final score: " << final_score << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-23-01.cc b/2020/puzzle-23-01.cc new file mode 100644 index 0000000..1602d21 --- /dev/null +++ b/2020/puzzle-23-01.cc @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Cups { + Cups(std::string const &s) : s_(s) { + std::cout << "Initial state: " << s_ << "\n"; + } + + void play(unsigned moves) { + // We assume the current value is always at index 0. + // So the values to remove are in offsets [1, 4). + while (moves-- > 0) { + char current = s_[0]; + char d = s_[0] == '1' ? '9' : s_[0] - 1; + std::string::size_type dest = std::string::npos; + while (dest == std::string::npos) { + dest = s_.find(d, 4); + d = d == '1' ? '9' : d - 1; + } + + s_ = s_.substr(4, dest - 3) + s_.substr(1, 3) + s_.substr(dest + 1) + + s_[0]; + std::cout << moves << ": " << s_ << "\n"; + } + } + + std::string result() const { + auto split = s_.find('1'); + return s_.substr(split + 1) + s_.substr(0, split); + } + +private: + std::string s_; +}; + +int main(void) { + std::string line; + std::getline(std::cin, line); + Cups cups(line); + cups.play(100); + std::cout << "Result: " << cups.result() << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-23-02.cc b/2020/puzzle-23-02.cc new file mode 100644 index 0000000..0f2fb47 --- /dev/null +++ b/2020/puzzle-23-02.cc @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Value = unsigned long; +struct Cup { + Cup() : value_(0), next_(nullptr) {} + + Value value() const { return value_; } + void value(Value v) { value_ = v; } + Cup *next() const { return next_; } + void next(Cup *n) { next_ = n; } + +private: + Value value_; + Cup *next_; +}; + +struct Cups { + static constexpr std::size_t num_cups_ = 1000000; + static constexpr std::size_t num_moves_ = 10000000; + + Cups(std::string const &s) : cups_(new Cup[num_cups_]), current_(cups_) { + std::size_t idx = 0; + for (auto c : s) { + cups_[idx].value(c - '0'); + cups_[idx].next(cups_ + idx + 1); + ++idx; + } + Value v = 10; + while (idx < num_cups_) { + cups_[idx].value(v++); + cups_[idx].next(cups_ + idx + 1); + ++idx; + } + cups_[num_cups_ - 1].next(cups_); + assert(v == num_cups_ + 1); + } + + ~Cups() { delete[] cups_; } + + void play() { + for (std::size_t move = 0; move < num_moves_; ++move) { + if (move % 1000 == 0) { + std::cout << "A" << move << " " << current_ - cups_ << " " + << current_->value() << " " << result() << "\n"; + // print(); + } + + // Remove first three after current. + Cup *rb = current_->next(); + Cup *re = rb->next()->next(); + current_->next(re->next()); + re->next(nullptr); + + // Work out the destination + auto vm1 = current_->value(); + do { + --vm1; + if (vm1 == 0) { + vm1 = num_cups_; + } + } while (vm1 == rb->value() || vm1 == rb->next()->value() || + vm1 == rb->next()->next()->value()); + + // Where do we insert? Note that we use the hack that all the values are + // actually in an index and we know what the value is (roughly) based on + // the index. + Cup *ins = nullptr; + if (vm1 < 10) { + ins = cups_; + while (ins->value() != vm1) { + assert(ins != cups_ + 10); + ++ins; + } + } else { + ins = cups_ + vm1 - 1; + } + + assert(ins->value() == vm1); + + // Insert the removed elements. + re->next(ins->next()); + ins->next(rb); + + // Move to next element. + current_ = current_->next(); + } + } + + Value result() const { + Cup *one = cups_; + while (one->value() != 1) { + ++one; + assert(one != cups_ + num_cups_); + } + + return one->next()->value() * one->next()->next()->value(); + } + + void print() const { + std::cout << "State:"; + Cup const *c = current_; + do { + std::cout << " " << c->value(); + c = c->next(); + } while (c != current_); + std::cout << " loop \n"; + } + +private: + Cup *cups_; + Cup *current_; +}; + +int main(void) { + std::string line; + std::getline(std::cin, line); + Cups cups(line); + cups.play(); + std::cout << "Result: " << cups.result() << "\n"; + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-24-01.cc b/2020/puzzle-24-01.cc new file mode 100644 index 0000000..3a48f90 --- /dev/null +++ b/2020/puzzle-24-01.cc @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Coord = long; +using Position = std::pair; +using Tiles = std::set; + +struct Tiler { + void flip_tile(std::string const &s) { + Coord x = 0; + Coord y = 0; + auto it = s.begin(); + while (it != s.end()) { + Coord dx = 2; + if (*it == 's' || *it == 'n') { + y += (*it == 'n') - (*it == 's'); + ++it; + dx = 1; + assert(it != s.end()); + } + + if (*it == 'e') { + x += dx; + } else if (*it == 'w') { + x -= dx; + } else { + assert(false); + } + ++it; + } + + // Insert tile to be flipped - if we fail remove it. + auto [iit, success] = black_tiles_.insert({x, y}); + if (!success) { + black_tiles_.erase(iit); + } + } + + std::size_t black_count() const noexcept { return black_tiles_.size(); } + +private: + Tiles black_tiles_; +}; + +int main(void) { + Tiler tiler; + std::string line; + while (std::getline(std::cin, line)) { + tiler.flip_tile(line); + } + + std::cout << "Number of black tiles: " << tiler.black_count() << "\n"; + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-24-02.cc b/2020/puzzle-24-02.cc new file mode 100644 index 0000000..fb3ce21 --- /dev/null +++ b/2020/puzzle-24-02.cc @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Coord = long; +using Position = std::pair; +struct State { + bool black_ = false; + int neighbours_ = 0; +}; +using Tiles = std::map; + +struct Tiler { + void flip_tile(std::string const &s) { + Coord x = 0; + Coord y = 0; + auto it = s.begin(); + while (it != s.end()) { + Coord dx = 2; + if (*it == 's' || *it == 'n') { + y += (*it == 'n') - (*it == 's'); + ++it; + dx = 1; + assert(it != s.end()); + } + + if (*it == 'e') { + x += dx; + } else if (*it == 'w') { + x -= dx; + } else { + assert(false); + } + ++it; + } + + // Insert tile to be flipped - if we fail remove it. + flip_tile(x, y); + } + + Tiler next_state() const { + Tiler next(*this); + for (auto const &kv : tiles_) { + if (kv.second.black_ && + (kv.second.neighbours_ == 0 || kv.second.neighbours_ > 2)) { + next.flip_tile(kv.first.first, kv.first.second); + } else if (!kv.second.black_ && kv.second.neighbours_ == 2) { + next.flip_tile(kv.first.first, kv.first.second); + } + } + + return next; + } + + std::size_t black_count() const noexcept { + std::size_t count = 0; + for (auto const &kv : tiles_) { + count += kv.second.black_; + } + + return count; + } + +private: + void set_neighbours(Coord x, Coord y, int delta) { + set_neighbour(x + 2, y, delta); + set_neighbour(x + 1, y - 1, delta); + set_neighbour(x - 1, y - 1, delta); + set_neighbour(x - 2, y, delta); + set_neighbour(x - 1, y + 1, delta); + set_neighbour(x + 1, y + 1, delta); + } + + void set_neighbour(Coord x, Coord y, int delta) { + auto [it, success] = tiles_.insert({{x, y}, {false, delta}}); + if (!success) { + it->second.neighbours_ += delta; + } + assert(it->second.neighbours_ >= 0); + } + + void flip_tile(Coord x, Coord y) { + auto [it, success] = tiles_.insert({{x, y}, {true, 0}}); + if (!success) { + it->second.black_ = !it->second.black_; + } + if (it->second.black_) { + set_neighbours(x, y, 1); + } else { + set_neighbours(x, y, -1); + } + } + +private: + Tiles tiles_; +}; + +int main(void) { + Tiler tiler; + std::string line; + while (std::getline(std::cin, line)) { + tiler.flip_tile(line); + } + + std::cout << "Day 0: " << tiler.black_count() << "\n"; + for (unsigned i = 1; i < 101; ++i) { + tiler = tiler.next_state(); + std::cout << "Day " << i << ": " << tiler.black_count() << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/2020/puzzle-25-01.cc b/2020/puzzle-25-01.cc new file mode 100644 index 0000000..c89ed20 --- /dev/null +++ b/2020/puzzle-25-01.cc @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr unsigned mod = 20201227; +constexpr unsigned initial_subject = 7; + +unsigned lcg(unsigned i, unsigned subject) { + return ((unsigned long)i * (unsigned long)subject) % mod; +} + +unsigned find_loop_length(unsigned pk) { + unsigned v = 1; + unsigned loop = 0; + while (v != pk) { + v = lcg(v, initial_subject); + ++loop; + } + + return loop; +} + +unsigned apply(unsigned pk, unsigned loop_size) { + unsigned v = 1; + for (unsigned i = 0; i < loop_size; ++i) { + v = lcg(v, pk); + } + + return v; +} + +int main(void) { + std::string line; + std::getline(std::cin, line); + unsigned pk1 = std::stoul(line); + std::getline(std::cin, line); + unsigned pk2 = std::stoul(line); + std::cout << "Public keys: " << pk1 << " " << pk2 << "\n"; + auto l1 = find_loop_length(pk1); + auto l2 = find_loop_length(pk2); + std::cout << "Loop lengths: " << l1 << " " << l2 << "\n"; + + auto ek1 = apply(pk1, l2); + auto ek2 = apply(pk2, l1); + + std::cout << "Encryption key: " << ek1 << " " << ek2 << "\n"; + + return 0; +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e51380b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.21) +project(advent-of-code) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +file(GLOB years LIST_DIRECTORIES true RELATIVE "${CMAKE_SOURCE_DIR}" CONFIGURE_DEPENDS "20[1-9][0-9]") +message(STATUS "Years to examine: ${years}") +foreach(year IN LISTS years) + file(GLOB puzzles RELATIVE "${CMAKE_SOURCE_DIR}" CONFIGURE_DEPENDS "${year}/puzzle-*.cc") + foreach(puzzle IN LISTS puzzles) + string(REGEX REPLACE "^.*puzzle-(.*)\\.cc" "puzzle-${year}-\\1" puzzle_name "${puzzle}") + string(REGEX REPLACE "^.*puzzle-(.*)\\.cc" "${CMAKE_SOURCE_DIR}/${year}/puzzle-\\1.CMakeLists.txt" sub_cmake_lists "${puzzle}") + message(STATUS "Puzzle: ${puzzle_name} - source ${puzzle}") + add_executable("${puzzle_name}" "${puzzle}") + if(EXISTS "${sub_cmake_lists}") + include("${sub_cmake_lists}") + endif() + endforeach() +endforeach() \ No newline at end of file