Initial commit

This commit is contained in:
2021-12-01 20:31:53 +00:00
commit 2f44ccc3a7
106 changed files with 9333 additions and 0 deletions

16
2015/puzzle-01-01.cc Normal file
View 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
View 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
View 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
View 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
View 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
View 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;
}

View File

@@ -0,0 +1,2 @@
find_package(OpenSSL REQUIRED)
target_link_libraries("${puzzle_name}" OpenSSL::SSL)

40
2015/puzzle-04-01.cc Normal file
View 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;
}

View File

@@ -0,0 +1,2 @@
find_package(OpenSSL REQUIRED)
target_link_libraries("${puzzle_name}" OpenSSL::SSL)

40
2015/puzzle-04-02.cc Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}