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