Initial commit
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | # Project exclude paths | ||||||
|  | /cmake-build-debug/ | ||||||
							
								
								
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										2
									
								
								.idea/advent-of-code.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.idea/advent-of-code.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <module classpath="CMake" type="CPP_MODULE" version="4" /> | ||||||
							
								
								
									
										4
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" /> | ||||||
|  | </project> | ||||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="ProjectModuleManager"> | ||||||
|  |     <modules> | ||||||
|  |       <module fileurl="file://$PROJECT_DIR$/.idea/advent-of-code.iml" filepath="$PROJECT_DIR$/.idea/advent-of-code.iml" /> | ||||||
|  |     </modules> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										16
									
								
								2015/puzzle-01-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								2015/puzzle-01-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								2015/puzzle-01-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								2015/puzzle-01-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								2015/puzzle-02-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								2015/puzzle-02-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								2015/puzzle-02-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								2015/puzzle-02-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								2015/puzzle-03-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								2015/puzzle-03-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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<Pos> 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; | ||||||
|  | } | ||||||
							
								
								
									
										64
									
								
								2015/puzzle-03-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								2015/puzzle-03-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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<Pos> 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; | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								2015/puzzle-04-01.CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								2015/puzzle-04-01.CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | find_package(OpenSSL REQUIRED) | ||||||
|  | target_link_libraries("${puzzle_name}" OpenSSL::SSL) | ||||||
							
								
								
									
										40
									
								
								2015/puzzle-04-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								2015/puzzle-04-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include <openssl/evp.h> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								2015/puzzle-04-02.CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								2015/puzzle-04-02.CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | find_package(OpenSSL REQUIRED) | ||||||
|  | target_link_libraries("${puzzle_name}" OpenSSL::SSL) | ||||||
							
								
								
									
										40
									
								
								2015/puzzle-04-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								2015/puzzle-04-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #include <openssl/evp.h> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								2015/puzzle-05-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								2015/puzzle-05-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | // 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; | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								2015/puzzle-05-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								2015/puzzle-05-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | // 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; | ||||||
|  | } | ||||||
							
								
								
									
										117
									
								
								2015/puzzle-06-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								2015/puzzle-06-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | enum class Action { TurnOn, Toggle, TurnOff }; | ||||||
|  |  | ||||||
|  | using Point = std::pair<unsigned, unsigned>; | ||||||
|  |  | ||||||
|  | /// 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 <unsigned N> 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; | ||||||
|  | } | ||||||
							
								
								
									
										116
									
								
								2015/puzzle-06-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								2015/puzzle-06-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | enum class Action { TurnOn, Toggle, TurnOff }; | ||||||
|  | using Point = std::pair<unsigned, unsigned>; | ||||||
|  | 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 <unsigned N> 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; | ||||||
|  | } | ||||||
							
								
								
									
										373
									
								
								2015/puzzle-07-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										373
									
								
								2015/puzzle-07-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,373 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | // 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 <class... Ts> struct Overloaded : Ts... { using Ts::operator()...; }; | ||||||
|  | // explicit deduction guide (not needed as of C++20) | ||||||
|  | template <class... Ts> Overloaded(Ts...) -> Overloaded<Ts...>; | ||||||
|  |  | ||||||
|  | /// 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<Value, Wire>; ///< 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 <not> 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 <bin_op> 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 <set> instruction. | ||||||
|  |   /// | ||||||
|  |   /// Also used for the latter half of <not> 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<std::uint16_t>(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<Wire, Value>;        ///< Map wires to values | ||||||
|  | using Instructions = std::vector<Instruction>; ///< 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<Value(Value)> 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<Value(Value, Value)> 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; | ||||||
|  | } | ||||||
							
								
								
									
										373
									
								
								2015/puzzle-07-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										373
									
								
								2015/puzzle-07-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,373 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | // 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 <class... Ts> struct Overloaded : Ts... { using Ts::operator()...; }; | ||||||
|  | // explicit deduction guide (not needed as of C++20) | ||||||
|  | template <class... Ts> Overloaded(Ts...) -> Overloaded<Ts...>; | ||||||
|  |  | ||||||
|  | /// 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<Value, Wire>; ///< 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 <not> 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 <bin_op> 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 <set> instruction. | ||||||
|  |   /// | ||||||
|  |   /// Also used for the latter half of <not> 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<std::uint16_t>(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<Wire, Value>;        ///< Map wires to values | ||||||
|  | using Instructions = std::vector<Instruction>; ///< 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<Value(Value)> 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<Value(Value, Value)> 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; | ||||||
|  | } | ||||||
							
								
								
									
										80
									
								
								2015/puzzle-08-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								2015/puzzle-08-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								2015/puzzle-08-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								2015/puzzle-08-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								2015/puzzle-09-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								2015/puzzle-09-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | using Weight = unsigned; | ||||||
|  | using Node = std::string; | ||||||
|  | using Edge = std::pair<Node, Node>; | ||||||
|  | using Nodes = std::set<Node>; | ||||||
|  | using Edges = std::map<Edge, Weight>; | ||||||
|  |  | ||||||
|  | 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<Node> 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; | ||||||
|  | } | ||||||
							
								
								
									
										96
									
								
								2015/puzzle-09-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								2015/puzzle-09-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | using Weight = unsigned; | ||||||
|  | using Node = std::string; | ||||||
|  | using Edge = std::pair<Node, Node>; | ||||||
|  | using Nodes = std::set<Node>; | ||||||
|  | using Edges = std::map<Edge, Weight>; | ||||||
|  |  | ||||||
|  | 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 <typename Fn> void visit_all_perms(Fn fn) const { | ||||||
|  |     std::vector<Node> 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; | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								2015/puzzle-10-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								2015/puzzle-10-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								2015/puzzle-10-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								2015/puzzle-10-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										77
									
								
								2015/puzzle-11-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								2015/puzzle-11-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								2015/puzzle-11-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								2015/puzzle-11-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								2015/puzzle-12-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								2015/puzzle-12-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								2015/puzzle-12-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								2015/puzzle-12-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								2015/puzzle-13-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								2015/puzzle-13-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | struct Graph { | ||||||
|  |   using Node = std::string; | ||||||
|  |   using Weight = int; | ||||||
|  |   using Nodes = std::set<Node>; | ||||||
|  |   using NodeList = std::vector<Node>; | ||||||
|  |   using Edge = std::pair<Node, Node>; | ||||||
|  |   using EdgeWeights = std::map<Edge, Weight>; | ||||||
|  |  | ||||||
|  |   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; | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								2015/puzzle-13-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								2015/puzzle-13-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | struct Graph { | ||||||
|  |   Graph() { nodes_.insert("self"); } | ||||||
|  |   using Node = std::string; | ||||||
|  |   using Weight = int; | ||||||
|  |   using Nodes = std::set<Node>; | ||||||
|  |   using NodeList = std::vector<Node>; | ||||||
|  |   using Edge = std::pair<Node, Node>; | ||||||
|  |   using EdgeWeights = std::map<Edge, Weight>; | ||||||
|  |  | ||||||
|  |   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; | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								2015/puzzle-14-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								2015/puzzle-14-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										89
									
								
								2015/puzzle-14-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								2015/puzzle-14-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | using Distance = unsigned long; | ||||||
|  | using Time = unsigned long; | ||||||
|  | using Speed = unsigned long; | ||||||
|  |  | ||||||
|  | struct Reindeer; | ||||||
|  | using ReindeerSet = std::set<Reindeer>; | ||||||
|  |  | ||||||
|  | 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<std::string, unsigned> 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; | ||||||
|  | } | ||||||
							
								
								
									
										123
									
								
								2015/puzzle-15-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								2015/puzzle-15-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | using Score = long; | ||||||
|  | using PropertyMap = std::map<std::string, Score>; | ||||||
|  |  | ||||||
|  | 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<Ingredient>::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<Ingredient> 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; | ||||||
|  | } | ||||||
							
								
								
									
										124
									
								
								2015/puzzle-15-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								2015/puzzle-15-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | using Score = long; | ||||||
|  | using PropertyMap = std::map<std::string, Score>; | ||||||
|  |  | ||||||
|  | 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<Ingredient>::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<Ingredient> 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; | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								2015/puzzle-16-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								2015/puzzle-16-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | using InfoMap = std::map<std::string, unsigned long>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										75
									
								
								2015/puzzle-16-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								2015/puzzle-16-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | using Comparison = std::function<bool(unsigned long, unsigned long)>; | ||||||
|  | using Info = std::pair<unsigned long, Comparison>; | ||||||
|  | using InfoMap = std::map<std::string, Info>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										61
									
								
								2015/puzzle-17-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								2015/puzzle-17-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | using Quantity = unsigned long; | ||||||
|  | constexpr Quantity total = 150; | ||||||
|  |  | ||||||
|  | using Quantities = std::vector<Quantity>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										92
									
								
								2015/puzzle-17-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								2015/puzzle-17-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | using Quantity = unsigned long; | ||||||
|  | constexpr Quantity total = 150; | ||||||
|  |  | ||||||
|  | using Quantities = std::vector<Quantity>; | ||||||
|  |  | ||||||
|  | template <typename Fn1, typename Fn2> | ||||||
|  | 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 <typename Fn1, typename Fn2> | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										102
									
								
								2015/puzzle-18-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								2015/puzzle-18-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <numeric> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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<unsigned char> 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; | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								2015/puzzle-18-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								2015/puzzle-18-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <numeric> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | 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<unsigned char> 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; | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								2015/puzzle-19-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								2015/puzzle-19-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <numeric> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) { | ||||||
|  |   std::multimap<std::string, std::string> 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<std::string> 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; | ||||||
|  | } | ||||||
							
								
								
									
										206
									
								
								2015/puzzle-19-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								2015/puzzle-19-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <cctype> | ||||||
|  | #include <climits> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <numeric> | ||||||
|  | #include <random> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <unordered_map> | ||||||
|  | #include <unordered_set> | ||||||
|  | #include <variant> | ||||||
|  |  | ||||||
|  | template <typename Map> | ||||||
|  | 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::size_t> current_molecules = { | ||||||
|  |       {molecule, steps}}; | ||||||
|  |   while (!current_molecules.empty() && | ||||||
|  |          current_molecules.find("e") == current_molecules.end()) { | ||||||
|  |     std::unordered_map<std::string, std::size_t> 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 <typename Map> | ||||||
|  | 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 <typename Map> | ||||||
|  | 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<std::pair<std::string, std::string>> 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; | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								2015/puzzle-20-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								2015/puzzle-20-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								2015/puzzle-20-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								2015/puzzle-20-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										127
									
								
								2015/puzzle-21-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								2015/puzzle-21-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <numeric> | ||||||
|  |  | ||||||
|  | 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<unsigned>::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<Item> weapons{ | ||||||
|  |             {"Dagger",     8,  4, 0}, | ||||||
|  |             {"Shortsword", 10, 5, 0}, | ||||||
|  |             {"Warhammer",  25, 6, 0}, | ||||||
|  |             {"Longsword",  40, 7, 0}, | ||||||
|  |             {"Greataxe",   74, 8, 0}, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const std::vector<Item> 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<Item> 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'; | ||||||
|  | } | ||||||
							
								
								
									
										130
									
								
								2015/puzzle-21-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								2015/puzzle-21-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <numeric> | ||||||
|  |  | ||||||
|  | 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<unsigned>::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<Item> weapons{ | ||||||
|  |             {"Dagger",     8,  4, 0}, | ||||||
|  |             {"Shortsword", 10, 5, 0}, | ||||||
|  |             {"Warhammer",  25, 6, 0}, | ||||||
|  |             {"Longsword",  40, 7, 0}, | ||||||
|  |             {"Greataxe",   74, 8, 0}, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const std::vector<Item> 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<Item> 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'; | ||||||
|  | } | ||||||
							
								
								
									
										216
									
								
								2015/puzzle-22-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								2015/puzzle-22-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <limits> | ||||||
|  | #include <algorithm> | ||||||
|  |  | ||||||
|  | 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<State> &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<State> states; | ||||||
|  |     states.emplace_back(enemy_hp, enemy_damage); | ||||||
|  |  | ||||||
|  |     /* Cost of the cheapest winning state so far.  */ | ||||||
|  |     auto best_cost{std::numeric_limits<unsigned long>::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; | ||||||
|  | } | ||||||
							
								
								
									
										214
									
								
								2015/puzzle-22-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								2015/puzzle-22-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <limits> | ||||||
|  | #include <algorithm> | ||||||
|  |  | ||||||
|  | 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<State> &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<State> states; | ||||||
|  |     states.emplace_back(enemy_hp, enemy_damage); | ||||||
|  |  | ||||||
|  |     /* Cost of the cheapest winning state so far.  */ | ||||||
|  |     auto best_cost{std::numeric_limits<unsigned long>::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; | ||||||
|  | } | ||||||
							
								
								
									
										129
									
								
								2015/puzzle-23-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								2015/puzzle-23-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <limits> | ||||||
|  | #include <algorithm> | ||||||
|  | #include <map> | ||||||
|  |  | ||||||
|  | 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<std::string, Op> 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<Instr> 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'; | ||||||
|  | } | ||||||
							
								
								
									
										129
									
								
								2015/puzzle-23-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								2015/puzzle-23-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <limits> | ||||||
|  | #include <algorithm> | ||||||
|  | #include <map> | ||||||
|  |  | ||||||
|  | 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<std::string, Op> 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<Instr> 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'; | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								2015/puzzle-24-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								2015/puzzle-24-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <limits> | ||||||
|  | #include <algorithm> | ||||||
|  | #include <map> | ||||||
|  | #include <numeric> | ||||||
|  | #include <set> | ||||||
|  |  | ||||||
|  | struct State { | ||||||
|  |     std::set<unsigned long> 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<unsigned long> weights; | ||||||
|  |     std::string line; | ||||||
|  |     while (std::getline(std::cin, line)) { | ||||||
|  |         weights.emplace_back(std::stoul(line)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::set<State> 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; | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								2015/puzzle-24-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								2015/puzzle-24-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <iostream> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <limits> | ||||||
|  | #include <algorithm> | ||||||
|  | #include <map> | ||||||
|  | #include <numeric> | ||||||
|  | #include <set> | ||||||
|  |  | ||||||
|  | struct State { | ||||||
|  |     std::set<unsigned long> 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<unsigned long> weights; | ||||||
|  |     std::string line; | ||||||
|  |     while (std::getline(std::cin, line)) { | ||||||
|  |         weights.emplace_back(std::stoul(line)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::set<State> 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; | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								2015/puzzle-25-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								2015/puzzle-25-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | #include <cstdint> | ||||||
|  | #include <string> | ||||||
|  | #include <iostream> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								2020/puzzle-01-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								2020/puzzle-01-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) { | ||||||
|  |   std::vector<int> 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; | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								2020/puzzle-01-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								2020/puzzle-01-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) { | ||||||
|  |   std::vector<int> 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; | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								2020/puzzle-02-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								2020/puzzle-02-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								2020/puzzle-02-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								2020/puzzle-02-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								2020/puzzle-03-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								2020/puzzle-03-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								2020/puzzle-03-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								2020/puzzle-03-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | 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] << " <right> <down>\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; | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								2020/puzzle-04-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								2020/puzzle-04-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using StringSet = std::set<std::string>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										187
									
								
								2020/puzzle-04-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								2020/puzzle-04-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <functional> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | // Various types that are useful | ||||||
|  | using StringSet = std::set<std::string>; | ||||||
|  | using StringMap = std::map<std::string, std::string>; | ||||||
|  | using ValidateFn = std::function<bool(std::string const &)>; | ||||||
|  | using ValidateMap = std::map<std::string, ValidateFn>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								2020/puzzle-05-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								2020/puzzle-05-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | /** \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 L2, char LOWER, char UPPER> | ||||||
|  | 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 <unsigned L2R, unsigned L2C> struct Seat { | ||||||
|  |   Seat(std::string const &s) { | ||||||
|  |     assert(s.length() == L2R + L2C); | ||||||
|  |     row_ = find_pos<L2R, 'F', 'B'>(s.substr(0, L2R)); | ||||||
|  |     col_ = find_pos<L2C, 'L', 'R'>(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; | ||||||
|  | } | ||||||
							
								
								
									
										97
									
								
								2020/puzzle-05-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								2020/puzzle-05-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | |||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | /** \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 L2, char LOWER, char UPPER> | ||||||
|  | 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 <unsigned L2R, unsigned L2C> 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<L2R, 'F', 'B'>(s.substr(0, L2R))), | ||||||
|  |         col_(find_pos<L2C, 'L', 'R'>(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<bool, max_id> 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<l2r, l2c> 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; | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								2020/puzzle-06-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								2020/puzzle-06-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | // 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; | ||||||
|  | } | ||||||
							
								
								
									
										61
									
								
								2020/puzzle-06-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								2020/puzzle-06-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <map> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using OccuranceMap = std::map<char, unsigned>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										119
									
								
								2020/puzzle-07-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								2020/puzzle-07-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | // This code is horrible - my only excuse is I wrote it before breakfast | ||||||
|  |  | ||||||
|  | using Node = std::string; | ||||||
|  | using Nodes = std::map<Node, bool>; | ||||||
|  | using Edge = std::pair<Node, Node>; | ||||||
|  | using Weight = unsigned; | ||||||
|  | using EdgeWeights = std::map<Edge, Weight>; | ||||||
|  | using NodeQueue = std::list<Node>; | ||||||
|  |  | ||||||
|  | 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 << "<none> -> " << 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; | ||||||
|  | } | ||||||
							
								
								
									
										84
									
								
								2020/puzzle-07-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								2020/puzzle-07-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | // This code is horrible - my only excuse is I wrote it before breakfast | ||||||
|  |  | ||||||
|  | using Node = std::string; | ||||||
|  | using Edge = std::pair<Node, Node>; | ||||||
|  | using Weight = unsigned; | ||||||
|  | using EdgeWeights = std::map<Edge, Weight>; | ||||||
|  |  | ||||||
|  | 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: <empty>\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; | ||||||
|  | } | ||||||
							
								
								
									
										133
									
								
								2020/puzzle-08-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								2020/puzzle-08-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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<Instruction>; | ||||||
|  |  | ||||||
|  | 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<bool> 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; | ||||||
|  | } | ||||||
							
								
								
									
										167
									
								
								2020/puzzle-08-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								2020/puzzle-08-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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<Instruction>; | ||||||
|  |  | ||||||
|  | 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<bool> 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; | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								2020/puzzle-09-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								2020/puzzle-09-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | template <std::size_t L> 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<unsigned long> 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; | ||||||
|  | } | ||||||
							
								
								
									
										77
									
								
								2020/puzzle-09-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								2020/puzzle-09-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | template <std::size_t L> 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<unsigned long> 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<unsigned long> 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; | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								2020/puzzle-10-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								2020/puzzle-10-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using Jolt = unsigned long; | ||||||
|  |  | ||||||
|  | int main(int argc, char **argv) { | ||||||
|  |   std::vector<Jolt> jolts; | ||||||
|  |   for (std::string line; std::getline(std::cin, line);) { | ||||||
|  |     jolts.push_back(std::stoul(line)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   std::sort(jolts.begin(), jolts.end()); | ||||||
|  |   std::array<unsigned, 4> 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; | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								2020/puzzle-10-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								2020/puzzle-10-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using Jolt = unsigned long; | ||||||
|  |  | ||||||
|  | Jolt count_valid_combinations(std::vector<Jolt> const &jolts) { | ||||||
|  |   std::vector<Jolt> 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<Jolt> 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; | ||||||
|  | } | ||||||
							
								
								
									
										96
									
								
								2020/puzzle-11-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								2020/puzzle-11-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using Array = std::vector<std::string>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										104
									
								
								2020/puzzle-11-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								2020/puzzle-11-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using Array = std::vector<std::string>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										144
									
								
								2020/puzzle-12-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								2020/puzzle-12-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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<Heading>(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; | ||||||
|  | } | ||||||
							
								
								
									
										110
									
								
								2020/puzzle-12-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								2020/puzzle-12-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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<Heading>(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; | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								2020/puzzle-13-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								2020/puzzle-13-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <climits> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								2020/puzzle-13-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								2020/puzzle-13-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <climits> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using Time = unsigned long; | ||||||
|  | using IDPair = std::pair<Time, Time>; | ||||||
|  | using IDVector = std::vector<IDPair>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								2020/puzzle-14-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								2020/puzzle-14-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using Data = std::uint64_t; | ||||||
|  | using Mem = std::vector<Data>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										93
									
								
								2020/puzzle-14-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								2020/puzzle-14-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using Data = std::uint64_t; | ||||||
|  | using Mem = std::map<Data, Data>; | ||||||
|  |  | ||||||
|  | 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<std::size_t> 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; | ||||||
|  | } | ||||||
							
								
								
									
										58
									
								
								2020/puzzle-15-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								2020/puzzle-15-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using NumMap = std::map<int, int>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								2020/puzzle-15-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								2020/puzzle-15-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  | #include <unordered_map> | ||||||
|  |  | ||||||
|  | using NumMap = std::unordered_map<int, int>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										84
									
								
								2020/puzzle-16-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								2020/puzzle-16-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | using ValidVector = std::vector<bool>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										193
									
								
								2020/puzzle-16-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								2020/puzzle-16-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,193 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | 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<unsigned> 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<unsigned> num_to_valid_field_; | ||||||
|  |   std::vector<unsigned> idx_to_valid_field_; | ||||||
|  |   std::vector<std::string> 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; | ||||||
|  | } | ||||||
							
								
								
									
										88
									
								
								2020/puzzle-17-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								2020/puzzle-17-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  |  | ||||||
|  | struct ConwayCubeState { | ||||||
|  |   using Point = std::tuple<int, int, int>; | ||||||
|  |   using PointSet = std::set<Point>; | ||||||
|  |   using NeighbourMap = std::map<Point, unsigned>; | ||||||
|  |  | ||||||
|  |   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; | ||||||
|  | } | ||||||
							
								
								
									
										91
									
								
								2020/puzzle-17-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								2020/puzzle-17-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  |  | ||||||
|  | struct ConwayCubeState { | ||||||
|  |   using Point = std::tuple<int, int, int, int>; | ||||||
|  |   using PointSet = std::set<Point>; | ||||||
|  |   using NeighbourMap = std::map<Point, unsigned>; | ||||||
|  |  | ||||||
|  |   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; | ||||||
|  | } | ||||||
							
								
								
									
										140
									
								
								2020/puzzle-18-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								2020/puzzle-18-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										149
									
								
								2020/puzzle-18-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								2020/puzzle-18-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										96
									
								
								2020/puzzle-19-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								2020/puzzle-19-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  |  | ||||||
|  | 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<std::string> 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; | ||||||
|  | } | ||||||
							
								
								
									
										116
									
								
								2020/puzzle-19-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								2020/puzzle-19-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  |  | ||||||
|  | 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<std::string> 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; | ||||||
|  | } | ||||||
							
								
								
									
										234
									
								
								2020/puzzle-20-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								2020/puzzle-20-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,234 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  |  | ||||||
|  | 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<std::string> 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<std::string> rows_; | ||||||
|  |   bool in_use_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | using Pictures = std::map<Id, Picture>; | ||||||
|  | using HashMap = std::multimap<Hash, Id>; | ||||||
|  | using Array = std::map<std::pair<unsigned, unsigned>, 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; | ||||||
|  | } | ||||||
							
								
								
									
										360
									
								
								2020/puzzle-20-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										360
									
								
								2020/puzzle-20-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,360 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  |  | ||||||
|  | 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<std::string> 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<std::string> 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<Id, Picture>; | ||||||
|  | using HashMap = std::multimap<Hash, Id>; | ||||||
|  | using Array = std::map<std::pair<unsigned, unsigned>, 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; | ||||||
|  | } | ||||||
							
								
								
									
										106
									
								
								2020/puzzle-21-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								2020/puzzle-21-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | using Ingredient = std::string; | ||||||
|  | using Allergen = std::string; | ||||||
|  | using Ingredients = std::set<Ingredient>; | ||||||
|  | using Allergens = std::set<Allergen>; | ||||||
|  | using IngredientInfo = std::pair<unsigned, Allergens>; | ||||||
|  |  | ||||||
|  | using IngredientMap = std::map<Ingredient, IngredientInfo>; | ||||||
|  | using AllergenMap = std::map<Allergen, Ingredients>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										143
									
								
								2020/puzzle-21-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								2020/puzzle-21-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | using Ingredient = std::string; | ||||||
|  | using Allergen = std::string; | ||||||
|  | using Ingredients = std::set<Ingredient>; | ||||||
|  | using Allergens = std::set<Allergen>; | ||||||
|  | using IngredientInfo = std::pair<unsigned, Allergens>; | ||||||
|  |  | ||||||
|  | using IngredientMap = std::map<Ingredient, IngredientInfo>; | ||||||
|  | using AllergenMap = std::map<Allergen, Ingredients>; | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										76
									
								
								2020/puzzle-22-01.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								2020/puzzle-22-01.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | using Card = unsigned; | ||||||
|  | using Cards = std::list<Card>; | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										129
									
								
								2020/puzzle-22-02.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								2020/puzzle-22-02.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | |||||||
|  | #include <algorithm> | ||||||
|  | #include <array> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <list> | ||||||
|  | #include <map> | ||||||
|  | #include <regex> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <tuple> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | using Card = unsigned; | ||||||
|  | using Cards = std::list<Card>; | ||||||
|  | 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<Hash> 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; | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user