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

18
2020/puzzle-01-01.cc Normal file
View File

@@ -0,0 +1,18 @@
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char **argv) {
std::vector<int> vals;
for (std::string line; std::getline(std::cin, line);) {
int val = std::stoi(line, nullptr, 10);
for (auto v : vals) {
if (v + val == 2020) {
std::cout << v << " * " << val << " = " << v * val << "\n";
}
}
vals.push_back(val);
}
return 0;
}

24
2020/puzzle-01-02.cc Normal file
View File

@@ -0,0 +1,24 @@
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char **argv) {
std::vector<int> vals;
for (std::string line; std::getline(std::cin, line);) {
int val = std::stoi(line, nullptr, 10);
auto size = vals.size();
for (std::size_t i = 0; i < size; ++i) {
int64_t v1 = vals[i];
for (std::size_t j = 0; j < i; ++j) {
int64_t v2 = vals[j];
if (v1 + v2 + val == 2020) {
std::cout << v1 << " * " << v2 << " * " << val << " = "
<< v1 * v2 * std::int64_t(val) << "\n";
}
}
}
vals.push_back(val);
}
return 0;
}

51
2020/puzzle-02-01.cc Normal file
View File

@@ -0,0 +1,51 @@
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
struct PasswordChecker {
PasswordChecker(std::string const &s) {
std::size_t pos = 0;
min_ = std::stoul(s, &pos, 10);
assert(s[pos] == '-');
std::string s2 = s.substr(pos + 1);
max_ = std::stoul(s2, &pos, 10);
assert(s2[pos] == ' ');
c_ = s2[pos + 1];
assert(s2[pos + 2] == ':');
assert(s2[pos + 3] == ' ');
password_ = s2.substr(pos + 4);
}
bool is_valid() const {
std::string::size_type count = 0;
for (auto c : password_) {
if (c == c_) {
++count;
}
}
return (count >= min_) && (count <= max_);
}
std::string::size_type min_;
std::string::size_type max_;
std::string::value_type c_;
std::string password_;
};
int main(int argc, char **argv) {
unsigned count = 0;
for (std::string line; std::getline(std::cin, line);) {
PasswordChecker check(line);
if (check.is_valid()) {
++count;
} else {
std::cout << "IN";
}
std::cout << "VALID: " << line << "\n";
}
std::cout << "Number of valid: " << count << "\n";
return 0;
}

51
2020/puzzle-02-02.cc Normal file
View File

@@ -0,0 +1,51 @@
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
struct PasswordChecker {
PasswordChecker(std::string const &s) {
std::size_t pos = 0;
pos1_ = std::stoul(s, &pos, 10);
assert(s[pos] == '-');
std::string s2 = s.substr(pos + 1);
pos2_ = std::stoul(s2, &pos, 10);
assert(s2[pos] == ' ');
c_ = s2[pos + 1];
assert(s2[pos + 2] == ':');
assert(s2[pos + 3] == ' ');
password_ = s2.substr(pos + 4);
}
bool is_valid() const {
std::string::size_type count = 0;
if (password_.at(pos1_ - 1) == c_) {
++count;
}
if (password_.at(pos2_ - 1) == c_) {
++count;
}
return count == 1;
}
std::string::size_type pos1_; // 1 based
std::string::size_type pos2_; // 1 based
std::string::value_type c_;
std::string password_;
};
int main(int argc, char **argv) {
unsigned count = 0;
for (std::string line; std::getline(std::cin, line);) {
PasswordChecker check(line);
if (check.is_valid()) {
++count;
} else {
std::cout << "IN";
}
std::cout << "VALID: " << line << "\n";
}
std::cout << "Number of valid: " << count << "\n";
return 0;
}

22
2020/puzzle-03-01.cc Normal file
View File

@@ -0,0 +1,22 @@
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
std::string rotate(std::string const &s, std::string::size_type amount) {
auto length = s.length();
return (s + s).substr(amount % length, length);
}
int main(int argc, char **argv) {
unsigned count = 0;
std::string::size_type rotation = 0;
for (std::string line; std::getline(std::cin, line);) {
auto rotated = rotate(line, rotation);
rotation += 3;
count += (rotated[0] == '#');
}
std::cout << "Number of trees: " << count << "\n";
return 0;
}

42
2020/puzzle-03-02.cc Normal file
View File

@@ -0,0 +1,42 @@
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
std::string rotate(std::string const &s, std::string::size_type amount) {
auto length = s.length();
return (s + s).substr(amount % length, length);
}
int main(int argc, char **argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <right> <down>\n";
return 1;
}
int right = atoi(argv[1]);
int down = atoi(argv[2]);
if (right <= 0) {
std::cerr << "Right is less than 1\n";
return 1;
}
if (down <= 0) {
std::cerr << "Down is less than 1\n";
return 1;
}
unsigned count = 0;
int row = -1;
std::string::size_type rotation = 0;
for (std::string line; std::getline(std::cin, line);) {
++row;
if ((row % down) != 0) {
continue;
}
auto rotated = rotate(line, rotation);
rotation += right;
count += (rotated[0] == '#');
}
std::cout << count << "\n";
return 0;
}

73
2020/puzzle-04-01.cc Normal file
View File

@@ -0,0 +1,73 @@
#include <cassert>
#include <iostream>
#include <set>
#include <string>
using StringSet = std::set<std::string>;
namespace {
StringSet parse_line(std::string const &str) {
StringSet result;
std::string::size_type pos = 0;
while (pos != std::string::npos) {
while (pos < str.length() && str.at(pos) == ' ') {
++pos;
}
auto end_pos = str.find(' ', pos);
auto colon_pos = str.find(':', pos);
assert(colon_pos != std::string::npos);
assert(colon_pos < end_pos);
auto len = colon_pos - pos;
result.insert(str.substr(pos, len));
pos = end_pos;
}
return result;
}
const StringSet mandatory{"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"};
const StringSet optional{"cid"};
bool is_valid_passport(StringSet const &mandatory_found) {
return mandatory_found.size() == mandatory.size();
}
} // namespace
int main(int argc, char **argv) {
StringSet mandatory_found;
StringSet optional_found;
unsigned valid = 0;
for (std::string line; std::getline(std::cin, line);) {
if (!line.empty()) {
auto keys = parse_line(line);
for (auto const &key : keys) {
std::cout << "Key " << key << " is ";
if (mandatory.find(key) != mandatory.end()) {
auto [it, success] = mandatory_found.insert(key);
std::cout << "mandatory";
if (!success) {
std::cout << " (duplicate)";
}
} else if (optional.find(key) != optional.end()) {
auto [it, success] = optional_found.insert(key);
std::cout << "optional";
if (!success) {
std::cout << " (duplicate)";
}
} else {
std::cout << "unexpected";
}
std::cout << "\n";
}
} else {
valid += is_valid_passport(mandatory_found);
mandatory_found.clear();
optional_found.clear();
}
}
valid += is_valid_passport(mandatory_found);
std::cout << valid << "\n";
return 0;
}

187
2020/puzzle-04-02.cc Normal file
View File

@@ -0,0 +1,187 @@
#include <cassert>
#include <functional>
#include <iostream>
#include <map>
#include <set>
#include <string>
// Various types that are useful
using StringSet = std::set<std::string>;
using StringMap = std::map<std::string, std::string>;
using ValidateFn = std::function<bool(std::string const &)>;
using ValidateMap = std::map<std::string, ValidateFn>;
namespace {
/** \brief Parse a string into a map of key/value pairs.
* \param str String to parse
* \return Map
*
* \a str is expected to look like a space separated list of pairs where each
* pair is of the form 'key:value'.
*/
StringMap parse_line(std::string const &str) {
StringMap result;
std::string::size_type pos = 0;
while (pos != std::string::npos) {
while (pos < str.length() && str.at(pos) == ' ') {
++pos;
}
auto end_pos = str.find(' ', pos);
auto colon_pos = str.find(':', pos);
assert(colon_pos != std::string::npos);
assert(colon_pos < end_pos);
auto key_len = colon_pos - pos;
auto value_len = (end_pos == std::string::npos)
? std::string::npos
: (end_pos - (colon_pos + 1));
auto key = str.substr(pos, key_len);
auto value = str.substr(colon_pos + 1, value_len);
result.insert({key, value});
pos = end_pos;
}
return result;
}
/** \brief Validate a year
* \param s String to validate
* \param min Minimum year to accept
* \param max Maximum year to accept
* \return True if \a s is in range [min, max]
*/
bool validate_year(std::string const &s, unsigned min, unsigned max) {
if (s.length() != 4) {
return false;
}
std::size_t pos = 0;
auto yr = std::stoul(s, &pos, 10);
if (pos != s.length()) {
return false;
}
return yr >= min && yr <= max;
}
/// Validate byr field
bool validate_byr(std::string const &s) { return validate_year(s, 1920, 2002); }
/// Validate iyr field
bool validate_iyr(std::string const &s) { return validate_year(s, 2010, 2020); }
/// Validate eyr field
bool validate_eyr(std::string const &s) { return validate_year(s, 2020, 2030); }
/// Validate hgt field
bool validate_hgt(std::string const &s) {
std::size_t pos = 0;
auto hgt = std::stoul(s, &pos, 10);
if (pos != s.length() - 2) {
return false;
}
if (s[pos] == 'c' && s[pos + 1] == 'm') {
return hgt >= 150 && hgt <= 193;
} else if (s[pos] == 'i' && s[pos + 1] == 'n') {
return hgt >= 59 && hgt <= 76;
}
return false;
}
/** \brief Validate a string contains valid characters only
* \param s String to check
* \param len Required length of string
* \param cs Valid characters
* \return True iff we pass validation
*/
bool validate_chars(std::string const &s, std::string::size_type len,
std::string const &cs) {
if (s.length() != len) {
return false;
}
for (auto c : s) {
if (cs.find(c) == std::string::npos) {
return false;
}
}
return true;
}
/// Validate hcl field
bool validate_hcl(std::string const &s) {
if (s.length() != 7) {
return false;
}
if (s[0] != '#') {
return false;
}
return validate_chars(s.substr(1), 6, "0123456789abcdef");
}
/// Validate ecl field
bool validate_ecl(std::string const &s) {
static const StringSet valid = {"amb", "blu", "brn", "gry",
"grn", "hzl", "oth"};
return valid.find(s) != valid.end();
}
/// Validate pid field
bool validate_pid(std::string const &s) {
return validate_chars(s, 9, "0123456789");
}
/// Validate cid field
bool validate_cid(std::string const &s) { return true; }
/// Check if a passport is valid
///
/// A passport is valid if it contains all mandatory fields, and passes
/// validation on all fields.
bool is_valid_passport(StringMap const &found) {
static const StringSet mandatory{"byr", "iyr", "eyr", "hgt",
"hcl", "ecl", "pid"};
static const ValidateMap validators{
{"byr", validate_byr}, {"iyr", validate_iyr}, {"eyr", validate_eyr},
{"hgt", validate_hgt}, {"hcl", validate_hcl}, {"ecl", validate_ecl},
{"pid", validate_pid}, {"cid", validate_cid},
};
unsigned mandatory_found = 0;
for (auto const &kv : found) {
if (mandatory.find(kv.first) != mandatory.end()) {
++mandatory_found;
}
auto validator = validators.find(kv.first);
if (validator == validators.end()) {
std::cout << "Found invalid key: " << kv.first << "\n";
} else {
auto valid = validator->second(kv.second);
if (!valid) {
std::cout << "Invalid value for key: " << kv.first << ": " << kv.second
<< "\n";
return false;
}
}
}
return mandatory_found == mandatory.size();
}
} // namespace
int main(int argc, char **argv) {
StringMap found;
unsigned valid = 0;
for (std::string line; std::getline(std::cin, line);) {
if (!line.empty()) {
auto keys = parse_line(line);
found.insert(keys.begin(), keys.end());
} else {
valid += is_valid_passport(found);
found.clear();
}
}
valid += is_valid_passport(found);
std::cout << valid << "\n";
return 0;
}

60
2020/puzzle-05-01.cc Normal file
View File

@@ -0,0 +1,60 @@
#include <cassert>
#include <iostream>
#include <string>
/** \brief Return the position described by BSP in s.
* \tparam L2 log2 of the max position.
* \tparam LOWER character which means use lower half.
* \tparam UPPER character which means use upper half.
* \param s String to parse, must be L2 characters long
* \return Position described by s.
*/
template <unsigned L2, char LOWER, char UPPER>
unsigned find_pos(std::string const &s) {
assert(s.length() == L2);
unsigned low = 0;
unsigned high = 1 << L2;
unsigned width = 1 << (L2 - 1);
for (auto c : s) {
assert(high != low);
assert(width != 0);
if (c == LOWER) {
high = low + width;
} else if (c == UPPER) {
low = low + width;
} else {
assert(false);
}
width >>= 1;
}
assert(low == high - 1);
return low;
}
template <unsigned L2R, unsigned L2C> struct Seat {
Seat(std::string const &s) {
assert(s.length() == L2R + L2C);
row_ = find_pos<L2R, 'F', 'B'>(s.substr(0, L2R));
col_ = find_pos<L2C, 'L', 'R'>(s.substr(L2R, L2C));
std::cout << s << ": row " << row_ << ", column " << col_ << ", seat ID "
<< id() << ".\n";
}
unsigned id() const noexcept { return row_ * (1 << L2C) + col_; }
unsigned row_;
unsigned col_;
};
int main(int argc, char **argv) {
unsigned max_id = 0;
for (std::string line; std::getline(std::cin, line);) {
Seat<7, 3> seat(line);
max_id = std::max(max_id, seat.id());
}
std::cout << "Max ID: " << max_id << "\n";
return 0;
}

97
2020/puzzle-05-02.cc Normal file
View File

@@ -0,0 +1,97 @@
#include <array>
#include <cassert>
#include <iostream>
#include <string>
/** \brief Return the position described by BSP in s.
* \tparam L2 log2 of the max position.
* \tparam LOWER character which means use lower half.
* \tparam UPPER character which means use upper half.
* \param s String to parse, must be L2 characters long
* \return Position described by s.
*
* \subsection After the fact commentary:
*
* We don't need to keep a record of high at all so the original solution
* was over enthusiastic.
*/
template <unsigned L2, char LOWER, char UPPER>
unsigned find_pos(std::string const &s) {
assert(s.length() == L2);
unsigned low = 0;
unsigned width = 1 << (L2 - 1);
for (auto c : s) {
assert(width != 0);
if (c == UPPER) {
low = low + width;
} else {
assert(c == LOWER);
}
width >>= 1;
}
assert(width == 0);
return low;
}
/** \brief A seat
* \tparam L2R log 2 number of rows
* \tparam L2C log 2 number of columns
*/
template <unsigned L2R, unsigned L2C> struct Seat {
/** \brief Construct the seat.
* \param s String representing seat location, must be L2R + L2C chars.
*
* First L2R characters must be 'F' and 'B' representing row, last L2C
* characters must by 'L', and 'R' representing column.
*
* \brief After the fact commentary:
*
* Should have used initialiser lists.
*/
Seat(std::string const &s)
: row_(find_pos<L2R, 'F', 'B'>(s.substr(0, L2R))),
col_(find_pos<L2C, 'L', 'R'>(s.substr(L2R, L2C))) {
assert(s.length() == L2R + L2C);
std::cout << s << ": row " << row_ << ", column " << col_ << ", seat ID "
<< id() << ".\n";
}
/** Get seat ID. */
unsigned id() const noexcept { return row_ * (1 << L2C) + col_; }
unsigned row_; ///< Seat row
unsigned col_; ///< Seat column
};
/** \brief main
*
* \subsection After the fact commentary:
*
* Could have initialised the array better.
*/
int main(int argc, char **argv) {
constexpr unsigned l2r = 7; ///< Log 2 number of rows
constexpr unsigned l2c = 3; ///< Log 2 number of columns
constexpr unsigned max_id = 1 << (l2r + l2c); ///< Max ID.
std::array<bool, max_id> id_present = {}; ///< Is the ID present?
// Read the lines in marking IDs that are present
for (std::string line; std::getline(std::cin, line);) {
Seat<l2r, l2c> seat(line);
assert(seat.id() < max_id);
id_present[seat.id()] = true;
}
// We have an empty seat if it's ID is not present, but the IDs at +/-1 are.
for (unsigned id = 1; id < id_present.size() - 1; ++id) {
if (!id_present[id] && id_present[id - 1] && id_present[id + 1]) {
std::cout << "Empty seat at: " << id << "\n";
}
}
return 0;
}

53
2020/puzzle-06-01.cc Normal file
View File

@@ -0,0 +1,53 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <string>
// Merge two strings of answers, preserving order and ensuring uniqueness of
// each answer.
std::string merge_answers(std::string const &a, std::string const &b) {
std::string result;
std::string::size_type pos_a = 0;
std::string::size_type pos_b = 0;
while (pos_a < a.length() && pos_b < b.length()) {
if (a[pos_a] < b[pos_b]) {
result += a[pos_a];
++pos_a;
} else if (b[pos_b] < a[pos_a]) {
result += b[pos_b];
++pos_b;
} else {
assert(a[pos_a] == b[pos_b]);
result += a[pos_a];
++pos_a;
++pos_b;
}
}
result += a.substr(pos_a);
result += b.substr(pos_b);
std::cout << "Merged " << a << " and " << b << " to " << result << "\n";
return result;
}
int main(int argc, char **argv) {
std::string current_answers;
unsigned count = 0;
for (std::string line; std::getline(std::cin, line);) {
if (!line.empty()) {
std::sort(line.begin(), line.end());
current_answers = merge_answers(current_answers, line);
} else {
std::cout << "Length of " << current_answers << " = "
<< current_answers.length() << "\n";
count += current_answers.length();
current_answers.clear();
}
}
std::cout << "Length of " << current_answers << " = "
<< current_answers.length() << "\n";
count += current_answers.length();
std::cout << "Total length = " << count << "\n";
return 0;
}

61
2020/puzzle-06-02.cc Normal file
View File

@@ -0,0 +1,61 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <map>
#include <string>
using OccuranceMap = std::map<char, unsigned>;
struct GroupAnswers {
void add_answers(std::string const &a) {
// Increase group size
++group_size_;
// Add answers
for (auto c : a) {
auto it = map_.find(c);
if (it == map_.end()) {
map_.insert({c, 1});
} else {
++(it->second);
}
}
}
// Count the number of answered questions answered by everyone.
unsigned all_answer_count() const noexcept {
unsigned count = 0;
for (auto kv : map_) {
if (kv.second == group_size_) {
++count;
}
}
return count;
}
void clear() {
map_.clear();
group_size_ = 0;
}
OccuranceMap map_;
unsigned group_size_ = 0;
};
int main(int argc, char **argv) {
GroupAnswers answers;
unsigned count = 0;
for (std::string line; std::getline(std::cin, line);) {
if (!line.empty()) {
answers.add_answers(line);
} else {
count += answers.all_answer_count();
answers.clear();
}
}
count += answers.all_answer_count();
std::cout << "Total length = " << count << "\n";
return 0;
}

119
2020/puzzle-07-01.cc Normal file
View File

@@ -0,0 +1,119 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
// This code is horrible - my only excuse is I wrote it before breakfast
using Node = std::string;
using Nodes = std::map<Node, bool>;
using Edge = std::pair<Node, Node>;
using Weight = unsigned;
using EdgeWeights = std::map<Edge, Weight>;
using NodeQueue = std::list<Node>;
std::ostream &operator<<(std::ostream &os, NodeQueue const &q) {
std::string p = "";
os << "[";
for (auto const &n : q) {
os << p << n;
p = ", ";
}
os << "]";
return os;
}
struct BagGraph {
BagGraph() { nodes_.insert({"", false}); }
void add_edges(std::string const &s) {
static const auto prefix_re = std::regex("([a-z ]+) bags? contain ");
static const auto suffix_re = std::regex("(\\d)+ ([a-z ]+) bags?[,.]\\s*");
static const auto empty_str = std::string("no other bags.");
std::smatch prefix_m;
if (!std::regex_search(s, prefix_m, prefix_re)) {
std::cout << "Failed to match: " << s << "\n";
assert(false);
return;
}
std::string n2 = prefix_m.str(1);
nodes_.insert({n2, false});
std::string suffix = prefix_m.suffix();
while (!suffix.empty()) {
if (suffix == empty_str) {
suffix = "";
Edge e = std::make_pair("", n2);
edges_.insert({e, 0});
std::cout << "<none> -> " << n2 << ": 0\n";
} else {
std::smatch suffix_m;
if (!std::regex_search(suffix, suffix_m, suffix_re)) {
std::cout << "Failed to match: " << suffix << "\n";
assert(false);
return;
}
Weight w = std::stoul(suffix_m.str(1));
Node n1 = suffix_m.str(2);
suffix = suffix_m.suffix();
Edge e = std::make_pair(n1, n2);
nodes_.insert({n1, false});
edges_.insert({e, w});
std::cout << n1 << " -> " << n2 << ": " << w << "\n";
}
}
}
unsigned num_containers(Node const &n) {
NodeQueue queue;
// Initial population of queue:
populate_queue(queue, n);
std::cout << "Initial queue: " << queue << "\n";
unsigned visited = 0;
// Iterate over queue finding how
while (!queue.empty()) {
Node n1 = queue.front();
std::cout << "Processing: " << n1 << "\n";
queue.pop_front();
if (!nodes_[n1]) {
nodes_[n1] = true;
populate_queue(queue, n1);
++visited;
std::cout << "Updated queue: " << queue << "\n";
} else {
std::cout << "Already visited\n";
}
}
return visited;
}
private:
void populate_queue(NodeQueue &queue, Node const &n) {
for (auto const &kv : edges_) {
if (kv.first.first == n && !nodes_[kv.first.second]) {
queue.push_back(kv.first.second);
}
}
}
EdgeWeights edges_;
Nodes nodes_;
};
int main(int argc, char **argv) {
BagGraph bg;
for (std::string line; std::getline(std::cin, line);) {
bg.add_edges(line);
}
Node n = "shiny gold";
auto visited = bg.num_containers(n);
std::cout << "Number of " << n << " containers " << visited << "\n";
return 0;
}

84
2020/puzzle-07-02.cc Normal file
View File

@@ -0,0 +1,84 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
// This code is horrible - my only excuse is I wrote it before breakfast
using Node = std::string;
using Edge = std::pair<Node, Node>;
using Weight = unsigned;
using EdgeWeights = std::map<Edge, Weight>;
struct BagGraph {
void add_edges(std::string const &s) {
static const auto prefix_re = std::regex("([a-z ]+) bags? contain ");
static const auto suffix_re = std::regex("(\\d)+ ([a-z ]+) bags?[,.]\\s*");
static const auto empty_str = std::string("no other bags.");
std::smatch prefix_m;
if (!std::regex_search(s, prefix_m, prefix_re)) {
std::cout << "Failed to match: " << s << "\n";
assert(false);
return;
}
std::string n1 = prefix_m.str(1);
std::string suffix = prefix_m.suffix();
while (!suffix.empty()) {
if (suffix == empty_str) {
std::cout << n1 << " -> none: <empty>\n";
suffix.clear();
} else {
std::smatch suffix_m;
if (!std::regex_search(suffix, suffix_m, suffix_re)) {
std::cout << "Failed to match: " << suffix << "\n";
assert(false);
return;
}
Weight w = std::stoul(suffix_m.str(1));
Node n2 = suffix_m.str(2);
suffix = suffix_m.suffix();
Edge e = std::make_pair(n1, n2);
edges_.insert({e, w});
std::cout << n1 << " -> " << n2 << ": " << w << "\n";
}
}
}
Weight num_contained(Node const &n) {
// Get the number of bags including the node we've been asked for so the
// result is 1 less than the internal result
return num_contained1(n) - 1;
}
private:
Weight num_contained1(Node const &n) {
Weight w = 0;
bool contains_something = false;
for (auto const &kv : edges_) {
if (kv.first.first == n) {
w += kv.second * num_contained1(kv.first.second);
contains_something = true;
}
}
return w + 1;
}
EdgeWeights edges_;
};
int main(int argc, char **argv) {
BagGraph bg;
for (std::string line; std::getline(std::cin, line);) {
bg.add_edges(line);
}
Node n = "shiny gold";
auto contained = bg.num_contained(n);
std::cout << n << " contains " << contained << " bags.\n";
return 0;
}

133
2020/puzzle-08-01.cc Normal file
View File

@@ -0,0 +1,133 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
enum class Opcode { Acc, Jmp, Nop };
using Value = int;
struct Instruction {
Instruction(std::string const &s)
: op_(to_opcode(s.substr(0, 3))), v_(to_value(s.substr(4))) {}
Opcode opcode() const noexcept { return op_; }
Value value() const noexcept { return v_; }
private:
Opcode to_opcode(std::string const &s) {
if (s == "acc") {
return Opcode::Acc;
} else if (s == "jmp") {
return Opcode::Jmp;
} else if (s == "nop") {
return Opcode::Nop;
} else {
assert(false);
return Opcode::Nop;
}
}
Value to_value(std::string const &s) {
int sign = 0;
int v = 0;
std::size_t pos = 0;
if (s[0] == '+') {
sign = 1;
} else if (s[0] == '-') {
sign = -1;
} else {
assert(false);
}
v = std::stoi(s.substr(1), &pos);
assert(pos == s.length() - 1);
return v * sign;
}
Opcode op_;
Value v_;
};
std::ostream &operator<<(std::ostream &os, Instruction const &i) {
switch (i.opcode()) {
case Opcode::Acc:
os << "acc";
break;
case Opcode::Jmp:
os << "jmp";
break;
case Opcode::Nop:
os << "nop";
break;
default:
os << "UNK";
break;
}
os << " ";
if (i.value() >= 0) {
os << "+";
}
os << i.value();
return os;
}
using Instructions = std::vector<Instruction>;
struct VM {
VM() : pc_(0), acc_(0) {}
void add_instruction(Instruction const &i) {
std::cout << i << "\n";
instrs_.push_back(i);
}
void execute() {
std::vector<bool> seen(instrs_.size(), false);
while (!seen[pc_]) {
assert(pc_ < instrs_.size());
seen[pc_] = true;
execute(instrs_[pc_]);
++pc_;
}
std::cout << "PC seen before: " << pc_ << "\n";
}
Value acc() const noexcept { return acc_; }
private:
void execute(Instruction const &i) {
std::cout << pc_ << ": " << i;
switch (i.opcode()) {
case Opcode::Acc:
std::cout << "; acc = " << acc_ << " + " << i.value();
acc_ += i.value();
std::cout << " = " << acc_;
break;
case Opcode::Jmp:
std::cout << "; pc = " << pc_ << " + " << i.value();
pc_ += i.value() - 1;
std::cout << " = " << pc_ + 1;
break;
case Opcode::Nop:
break;
}
std::cout << "\n";
}
Instructions instrs_;
std::size_t pc_;
Value acc_;
};
int main(int argc, char **argv) {
VM vm;
for (std::string line; std::getline(std::cin, line);) {
vm.add_instruction(Instruction(line));
}
vm.execute();
std::cout << "Accumulator: " << vm.acc() << "\n";
return 0;
}

167
2020/puzzle-08-02.cc Normal file
View File

@@ -0,0 +1,167 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
enum class Opcode { Acc, Jmp, Nop };
using Value = int;
struct Instruction {
Instruction(std::string const &s)
: op_(to_opcode(s.substr(0, 3))), v_(to_value(s.substr(4))) {}
Opcode opcode() const noexcept { return op_; }
Value value() const noexcept { return v_; }
bool flip_jmp() {
switch (op_) {
case Opcode::Acc:
return false;
case Opcode::Jmp:
op_ = Opcode::Nop;
return true;
case Opcode::Nop:
op_ = Opcode::Jmp;
return true;
default:
assert(false);
}
}
private:
Opcode to_opcode(std::string const &s) {
if (s == "acc") {
return Opcode::Acc;
} else if (s == "jmp") {
return Opcode::Jmp;
} else if (s == "nop") {
return Opcode::Nop;
} else {
assert(false);
return Opcode::Nop;
}
}
Value to_value(std::string const &s) {
int sign = 0;
int v = 0;
std::size_t pos = 0;
if (s[0] == '+') {
sign = 1;
} else if (s[0] == '-') {
sign = -1;
} else {
assert(false);
}
v = std::stoi(s.substr(1), &pos);
assert(pos == s.length() - 1);
return v * sign;
}
Opcode op_;
Value v_;
};
std::ostream &operator<<(std::ostream &os, Instruction const &i) {
switch (i.opcode()) {
case Opcode::Acc:
os << "acc";
break;
case Opcode::Jmp:
os << "jmp";
break;
case Opcode::Nop:
os << "nop";
break;
default:
os << "UNK";
break;
}
os << " ";
if (i.value() >= 0) {
os << "+";
}
os << i.value();
return os;
}
using Instructions = std::vector<Instruction>;
struct VM {
VM() : pc_(0), acc_(0) {}
void add_instruction(Instruction const &i) {
std::cout << i << "\n";
instrs_.push_back(i);
}
bool execute() {
std::vector<bool> seen(instrs_.size(), false);
acc_ = 0;
pc_ = 0;
while (!seen[pc_]) {
assert(pc_ < instrs_.size());
seen[pc_] = true;
execute(instrs_[pc_]);
++pc_;
if (pc_ == instrs_.size()) {
std::cout << "Terminated\n";
return true;
}
}
std::cout << "PC seen before: " << pc_ << "\n";
return false;
}
void find_fix() {
for (std::size_t pos = 0; pos < instrs_.size(); ++pos) {
if (instrs_[pos].flip_jmp()) {
if (execute()) {
std::cout << "Success at instruction " << pos << ": " << instrs_[pos]
<< "\n";
return;
}
instrs_[pos].flip_jmp();
}
}
}
Value acc() const noexcept { return acc_; }
private:
void execute(Instruction const &i) {
std::cout << pc_ << ": " << i;
switch (i.opcode()) {
case Opcode::Acc:
std::cout << "; acc = " << acc_ << " + " << i.value();
acc_ += i.value();
std::cout << " = " << acc_;
break;
case Opcode::Jmp:
std::cout << "; pc = " << pc_ << " + " << i.value();
pc_ += i.value() - 1;
std::cout << " = " << pc_ + 1;
break;
case Opcode::Nop:
break;
}
std::cout << "\n";
}
Instructions instrs_;
std::size_t pc_;
Value acc_;
};
int main(int argc, char **argv) {
VM vm;
for (std::string line; std::getline(std::cin, line);) {
vm.add_instruction(Instruction(line));
}
vm.find_fix();
std::cout << "Accumulator: " << vm.acc() << "\n";
return 0;
}

53
2020/puzzle-09-01.cc Normal file
View File

@@ -0,0 +1,53 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
template <std::size_t L> struct Buffer {
Buffer() {}
bool add_value(std::string const &s) {
unsigned long i = std::stoul(s);
auto size = buf_.size();
if (size == L && !valid(i)) {
return false;
}
if (size == L) {
buf_.pop_front();
}
buf_.push_back(i);
return true;
}
private:
bool valid(unsigned long v) const {
assert(buf_.size() == L);
for (auto i = buf_.begin(); i != buf_.end(); ++i) {
auto j = i;
for (++j; j != buf_.end(); ++j) {
if (v == *i + *j) {
std::cout << "v = "
<< " " << *i << " + " << *j << "\n";
return true;
}
}
}
return false;
}
std::list<unsigned long> buf_;
};
int main(int argc, char **argv) {
Buffer<25> buf;
for (std::string line; std::getline(std::cin, line);) {
if (!buf.add_value(line)) {
std::cout << "Invalid: " << line << "\n";
return 0;
}
}
return 0;
}

77
2020/puzzle-09-02.cc Normal file
View File

@@ -0,0 +1,77 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
template <std::size_t L> struct Buffer {
Buffer() {}
bool add_value(std::string const &s) {
unsigned long i = std::stoul(s);
bool result = true;
auto size = buf_.size();
if (size >= L) {
result = valid(i);
}
buf_.push_back(i);
return result;
}
unsigned long get_weakness(unsigned long v) {
std::size_t sum_begin = 0;
std::size_t sum_end = 0;
unsigned long sum = 0;
while (sum != v) {
if (sum < v) {
assert(sum_end != buf_.size());
sum += buf_[sum_end];
++sum_end;
}
if (sum > v) {
assert(sum_begin != buf_.size());
sum -= buf_[sum_begin];
++sum_begin;
}
}
std::cout << "sum_begin = " << sum_begin << " sum_end = " << sum_end
<< "\n";
std::vector<unsigned long> vec(buf_.begin() + sum_begin,
buf_.begin() + sum_end);
std::sort(vec.begin(), vec.end());
return vec[0] + vec[vec.size() - 1];
}
private:
bool valid(unsigned long v) const {
assert(buf_.size() >= L);
for (auto i = buf_.size() - 25; i != buf_.size(); ++i) {
for (auto j = i + 1; j != buf_.size(); ++j) {
if (v == buf_[i] + buf_[j]) {
return true;
}
}
}
return false;
}
std::vector<unsigned long> buf_;
};
int main(int argc, char **argv) {
Buffer<25> buf;
unsigned long invalid = 0;
for (std::string line; std::getline(std::cin, line);) {
if (!buf.add_value(line)) {
if (invalid == 0) {
invalid = std::stoul(line);
}
}
}
std::cout << "First Invalid: " << invalid << "\n";
std::cout << "Weakness: " << buf.get_weakness(invalid) << "\n";
return 0;
}

32
2020/puzzle-10-01.cc Normal file
View File

@@ -0,0 +1,32 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using Jolt = unsigned long;
int main(int argc, char **argv) {
std::vector<Jolt> jolts;
for (std::string line; std::getline(std::cin, line);) {
jolts.push_back(std::stoul(line));
}
std::sort(jolts.begin(), jolts.end());
std::array<unsigned, 4> gaps = {};
Jolt last = 0;
for (auto j : jolts) {
++gaps[j - last];
last = j;
}
// and the gap to our voltage
++gaps[3];
std::cout << gaps[1] << " * " << gaps[3] << " = " << gaps[1] * gaps[3]
<< "\n";
return 0;
}

43
2020/puzzle-10-02.cc Normal file
View File

@@ -0,0 +1,43 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using Jolt = unsigned long;
Jolt count_valid_combinations(std::vector<Jolt> const &jolts) {
std::vector<Jolt> valid(jolts.size(), 0);
valid[jolts.size() - 1] = 1;
std::size_t i = jolts.size() - 1;
do {
auto i2 = i;
--i;
valid[i] = 0;
while (i2 < jolts.size() && jolts[i2] < jolts[i] + 4) {
valid[i] += valid[i2];
++i2;
}
std::cout << jolts[i] << ": " << valid[i] << "\n";
} while (i > 0);
return valid[0];
}
int main(int argc, char **argv) {
std::vector<Jolt> jolts;
for (std::string line; std::getline(std::cin, line);) {
jolts.push_back(std::stoul(line));
}
jolts.push_back(0);
std::sort(jolts.begin(), jolts.end());
jolts.push_back(jolts[jolts.size() - 1] + 3);
auto combinations = count_valid_combinations(jolts);
std::cout << "Number of valid combinations: " << combinations << "\n";
return 0;
}

96
2020/puzzle-11-01.cc Normal file
View File

@@ -0,0 +1,96 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using Array = std::vector<std::string>;
char next_state(Array const &a, std::size_t row, std::size_t col) {
unsigned occupied = 0;
if (row > 0) {
if (col > 0) {
occupied += (a[row - 1][col - 1] == '#');
}
occupied += a[row - 1][col] == '#';
if (col < a[row - 1].size() - 1) {
occupied += (a[row - 1][col + 1] == '#');
}
}
if (col > 0) {
occupied += (a[row][col - 1] == '#');
}
if (col < a[row].size() - 1) {
occupied += (a[row][col + 1] == '#');
}
if (row < a.size() - 1) {
if (col > 0) {
occupied += (a[row + 1][col - 1] == '#');
}
occupied += a[row + 1][col] == '#';
if (col < a[row + 1].size() - 1) {
occupied += (a[row + 1][col + 1] == '#');
}
}
if (a[row][col] == 'L' && occupied == 0) {
return '#';
} else if (a[row][col] == '#' && occupied >= 4) {
return 'L';
} else {
return a[row][col];
}
}
unsigned long count_occupied(Array const &a) {
unsigned long count = 0;
for (auto const &row : a) {
for (auto col : row) {
count += (col == '#');
}
}
return count;
}
unsigned long run_to_steady_state(Array const &a) {
Array current = a;
bool changed = true;
unsigned i = 0;
while (changed) {
std::cout << "------ iter = " << i++ << "\n";
changed = false;
Array next;
for (std::size_t row = 0; row < current.size(); ++row) {
std::string s = "";
for (std::size_t col = 0; col < current[row].size(); ++col) {
char c = next_state(current, row, col);
s.push_back(c);
if (c != current[row][col]) {
changed = true;
}
}
next.push_back(s);
std::cout << s << "\n";
}
current = next;
}
return count_occupied(current);
}
int main(int argc, char **argv) {
Array array;
for (std::string line; std::getline(std::cin, line);) {
array.push_back(line);
std::cout << line << "\n";
}
unsigned long result = run_to_steady_state(array);
std::cout << "Number of occupied states: " << result << "\n";
return 0;
}

104
2020/puzzle-11-02.cc Normal file
View File

@@ -0,0 +1,104 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using Array = std::vector<std::string>;
unsigned search(Array const &a, std::size_t row, std::size_t col, int rd,
int cd) {
int cr = (int)row;
int cc = (int)col;
while (true) {
cr += rd;
cc += cd;
if (cr == row && cc == col) {
return 0;
}
if (cr < 0 || cr >= a.size()) {
return 0;
}
if (cc < 0 || cc >= a[cr].size()) {
return 0;
}
if (a[cr][cc] == '#') {
return 1;
}
if (a[cr][cc] == 'L') {
return 0;
}
}
assert(false);
}
char next_state(Array const &a, std::size_t row, std::size_t col) {
unsigned occupied = 0;
for (int row_delta = -1; row_delta < 2; ++row_delta) {
for (int col_delta = -1; col_delta < 2; ++col_delta) {
occupied += search(a, row, col, row_delta, col_delta);
}
}
if (a[row][col] == 'L' && occupied == 0) {
return '#';
} else if (a[row][col] == '#' && occupied >= 5) {
return 'L';
} else {
return a[row][col];
}
}
unsigned long count_occupied(Array const &a) {
unsigned long count = 0;
for (auto const &row : a) {
for (auto col : row) {
count += (col == '#');
}
}
return count;
}
unsigned long run_to_steady_state(Array const &a) {
Array current = a;
bool changed = true;
unsigned i = 0;
while (changed) {
std::cout << "------ iter = " << i++ << "\n";
changed = false;
Array next;
for (std::size_t row = 0; row < current.size(); ++row) {
std::string s = "";
for (std::size_t col = 0; col < current[row].size(); ++col) {
char c = next_state(current, row, col);
s.push_back(c);
if (c != current[row][col]) {
changed = true;
}
}
next.push_back(s);
std::cout << s << "\n";
}
current = next;
}
return count_occupied(current);
}
int main(int argc, char **argv) {
Array array;
for (std::string line; std::getline(std::cin, line);) {
array.push_back(line);
std::cout << line << "\n";
}
unsigned long result = run_to_steady_state(array);
std::cout << "Number of occupied states: " << result << "\n";
return 0;
}

144
2020/puzzle-12-01.cc Normal file
View File

@@ -0,0 +1,144 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
enum class Heading { North = 'N', East = 'E', South = 'S', West = 'W' };
struct Position {
Position() : x_(0), y_(0), head_(Heading::East) {}
void move(std::string const &s) {
char act = s[0];
int dist = std::stoi(s.substr(1));
switch (act) {
case 'N':
case 'E':
case 'S':
case 'W':
move(dist, static_cast<Heading>(act));
break;
case 'F':
move(dist, head_);
break;
case 'L':
rotate_left(dist);
break;
case 'R':
rotate_left(360 - dist);
break;
default:
assert(false);
}
std::cout << s << ": (" << x_ << ", " << y_ << ")\n";
}
void move(int dist, Heading head) {
switch (head) {
case Heading::North:
y_ += dist;
break;
case Heading::East:
x_ += dist;
break;
case Heading::South:
y_ -= dist;
break;
case Heading::West:
x_ -= dist;
break;
default:
assert(false);
break;
}
}
void rotate_left(int deg) {
/* We want the rang [-180, 180]. */
while (deg < -180) {
deg += 360;
}
while (deg > 180) {
deg -= 360;
}
if (deg == -90) {
switch (head_) {
case Heading::North:
head_ = Heading::East;
break;
case Heading::East:
head_ = Heading::South;
break;
case Heading::South:
head_ = Heading::West;
break;
case Heading::West:
head_ = Heading::North;
break;
default:
assert(false);
}
} else if (deg == 90) {
switch (head_) {
case Heading::North:
head_ = Heading::West;
break;
case Heading::East:
head_ = Heading::North;
break;
case Heading::South:
head_ = Heading::East;
break;
case Heading::West:
head_ = Heading::South;
break;
default:
assert(false);
}
} else if (deg == 180 || deg == -180) {
switch (head_) {
case Heading::North:
head_ = Heading::South;
break;
case Heading::East:
head_ = Heading::West;
break;
case Heading::South:
head_ = Heading::North;
break;
case Heading::West:
head_ = Heading::East;
break;
default:
assert(false);
}
} else if (deg != 0) {
assert(false);
}
}
int distance() const noexcept { return std::abs(x_) + std::abs(y_); }
int x_;
int y_;
Heading head_;
};
int main(int argc, char **argv) {
Position pos;
for (std::string line; std::getline(std::cin, line);) {
pos.move(line);
}
auto dist = pos.distance();
std::cout << "Distance: " << dist << "\n";
return 0;
}

110
2020/puzzle-12-02.cc Normal file
View File

@@ -0,0 +1,110 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
enum class Heading { North = 'N', East = 'E', South = 'S', West = 'W' };
struct Position {
Position() : x_(0), y_(0), wx_(10), wy_(1), head_(Heading::East) {}
void move(std::string const &s) {
char act = s[0];
int dist = std::stoi(s.substr(1));
switch (act) {
case 'N':
case 'E':
case 'S':
case 'W':
move_way(dist, static_cast<Heading>(act));
break;
case 'F':
x_ += wx_ * dist;
y_ += wy_ * dist;
break;
case 'L':
rotate_left(dist);
break;
case 'R':
rotate_left(360 - dist);
break;
default:
assert(false);
}
std::cout << s << ": Pos = (" << x_ << ", " << y_ << "), Way = (" << wx_
<< ", " << wy_ << ")\n";
}
void move_way(int dist, Heading head) {
switch (head) {
case Heading::North:
wy_ += dist;
break;
case Heading::East:
wx_ += dist;
break;
case Heading::South:
wy_ -= dist;
break;
case Heading::West:
wx_ -= dist;
break;
default:
assert(false);
break;
}
}
void rotate_left(int deg) {
/* We want the rang [-180, 180]. */
while (deg < -180) {
deg += 360;
}
while (deg > 180) {
deg -= 360;
}
if (deg == -90) {
int px = wx_;
int py = wy_;
wx_ = py;
wy_ = -px;
} else if (deg == 90) {
int px = wx_;
int py = wy_;
wx_ = -py;
wy_ = px;
} else if (deg == 180 || deg == -180) {
wx_ = -wx_;
wy_ = -wy_;
} else if (deg != 0) {
assert(false);
}
}
int distance() const noexcept { return std::abs(x_) + std::abs(y_); }
int x_;
int y_;
int wx_;
int wy_;
Heading head_;
};
int main(int argc, char **argv) {
Position pos;
for (std::string line; std::getline(std::cin, line);) {
pos.move(line);
}
auto dist = pos.distance();
std::cout << "Distance: " << dist << "\n";
return 0;
}

52
2020/puzzle-13-01.cc Normal file
View File

@@ -0,0 +1,52 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <climits>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using Time = unsigned long;
int main(int argc, char **argv) {
Time arrival;
Time closest = ULONG_MAX;
Time closest_id = 0;
std::string line;
std::getline(std::cin, line);
arrival = std::stoul(line);
std::cout << "Arrival time: " << arrival << "\n";
std::string buses;
std::getline(std::cin, buses);
std::string::size_type pos = 0;
while (pos < buses.size()) {
if (buses[pos] == ',' || buses[pos] == 'x') {
++pos;
continue;
}
std::string::size_type len = 0;
Time depart = std::stoul(buses.substr(pos), &len);
pos += len;
auto next_departure_in = depart - (arrival % depart);
if (next_departure_in == depart) {
next_departure_in = 0;
}
std::cout << "Bus #" << depart
<< " previous departure: " << (arrival / depart) * depart
<< ", next departure in: " << next_departure_in << "\n";
if (next_departure_in < closest) {
closest = next_departure_in;
closest_id = depart;
}
}
std::cout << "Next departure # " << closest_id << " in " << closest
<< " minutes.\n";
std::cout << "Result = " << closest * closest_id << "\n";
return 0;
}

55
2020/puzzle-13-02.cc Normal file
View File

@@ -0,0 +1,55 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <climits>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using Time = unsigned long;
using IDPair = std::pair<Time, Time>;
using IDVector = std::vector<IDPair>;
int main(int argc, char **argv) {
std::string line;
std::getline(std::cin, line); // Ignore first line
std::string buses;
std::getline(std::cin, buses);
std::string::size_type pos = 0;
Time offset = 0;
IDVector ids;
while (pos < buses.size()) {
if (buses[pos] == ',') {
++pos;
continue;
} else if (buses[pos] == 'x') {
++offset;
++pos;
continue;
}
std::string::size_type len = 0;
Time depart = std::stoul(buses.substr(pos), &len);
std::cout << "Bus #" << depart << " at offset " << offset << "\n";
ids.push_back(std::make_pair(depart, offset));
pos += len;
++offset;
}
// Make assumption that all bus IDs are co-prime.
Time t = 0;
Time t_incr = 1;
for (auto const &bus : ids) {
while ((t + bus.second) % bus.first != 0) {
t += t_incr;
}
std::cout << "Bus #" << bus.first << " at offset " << bus.second
<< " t = " << t << "\n";
t_incr *= bus.first;
}
return 0;
}

68
2020/puzzle-14-01.cc Normal file
View File

@@ -0,0 +1,68 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using Data = std::uint64_t;
using Mem = std::vector<Data>;
struct Computer {
Computer() : set_(0), clear_(0), mem_() {}
void add_instruction(std::string const &s) {
static const std::regex re("mem\\[(\\d+)\\] = (\\d+)");
std::smatch m;
if (s.substr(0, 4) == "mask") {
set_ = 0;
clear_ = 0;
for (auto c : s.substr(7)) {
set_ <<= 1;
clear_ <<= 1;
set_ |= (c == '1');
clear_ |= (c != '0');
}
std::cout << "mask set: " << std::hex << set_ << "\n";
std::cout << "mask clear: " << std::hex << clear_ << "\n";
} else if (std::regex_search(s, m, re)) {
Data loc = std::stoul(m.str(1));
Data value = std::stoul(m.str(2));
std::cout << "mem[" << std::dec << loc << " = (" << value << " | " << set_
<< ") & " << clear_ << " = ";
value = (value | set_) & clear_;
std::cout << value << "\n";
if (mem_.size() <= loc) {
mem_.resize(loc + 1, 0);
}
mem_[loc] = value;
}
}
Data mem_sum() const noexcept {
Data r = 0;
for (auto v : mem_) {
r += v;
}
return r;
}
Data set_;
Data clear_;
Mem mem_;
};
int main(int argc, char **argv) {
Computer c;
std::string line;
while (std::getline(std::cin, line)) {
c.add_instruction(line);
}
std::cout << "Memory sum: " << std::dec << c.mem_sum() << "\n";
return 0;
}

93
2020/puzzle-14-02.cc Normal file
View File

@@ -0,0 +1,93 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using Data = std::uint64_t;
using Mem = std::map<Data, Data>;
struct Computer {
void add_instruction(std::string const &s) {
static const std::regex re("mem\\[(\\d+)\\] = (\\d+)");
std::smatch m;
if (s.substr(0, 4) == "mask") {
std::string mask = s.substr(7);
std::reverse(mask.begin(), mask.end());
set_ = 0;
bits_.clear();
std::size_t bit = 0;
std::cout << "Bitmask: floating bits: ";
for (auto c : mask) {
if (c == '1') {
set_ |= Data(1) << bit;
} else if (c == 'X') {
bits_.push_back(std::size_t(1) << bit);
std::cout << bit << " (" << (std::size_t(1) << bit) << ") ";
} else {
assert(c == '0');
}
++bit;
}
assert(bit == mask.size());
std::cout << " set: 0x" << std::hex << set_ << std::dec << "\n";
} else if (std::regex_search(s, m, re)) {
Data loc = std::stoul(m.str(1));
Data value = std::stoul(m.str(2));
set_mem(loc, value);
}
}
void set_mem(Data loc, Data value) {
std::cout << "Writing: " << value << " to base loc " << std::hex << loc
<< " which floats to:";
for (auto b : bits_) {
loc &= ~b;
}
for (std::size_t i = 0; i < (1 << bits_.size()); ++i) {
auto l = loc | set_;
for (std::size_t j = 0; j < bits_.size(); ++j) {
if (i & (std::size_t(1) << j)) {
l |= bits_[j];
}
}
std::cout << " " << l;
auto [it, success] = mem_.insert({l, value});
if (!success) {
it->second = value;
}
}
std::cout << std::dec << "\n";
}
Data mem_sum() const noexcept {
Data r = 0;
for (auto v : mem_) {
r += v.second;
}
return r;
}
Data set_;
std::vector<std::size_t> bits_;
Mem mem_;
};
int main(int argc, char **argv) {
Computer c;
std::string line;
while (std::getline(std::cin, line)) {
c.add_instruction(line);
}
std::cout << "Memory sum: " << std::dec << c.mem_sum() << "\n";
return 0;
}

58
2020/puzzle-15-01.cc Normal file
View File

@@ -0,0 +1,58 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using NumMap = std::map<int, int>;
int add_to_map(NumMap &nums, int num, int turn) {
auto [it, success] = nums.insert({num, turn});
if (success) {
// std::cout << "Turn " << turn << ": " << num << " (new)\n";
return 0;
} else {
int r = turn - it->second;
// std::cout << "Turn " << turn << ": " << num << " (previous seen turn "
// << it->second << " dist = " << r << ")\n";
it->second = turn;
return r;
}
}
int run(std::string const &s, int num_turns) {
NumMap seen;
std::size_t pos = 0;
std::size_t len;
int next_num = 0;
int turn = 1;
while (pos < s.size()) {
int num = std::stoi(s.substr(pos), &len);
pos += len;
while (pos < s.size() && s[pos] == ',') {
++pos;
}
next_num = add_to_map(seen, num, turn);
++turn;
}
while (turn < num_turns) {
next_num = add_to_map(seen, next_num, turn);
++turn;
}
return next_num;
}
int main(int argc, char **argv) {
std::string line;
while (std::getline(std::cin, line)) {
int r = run(line, 2020);
std::cout << "2020'th number: " << r << "\n";
}
return 0;
}

59
2020/puzzle-15-02.cc Normal file
View File

@@ -0,0 +1,59 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
#include <unordered_map>
using NumMap = std::unordered_map<int, int>;
int add_to_map(NumMap &nums, int num, int turn) {
auto [it, success] = nums.insert({num, turn});
if (success) {
// std::cout << "Turn " << turn << ": " << num << " (new)\n";
return 0;
} else {
int r = turn - it->second;
// std::cout << "Turn " << turn << ": " << num << " (previous seen turn "
// << it->second << " dist = " << r << ")\n";
it->second = turn;
return r;
}
}
int run(std::string const &s, int num_turns) {
NumMap seen;
std::size_t pos = 0;
std::size_t len;
int next_num = 0;
int turn = 1;
while (pos < s.size()) {
int num = std::stoi(s.substr(pos), &len);
pos += len;
while (pos < s.size() && s[pos] == ',') {
++pos;
}
next_num = add_to_map(seen, num, turn);
++turn;
}
while (turn < num_turns) {
next_num = add_to_map(seen, next_num, turn);
++turn;
}
return next_num;
}
int main(int argc, char **argv) {
std::string line;
while (std::getline(std::cin, line)) {
int r = run(line, 30000000);
std::cout << "30000000th number: " << r << "\n";
}
return 0;
}

84
2020/puzzle-16-01.cc Normal file
View File

@@ -0,0 +1,84 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
using ValidVector = std::vector<bool>;
struct Classifier {
void add_ranges(std::string const &s) {
static const std::regex re("(\\w+): (\\d+)-(\\d+) or (\\d+)-(\\d+)");
std::smatch m;
if (std::regex_search(s, m, re)) {
std::cout << m.str(1) << "valid = ";
add_range(m.str(2), m.str(3));
std::cout << ", ";
add_range(m.str(4), m.str(5));
std::cout << "\n";
}
}
void add_range(std::string const &lows, std::string const &highs) {
int low = std::stoi(lows);
int high = std::stoi(highs);
++high;
if (vecs_.size() < high) {
vecs_.resize(high, false);
}
std::cout << "[" << low << ", " << high << "]";
while (low < high) {
vecs_[low] = true;
++low;
}
}
int count_errors(std::string const &s) {
std::string::size_type pos = 0;
int result = 0;
while (pos < s.size()) {
std::size_t len = 0;
int num = std::stoi(s.substr(pos), &len);
if (!vecs_[num]) {
result += num;
std::cout << "Invalid: " << num << "\n";
}
pos += len;
while (pos < s.size() && s[pos] == ',') {
++pos;
}
}
return result;
}
ValidVector vecs_;
};
enum class State { Permitted, Your, Nearby };
int main(int argc, char **argv) {
std::string line;
State s = State::Permitted;
Classifier c;
int e = 0;
while (std::getline(std::cin, line)) {
if (line == "your ticket:") {
s = State::Your;
} else if (line == "nearby tickets:") {
s = State::Nearby;
} else if (line == "") {
/* nothing */;
} else if (s == State::Permitted) {
c.add_ranges(line);
} else if (s == State::Nearby) {
e += c.count_errors(line);
}
}
std::cout << "Error count: " << e << "\n";
return 0;
}

193
2020/puzzle-16-02.cc Normal file
View File

@@ -0,0 +1,193 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
struct Classifier {
void add_ranges(std::string const &s) {
static const std::regex re("(.+): (\\d+)-(\\d+) or (\\d+)-(\\d+)");
std::smatch m;
if (std::regex_search(s, m, re)) {
auto field = field_names_.size();
field_names_.push_back(m.str(1));
std::cout << m.str(1) << "valid = ";
add_range(m.str(2), m.str(3), field);
std::cout << ", ";
add_range(m.str(4), m.str(5), field);
std::cout << "\n";
}
}
void add_range(std::string const &lows, std::string const &highs,
std::size_t field) {
int low = std::stoi(lows);
int high = std::stoi(highs);
++high;
unsigned flag = 1 << field;
assert(flag != 0);
if (num_to_valid_field_.size() < high) {
num_to_valid_field_.resize(high, 0);
}
std::cout << "[" << low << ", " << high << "]";
while (low < high) {
num_to_valid_field_[low] |= flag;
++low;
}
}
void close() {
idx_to_valid_field_.resize(0);
idx_to_valid_field_.resize(field_names_.size(),
(1 << field_names_.size()) - 1);
}
int count_errors(std::string const &s) const {
std::string::size_type pos = 0;
int result = 0;
while (pos < s.size()) {
std::size_t len = 0;
int num = std::stoi(s.substr(pos), &len);
if (num >= num_to_valid_field_.size() || num_to_valid_field_[num] == 0) {
result += num == 0 ? 1 : num;
std::cout << "Invalid: " << num << "\n";
}
pos += len;
while (pos < s.size() && s[pos] == ',') {
++pos;
}
}
return result;
}
void mark_valid(std::string const &s) {
std::string::size_type pos = 0;
unsigned idx = 0;
while (pos < s.size()) {
std::size_t len = 0;
int num = std::stoi(s.substr(pos), &len);
unsigned valid_fields =
num >= num_to_valid_field_.size() ? 0 : num_to_valid_field_[num];
idx_to_valid_field_[idx] &= valid_fields;
pos += len;
while (pos < s.size() && s[pos] == ',') {
++pos;
}
assert(idx_to_valid_field_[idx] != 0);
++idx;
}
}
void print_valid() const {
for (std::size_t idx = 0; idx < idx_to_valid_field_.size(); ++idx) {
std::cout << "Index " << idx << " valid for fields:";
unsigned field = 0;
for (auto valid_fields = idx_to_valid_field_[idx]; valid_fields != 0;
valid_fields >>= 1) {
if (valid_fields & 1) {
std::cout << " " << field_names_[field];
}
++field;
}
std::cout << "\n";
}
}
void reduce() {
bool changed = true;
while (changed) {
changed = false;
for (unsigned idx = 0; idx < idx_to_valid_field_.size(); ++idx) {
auto valid_fields = idx_to_valid_field_[idx];
if ((valid_fields & (valid_fields - 1)) == 0) {
std::cout << "Index " << idx << " can only be field " << valid_fields
<< "\n";
for (unsigned idx2 = 0; idx2 < idx_to_valid_field_.size(); ++idx2) {
if (idx == idx2) {
continue;
}
changed |= (idx_to_valid_field_[idx2] & valid_fields) != 0;
idx_to_valid_field_[idx2] &= ~valid_fields;
}
}
}
}
}
unsigned long calculate_product(std::string const &s,
std::string const &prefix) {
std::vector<unsigned> values;
std::size_t pos = 0;
while (pos < s.size()) {
if (s[pos] == ',') {
++pos;
} else {
std::size_t len;
values.push_back(std::stoi(s.substr(pos), &len));
pos += len;
}
}
unsigned long product = 1;
for (unsigned field = 0; field < field_names_.size(); ++field) {
if (field_names_[field].substr(0, prefix.size()) == prefix) {
unsigned idx = 0;
while (idx < idx_to_valid_field_.size() &&
idx_to_valid_field_[idx] != (1 << field)) {
++idx;
}
assert(idx < idx_to_valid_field_.size());
product *= values[idx];
}
}
return product;
}
std::vector<unsigned> num_to_valid_field_;
std::vector<unsigned> idx_to_valid_field_;
std::vector<std::string> field_names_;
};
enum class State { Permitted, Your, Nearby };
int main(int argc, char **argv) {
std::string line;
State s = State::Permitted;
Classifier c;
int e = 0;
std::string our_ticket;
while (std::getline(std::cin, line)) {
if (line == "your ticket:") {
s = State::Your;
} else if (line == "nearby tickets:") {
s = State::Nearby;
c.close();
} else if (line == "") {
/* nothing */;
} else if (s == State::Permitted) {
c.add_ranges(line);
} else if (s == State::Your) {
our_ticket = line;
} else if (s == State::Nearby) {
auto e2 = c.count_errors(line);
e += e2;
if (e2 == 0) {
c.mark_valid(line);
}
}
}
c.print_valid();
c.reduce();
c.print_valid();
std::cout << "Product: " << c.calculate_product(our_ticket, "departure ")
<< "\n";
return 0;
}

88
2020/puzzle-17-01.cc Normal file
View File

@@ -0,0 +1,88 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
struct ConwayCubeState {
using Point = std::tuple<int, int, int>;
using PointSet = std::set<Point>;
using NeighbourMap = std::map<Point, unsigned>;
ConwayCubeState() : next_row_(0) {}
void add_row(std::string const &s) {
for (int x = 0; x < s.size(); ++x) {
if (s[x] == '#') {
set_point(x, next_row_, 0);
}
}
++next_row_;
}
ConwayCubeState next_state() const {
ConwayCubeState next;
for (auto const &n : neighbours_) {
bool active = points_.find(n.first) != points_.end();
if (active && (n.second == 2 || n.second == 3)) {
next.set_point(n.first);
} else if (!active && n.second == 3) {
next.set_point(n.first);
}
}
return next;
}
auto active() const { return points_.size(); }
private:
void set_point(Point const &pt) {
set_point(std::get<0>(pt), std::get<1>(pt), std::get<2>(pt));
}
void set_point(int px, int py, int pz) {
points_.insert({px, py, pz});
for (int x = px - 1; x < px + 2; ++x) {
for (int y = py - 1; y < py + 2; ++y) {
for (int z = pz - 1; z < pz + 2; ++z) {
if (x == px && y == py && z == pz) {
continue;
}
auto [it, success] = neighbours_.insert({{x, y, z}, 1});
if (!success) {
assert(it != neighbours_.end());
it->second += 1;
}
}
}
}
}
int next_row_;
PointSet points_;
NeighbourMap neighbours_;
};
int main(void) {
std::string line;
ConwayCubeState state;
while (std::getline(std::cin, line)) {
state.add_row(line);
std::cout << line << "\n";
}
std::cout << "Initial active count = " << state.active() << "\n";
for (unsigned i = 0; i < 6; ++i) {
state = state.next_state();
std::cout << i + 1 << ": active count = " << state.active() << "\n";
}
return 0;
}

91
2020/puzzle-17-02.cc Normal file
View File

@@ -0,0 +1,91 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
struct ConwayCubeState {
using Point = std::tuple<int, int, int, int>;
using PointSet = std::set<Point>;
using NeighbourMap = std::map<Point, unsigned>;
ConwayCubeState() : next_row_(0) {}
void add_row(std::string const &s) {
for (int x = 0; x < s.size(); ++x) {
if (s[x] == '#') {
set_point(x, next_row_, 0, 0);
}
}
++next_row_;
}
ConwayCubeState next_state() const {
ConwayCubeState next;
for (auto const &n : neighbours_) {
bool active = points_.find(n.first) != points_.end();
if (active && (n.second == 2 || n.second == 3)) {
next.set_point(n.first);
} else if (!active && n.second == 3) {
next.set_point(n.first);
}
}
return next;
}
auto active() const { return points_.size(); }
private:
void set_point(Point const &pt) {
set_point(std::get<0>(pt), std::get<1>(pt), std::get<2>(pt),
std::get<3>(pt));
}
void set_point(int pw, int px, int py, int pz) {
points_.insert({pw, px, py, pz});
for (int w = pw - 1; w < pw + 2; ++w) {
for (int x = px - 1; x < px + 2; ++x) {
for (int y = py - 1; y < py + 2; ++y) {
for (int z = pz - 1; z < pz + 2; ++z) {
if (w == pw && x == px && y == py && z == pz) {
continue;
}
auto [it, success] = neighbours_.insert({{w, x, y, z}, 1});
if (!success) {
assert(it != neighbours_.end());
it->second += 1;
}
}
}
}
}
}
int next_row_;
PointSet points_;
NeighbourMap neighbours_;
};
int main(void) {
std::string line;
ConwayCubeState state;
while (std::getline(std::cin, line)) {
state.add_row(line);
std::cout << line << "\n";
}
std::cout << "Initial active count = " << state.active() << "\n";
for (unsigned i = 0; i < 6; ++i) {
state = state.next_state();
std::cout << i + 1 << ": active count = " << state.active() << "\n";
}
return 0;
}

140
2020/puzzle-18-01.cc Normal file
View File

@@ -0,0 +1,140 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
enum class Token : char {
Eof,
Number,
LParens = '(',
RParens = ')',
Add = '+',
Multiply = '*'
};
using Value = unsigned long;
struct Parser {
Parser(std::string const &s) : expr_(s), pos_(0) { skip_whitespace(); }
Value evaluate() { return binop(); }
private:
Value binop() {
auto value = primary();
do {
if (peek() == Token::Add) {
chew(Token::Add);
value += primary();
} else if (peek() == Token::Multiply) {
chew(Token::Multiply);
value *= primary();
} else {
return value;
}
} while (true);
}
Value primary() {
if (peek() == Token::LParens) {
chew(Token::LParens);
Value value = binop();
chew(Token::RParens);
return value;
} else if (peek() == Token::Number) {
return chew_number();
} else {
std::cout << "expr_ = " << expr_ << "\n";
std::cout << "pos_ = " << pos_ << "\n";
std::cout << "End = " << expr_.substr(pos_) << "\n";
assert(false);
}
}
Token peek() {
if (pos_ == expr_.size()) {
return Token::Eof;
}
switch (expr_[pos_]) {
case '(':
return Token::LParens;
case ')':
return Token::RParens;
case '+':
return Token::Add;
case '*':
return Token::Multiply;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return Token::Number;
default:
std::cout << "expr_ = " << expr_ << "\n";
std::cout << "pos_ = " << pos_ << "\n";
std::cout << "End = " << expr_.substr(pos_) << "\n";
assert(false);
}
}
void chew(Token tok) {
assert(peek() == tok);
switch (tok) {
case Token::LParens:
case Token::RParens:
case Token::Add:
case Token::Multiply:
++pos_;
skip_whitespace();
break;
default:
assert(false);
}
}
void skip_whitespace() {
while (pos_ < expr_.size() && expr_[pos_] == ' ') {
++pos_;
}
}
Value chew_number() {
assert(peek() == Token::Number);
std::size_t len = 0;
Value value = std::stoul(expr_.substr(pos_), &len);
pos_ += len;
skip_whitespace();
return value;
}
std::string expr_;
std::string::size_type pos_;
};
int main(void) {
std::string line;
Value result = 0;
while (std::getline(std::cin, line)) {
Parser p(line);
Value r = p.evaluate();
std::cout << line << " = " << r << "\n";
result += r;
}
std::cout << "Sum: " << result << "\n";
return 0;
}

149
2020/puzzle-18-02.cc Normal file
View File

@@ -0,0 +1,149 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
enum class Token : char {
Eof,
Number,
LParens = '(',
RParens = ')',
Add = '+',
Multiply = '*'
};
using Value = unsigned long;
struct Parser {
Parser(std::string const &s) : expr_(s), pos_(0) { skip_whitespace(); }
Value evaluate() { return multop(); }
private:
Value addop() {
auto value = primary();
do {
if (peek() == Token::Add) {
chew(Token::Add);
value += primary();
} else {
return value;
}
} while (true);
}
Value multop() {
auto value = addop();
do {
if (peek() == Token::Multiply) {
chew(Token::Multiply);
value *= addop();
} else {
return value;
}
} while (true);
}
Value primary() {
if (peek() == Token::LParens) {
chew(Token::LParens);
Value value = evaluate();
chew(Token::RParens);
return value;
} else if (peek() == Token::Number) {
return chew_number();
} else {
std::cout << "expr_ = " << expr_ << "\n";
std::cout << "pos_ = " << pos_ << "\n";
std::cout << "End = " << expr_.substr(pos_) << "\n";
assert(false);
}
}
Token peek() {
if (pos_ == expr_.size()) {
return Token::Eof;
}
switch (expr_[pos_]) {
case '(':
return Token::LParens;
case ')':
return Token::RParens;
case '+':
return Token::Add;
case '*':
return Token::Multiply;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return Token::Number;
default:
std::cout << "expr_ = " << expr_ << "\n";
std::cout << "pos_ = " << pos_ << "\n";
std::cout << "End = " << expr_.substr(pos_) << "\n";
assert(false);
}
}
void chew(Token tok) {
assert(peek() == tok);
switch (tok) {
case Token::LParens:
case Token::RParens:
case Token::Add:
case Token::Multiply:
++pos_;
skip_whitespace();
break;
default:
assert(false);
}
}
void skip_whitespace() {
while (pos_ < expr_.size() && expr_[pos_] == ' ') {
++pos_;
}
}
Value chew_number() {
assert(peek() == Token::Number);
std::size_t len = 0;
Value value = std::stoul(expr_.substr(pos_), &len);
pos_ += len;
skip_whitespace();
return value;
}
std::string expr_;
std::string::size_type pos_;
};
int main(void) {
std::string line;
Value result = 0;
while (std::getline(std::cin, line)) {
Parser p(line);
Value r = p.evaluate();
std::cout << line << " = " << r << "\n";
result += r;
}
std::cout << "Sum: " << result << "\n";
return 0;
}

96
2020/puzzle-19-01.cc Normal file
View File

@@ -0,0 +1,96 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
struct Matcher {
void add_rule(std::string const &s) {
std::size_t len = 0;
unsigned long id = std::stoul(s, &len);
assert(s[len] == ':');
++len;
while (s[len] == ' ') {
++len;
}
if (rules_.size() <= id) {
rules_.resize(id + 1);
}
rules_[id] = s.substr(len);
}
void calculate_regex() { re_.assign("^" + expand_rule(0) + "$"); }
bool does_match(std::string const &s) const {
return std::regex_match(s, re_);
}
private:
std::string expand_rule(std::size_t id) {
std::string re;
std::string const &rule = rules_[id];
std::size_t pos = 0;
bool needs_brackets = false;
while (pos < rule.size()) {
if (rule[pos] == ' ') {
++pos;
} else if (rule[pos] == '|') {
re += "|";
needs_brackets = true;
++pos;
} else if (rule[pos] == '"') {
++pos;
while (pos < rule.size() && rule[pos] != '"') {
re += rule[pos];
++pos;
}
assert(pos < rule.size());
assert(rule[pos] == '"');
++pos;
} else if (std::isdigit(rule[pos])) {
std::size_t len = 0;
std::size_t subid = std::stoul(rule.substr(pos), &len);
pos += len;
re += expand_rule(subid);
} else {
assert(false);
}
}
if (needs_brackets) {
re = "(" + re + ")";
}
std::cout << "Rule " << id << " expands to: " << re << "\n";
return re;
}
std::vector<std::string> rules_;
std::regex re_;
};
int main(void) {
std::string line;
Matcher matcher;
bool adding_rules = true;
unsigned matches = 0;
while (std::getline(std::cin, line)) {
if (line.empty()) {
adding_rules = false;
matcher.calculate_regex();
} else if (adding_rules) {
matcher.add_rule(line);
} else {
bool m = matcher.does_match(line);
std::cout << line << ": does " << (m ? "" : "not ") << "match\n";
matches += m;
}
}
std::cout << "Total number of matches: " << matches << "\n";
return 0;
}

116
2020/puzzle-19-02.cc Normal file
View File

@@ -0,0 +1,116 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
struct Matcher {
void add_rule(std::string const &s) {
std::size_t len = 0;
unsigned long id = std::stoul(s, &len);
assert(s[len] == ':');
++len;
while (s[len] == ' ') {
++len;
}
if (rules_.size() <= id) {
rules_.resize(id + 1);
}
rules_[id] = s.substr(len);
}
void calculate_regex() {
re42_ = expand_rule(42);
re31_ = expand_rule(31);
}
bool does_match(std::string const &s) const {
std::smatch m;
std::string begin = "^" + re42_ + re42_;
unsigned repeats = 1;
while (std::regex_search(s, m, std::regex(begin))) {
std::string end = re31_ + "$";
std::string suffix = m.suffix();
for (unsigned i = 0; i < repeats; ++i) {
if (std::regex_search(suffix, std::regex("^" + end))) {
return true;
}
end = re31_ + end;
}
begin += re42_;
++repeats;
}
return false;
}
private:
std::string expand_rule(std::size_t id) {
std::string re;
std::string const &rule = rules_[id];
std::size_t pos = 0;
bool needs_brackets = false;
while (pos < rule.size()) {
if (rule[pos] == ' ') {
++pos;
} else if (rule[pos] == '|') {
re += "|";
needs_brackets = true;
++pos;
} else if (rule[pos] == '"') {
++pos;
while (pos < rule.size() && rule[pos] != '"') {
re += rule[pos];
++pos;
}
assert(pos < rule.size());
assert(rule[pos] == '"');
++pos;
} else if (std::isdigit(rule[pos])) {
std::size_t len = 0;
std::size_t subid = std::stoul(rule.substr(pos), &len);
pos += len;
re += expand_rule(subid);
} else {
assert(false);
}
}
if (needs_brackets) {
re = "(" + re + ")";
}
std::cout << "Rule " << id << " expands to: " << re << "\n";
return re;
}
std::vector<std::string> rules_;
std::string re42_;
std::string re31_;
};
int main(void) {
std::string line;
Matcher matcher;
bool adding_rules = true;
unsigned matches = 0;
while (std::getline(std::cin, line)) {
if (line.empty()) {
adding_rules = false;
matcher.calculate_regex();
} else if (adding_rules) {
matcher.add_rule(line);
} else {
bool m = matcher.does_match(line);
std::cout << line << ": does " << (m ? "" : "not ") << "match\n";
matches += m;
}
}
std::cout << "Total number of matches: " << matches << "\n";
return 0;
}

234
2020/puzzle-20-01.cc Normal file
View File

@@ -0,0 +1,234 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
using Id = unsigned long;
using Hash = unsigned long;
enum Edge { Top, Left, Bottom, Right };
struct Picture {
Picture(std::string id, std::istream &is) : in_use_(false) {
assert(id.substr(0, 5) == "Tile ");
id_ = std::stoul(id.substr(5));
std::string line;
while (std::getline(is, line)) {
if (line.empty()) {
return;
}
rows_.push_back(line);
}
}
Picture(Picture const &) = delete;
Picture &operator=(Picture const &) = delete;
Picture(Picture &&) = default;
Picture &operator=(Picture &&) = default;
void flip() {
for (auto &r : rows_) {
std::reverse(r.begin(), r.end());
}
}
void rotate() {
std::vector<std::string> copy(rows_.size());
for (auto const &r : rows_) {
std::size_t off = copy.size();
assert(r.size() == copy.size());
for (auto c : r) {
copy[--off] += c;
}
assert(off == 0);
}
rows_ = copy;
}
Hash hash(Edge edge) const {
unsigned x = (edge == Edge::Right) ? rows_[0].size() - 1 : 0;
unsigned y = (edge == Edge::Bottom) ? rows_.size() - 1 : 0;
unsigned dx = (edge == Edge::Top || edge == Edge::Bottom) ? 1 : 0;
unsigned dy = (edge == Edge::Left || edge == Edge::Right) ? 1 : 0;
Hash hash = 0;
for (unsigned i = 0; i < rows_.size(); ++i) {
hash <<= 1;
hash |= (rows_[y][x] == '#');
y += dy;
x += dx;
}
return hash;
}
Id id() const noexcept { return id_; }
bool operator<(Picture const &pict) const noexcept { return id_ < pict.id_; }
bool operator==(Picture const &pict) const noexcept {
return id_ == pict.id_;
}
bool in_use() const noexcept { return in_use_; }
void use() noexcept { in_use_ = true; }
void release() noexcept { in_use_ = false; }
private:
Id id_;
std::vector<std::string> rows_;
bool in_use_;
};
using Pictures = std::map<Id, Picture>;
using HashMap = std::multimap<Hash, Id>;
using Array = std::map<std::pair<unsigned, unsigned>, Id>;
struct PictureArray {
void add(Picture &&pic) {
auto id = pic.id();
auto [it, success] = pictures_.insert(std::make_pair(id, std::move(pic)));
assert(success);
// Set up hash -> ID mapping
Picture &picture = it->second;
for (unsigned r = 0; r < 4; ++r) {
for (unsigned f = 0; f < 2; ++f) {
hash_map_.insert({picture.hash(Edge::Top), picture.id()});
picture.flip();
}
picture.rotate();
}
}
Id solve() {
assert(pictures_.size() == 9 || pictures_.size() == 144);
for (auto &kv : pictures_) {
if (try_position(0, 0, kv.second)) {
print_ids();
return piece(0, 0).id() * piece(width() - 1, 0).id() *
piece(0, height() - 1).id() *
piece(width() - 1, height() - 1).id();
}
}
assert(false);
return 0;
}
private:
bool try_position(unsigned x, unsigned y, Picture &pict) {
if (pict.in_use()) {
return false;
}
auto [it, success] = array_.insert({{x, y}, pict.id()});
if (!success) {
it->second = pict.id();
}
pict.use();
for (unsigned r = 0; r < 4; ++r) {
pict.rotate();
for (unsigned f = 0; f < 2; ++f) {
pict.flip();
if (x > 0) {
if (piece(x - 1, y).hash(Edge::Right) != pict.hash(Edge::Left)) {
continue;
}
}
if (y > 0) {
if (piece(x, y - 1).hash(Edge::Bottom) != pict.hash(Edge::Top)) {
continue;
}
}
auto next_x = x + 1;
auto next_y = y;
Hash hash = pict.hash(Edge::Right);
if (next_x == width()) {
next_x = 0;
next_y = y + 1;
hash = piece(0, y).hash(Edge::Bottom);
}
if (next_y == height()) {
return true;
}
auto [it, ite] = hash_map_.equal_range(hash);
while (it != ite) {
auto itp = pictures_.find(it->second);
assert(itp != pictures_.end());
if (try_position(next_x, next_y, itp->second)) {
return true;
}
++it;
}
}
}
pict.release();
return false;
}
void print_ids() const {
for (unsigned y = 0; y < height(); ++y) {
for (unsigned x = 0; x < width(); ++x) {
std::cout << " " << piece(x, y).id();
}
std::cout << "\n";
}
}
Picture const &piece(unsigned x, unsigned y) const {
auto const &it = array_.find({x, y});
assert(it != array_.end());
auto const &itp = pictures_.find(it->second);
assert(itp != pictures_.end());
return itp->second;
}
unsigned width() const noexcept {
if (pictures_.size() == 9) {
return 3;
} else if (pictures_.size() == 144) {
return 12;
} else {
assert(false);
}
return 0;
}
unsigned height() const noexcept { return width(); }
Pictures pictures_;
HashMap hash_map_;
Array array_;
};
int main(void) {
PictureArray pictures_;
std::string line;
while (std::getline(std::cin, line)) {
if (line.empty()) {
continue;
}
Picture pic(line, std::cin);
pictures_.add(std::move(pic));
}
auto solution = pictures_.solve();
std::cout << "Product = " << solution << "\n";
return 0;
}

360
2020/puzzle-20-02.cc Normal file
View File

@@ -0,0 +1,360 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
using Id = unsigned long;
using Hash = unsigned long;
enum Edge { Top, Left, Bottom, Right };
struct PictureArray;
struct Picture {
Picture(std::string id, std::istream &is) : in_use_(false) {
assert(id.substr(0, 5) == "Tile ");
id_ = std::stoul(id.substr(5));
std::string line;
while (std::getline(is, line)) {
if (line.empty()) {
return;
}
rows_.push_back(line);
}
}
Picture(PictureArray const &array);
Picture(Picture const &) = delete;
Picture &operator=(Picture const &) = delete;
Picture(Picture &&) = default;
Picture &operator=(Picture &&) = default;
void flip() {
for (auto &r : rows_) {
std::reverse(r.begin(), r.end());
}
}
void rotate() {
std::vector<std::string> copy(rows_.size());
for (auto const &r : rows_) {
std::size_t off = copy.size();
assert(r.size() == copy.size());
for (auto c : r) {
copy[--off] += c;
}
assert(off == 0);
}
rows_ = copy;
}
Hash hash(Edge edge) const {
unsigned x = (edge == Edge::Right) ? rows_[0].size() - 1 : 0;
unsigned y = (edge == Edge::Bottom) ? rows_.size() - 1 : 0;
unsigned dx = (edge == Edge::Top || edge == Edge::Bottom) ? 1 : 0;
unsigned dy = (edge == Edge::Left || edge == Edge::Right) ? 1 : 0;
Hash hash = 0;
for (unsigned i = 0; i < rows_.size(); ++i) {
hash <<= 1;
hash |= (rows_[y][x] == '#');
y += dy;
x += dx;
}
return hash;
}
Id id() const noexcept { return id_; }
bool operator<(Picture const &pict) const noexcept { return id_ < pict.id_; }
bool operator==(Picture const &pict) const noexcept {
return id_ == pict.id_;
}
bool in_use() const noexcept { return in_use_; }
void use() noexcept { in_use_ = true; }
void release() noexcept { in_use_ = false; }
friend std::ostream &operator<<(std::ostream &os, Picture const &pict);
unsigned find_monsters() {
for (unsigned r = 0; r < 4; ++r) {
rotate();
for (unsigned f = 0; f < 2; ++f) {
flip();
auto monster_count = find_monsters1();
if (monster_count > 0) {
return monster_count;
}
}
}
return 0;
}
unsigned roughness() const {
unsigned rough = 0;
for (auto const &r : rows_) {
for (auto c : r) {
if (c == '#') {
++rough;
}
}
}
return rough;
}
private:
unsigned find_monsters1() {
// 0 1
// 01234567890123456789
// #
// # ## ## ###
// # # # # # #
static const std::string::size_type locs[] = {18,
std::string::npos,
0,
5,
6,
11,
12,
17,
18,
19,
std::string::npos,
1,
4,
7,
10,
13,
16,
std::string::npos,
std::string::npos};
static const std::string::size_type mwidth = 20;
static const std::size_t mheight = 3;
unsigned monster_count = 0;
for (std::size_t y = 0; y <= rows_.size() - mheight; ++y) {
for (std::string::size_type x = 0; x <= rows_[y].size() - mwidth; ++x) {
std::size_t cy = 0;
std::string::size_type const *cx = locs;
bool found = true;
while (cy < mheight) {
if (*cx == std::string::npos) {
++cy;
} else if (rows_[y + cy][x + *cx] != '#') {
found = false;
break;
}
++cx;
}
if (found) {
++monster_count;
std::size_t cy = 0;
std::string::size_type const *cx = locs;
while (cy < mheight) {
if (*cx == std::string::npos) {
++cy;
} else {
rows_[y + cy][x + *cx] = '*';
}
++cx;
}
}
}
}
return monster_count;
}
private:
Id id_;
std::vector<std::string> rows_;
bool in_use_;
};
std::ostream &operator<<(std::ostream &os, Picture const &pict) {
for (auto const &r : pict.rows_) {
os << r << '\n';
}
return os;
}
using Pictures = std::map<Id, Picture>;
using HashMap = std::multimap<Hash, Id>;
using Array = std::map<std::pair<unsigned, unsigned>, Id>;
struct PictureArray {
void add(Picture &&pic) {
auto id = pic.id();
auto [it, success] = pictures_.insert(std::make_pair(id, std::move(pic)));
assert(success);
// Set up hash -> ID mapping
Picture &picture = it->second;
for (unsigned r = 0; r < 4; ++r) {
for (unsigned f = 0; f < 2; ++f) {
hash_map_.insert({picture.hash(Edge::Top), picture.id()});
picture.flip();
}
picture.rotate();
}
}
Id solve() {
assert(pictures_.size() == 9 || pictures_.size() == 144);
for (auto &kv : pictures_) {
if (try_position(0, 0, kv.second)) {
print_ids();
return piece(0, 0).id() * piece(width() - 1, 0).id() *
piece(0, height() - 1).id() *
piece(width() - 1, height() - 1).id();
}
}
assert(false);
return 0;
}
unsigned width() const noexcept {
if (pictures_.size() == 9) {
return 3;
} else if (pictures_.size() == 144) {
return 12;
} else {
assert(false);
}
return 0;
}
unsigned height() const noexcept { return width(); }
Picture const &piece(unsigned x, unsigned y) const {
auto const &it = array_.find({x, y});
assert(it != array_.end());
auto const &itp = pictures_.find(it->second);
assert(itp != pictures_.end());
return itp->second;
}
private:
bool try_position(unsigned x, unsigned y, Picture &pict) {
if (pict.in_use()) {
return false;
}
auto [it, success] = array_.insert({{x, y}, pict.id()});
if (!success) {
it->second = pict.id();
}
pict.use();
for (unsigned r = 0; r < 4; ++r) {
pict.rotate();
for (unsigned f = 0; f < 2; ++f) {
pict.flip();
if (x > 0) {
if (piece(x - 1, y).hash(Edge::Right) != pict.hash(Edge::Left)) {
continue;
}
}
if (y > 0) {
if (piece(x, y - 1).hash(Edge::Bottom) != pict.hash(Edge::Top)) {
continue;
}
}
auto next_x = x + 1;
auto next_y = y;
Hash hash = pict.hash(Edge::Right);
if (next_x == width()) {
next_x = 0;
next_y = y + 1;
hash = piece(0, y).hash(Edge::Bottom);
}
if (next_y == height()) {
return true;
}
auto [it, ite] = hash_map_.equal_range(hash);
while (it != ite) {
auto itp = pictures_.find(it->second);
assert(itp != pictures_.end());
if (try_position(next_x, next_y, itp->second)) {
return true;
}
++it;
}
}
}
pict.release();
return false;
}
void print_ids() const {
for (unsigned y = 0; y < height(); ++y) {
for (unsigned x = 0; x < width(); ++x) {
std::cout << " " << piece(x, y).id();
}
std::cout << "\n";
}
}
Pictures pictures_;
HashMap hash_map_;
Array array_;
};
Picture::Picture(PictureArray const &array) : id_(0), in_use_(false) {
for (unsigned y = 0; y < array.height(); ++y) {
auto ybase = rows_.size();
for (unsigned x = 0; x < array.width(); ++x) {
auto &pict = array.piece(x, y);
for (auto py = 1; py < pict.rows_.size() - 1; ++py) {
auto yidx = ybase + py - 1;
if (rows_.size() <= yidx) {
rows_.resize(yidx + 1);
}
rows_[yidx] += pict.rows_[py].substr(1, pict.rows_[py].size() - 2);
}
}
}
assert(rows_.size() == array.height() * 8);
assert(rows_[0].size() == array.width() * 8);
}
int main(void) {
PictureArray pictures_;
std::string line;
while (std::getline(std::cin, line)) {
if (line.empty()) {
continue;
}
Picture pic(line, std::cin);
pictures_.add(std::move(pic));
}
auto solution = pictures_.solve();
std::cout << "Product = " << solution << "\n";
Picture merged(pictures_);
auto monster_count = merged.find_monsters();
std::cout << merged;
std::cout << "Number of monsters: " << monster_count << "\n";
std::cout << "Roughness: " << merged.roughness() << "\n";
return 0;
}

106
2020/puzzle-21-01.cc Normal file
View File

@@ -0,0 +1,106 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
#include <vector>
using Ingredient = std::string;
using Allergen = std::string;
using Ingredients = std::set<Ingredient>;
using Allergens = std::set<Allergen>;
using IngredientInfo = std::pair<unsigned, Allergens>;
using IngredientMap = std::map<Ingredient, IngredientInfo>;
using AllergenMap = std::map<Allergen, Ingredients>;
class IngredientParser {
public:
void add_recipe(std::string const &s) {
auto it = s.begin();
Ingredients i;
while (it != s.end()) {
auto ite = std::find(it, s.end(), ' ');
auto ingredient = std::string(it, ite);
while (ite != s.end() && *ite == ' ') {
++ite;
}
it = ite;
if (ingredient == "(contains") {
break;
} else {
i.insert(ingredient);
auto [iit, success] =
ingredients_.insert({ingredient, {1, Allergens()}});
if (!success) {
iit->second.first++;
}
}
}
while (it != s.end()) {
auto ite = std::find_if(
it, s.end(), [](char c) -> bool { return c == ',' || c == ')'; });
auto allergen = std::string(it, ite);
++ite;
while (ite != s.end() && *ite == ' ') {
++ite;
}
it = ite;
auto [insert_it, success] = allergens_.insert({allergen, i});
if (!success) {
Ingredients a;
std::set_intersection(i.begin(), i.end(), insert_it->second.begin(),
insert_it->second.end(),
std::inserter(a, a.end()));
insert_it->second = a;
}
}
}
unsigned clean_ingredients() {
for (auto const &kv : allergens_) {
auto allergen = kv.first;
std::cout << "Allergen " << allergen << ":";
for (auto const &i : kv.second) {
std::cout << " " << i;
auto it = ingredients_.find(i);
assert(it != ingredients_.end());
it->second.second.insert(allergen);
}
std::cout << "\n";
}
unsigned count = 0;
for (auto const &i : ingredients_) {
if (i.second.second.size() == 0) {
std::cout << i.first << " is not an allergen, appears "
<< i.second.first << ".\n";
count += i.second.first;
}
}
return count;
}
private:
IngredientMap ingredients_;
AllergenMap allergens_;
};
int main(void) {
std::string line;
IngredientParser parser;
while (std::getline(std::cin, line)) {
parser.add_recipe(line);
}
auto clean = parser.clean_ingredients();
std::cout << "Number of clean ingredients: " << clean << "\n";
return 0;
}

143
2020/puzzle-21-02.cc Normal file
View File

@@ -0,0 +1,143 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
#include <vector>
using Ingredient = std::string;
using Allergen = std::string;
using Ingredients = std::set<Ingredient>;
using Allergens = std::set<Allergen>;
using IngredientInfo = std::pair<unsigned, Allergens>;
using IngredientMap = std::map<Ingredient, IngredientInfo>;
using AllergenMap = std::map<Allergen, Ingredients>;
class IngredientParser {
public:
void add_recipe(std::string const &s) {
auto it = s.begin();
Ingredients i;
while (it != s.end()) {
auto ite = std::find(it, s.end(), ' ');
auto ingredient = std::string(it, ite);
while (ite != s.end() && *ite == ' ') {
++ite;
}
it = ite;
if (ingredient == "(contains") {
break;
} else {
i.insert(ingredient);
auto [iit, success] =
ingredients_.insert({ingredient, {1, Allergens()}});
if (!success) {
iit->second.first++;
}
}
}
while (it != s.end()) {
auto ite = std::find_if(
it, s.end(), [](char c) -> bool { return c == ',' || c == ')'; });
auto allergen = std::string(it, ite);
++ite;
while (ite != s.end() && *ite == ' ') {
++ite;
}
it = ite;
auto [insert_it, success] = allergens_.insert({allergen, i});
if (!success) {
Ingredients a;
std::set_intersection(i.begin(), i.end(), insert_it->second.begin(),
insert_it->second.end(),
std::inserter(a, a.end()));
insert_it->second = a;
}
}
}
unsigned clean_ingredients() {
for (auto const &kv : allergens_) {
auto allergen = kv.first;
std::cout << "Allergen " << allergen << ":";
for (auto const &i : kv.second) {
std::cout << " " << i;
auto it = ingredients_.find(i);
assert(it != ingredients_.end());
it->second.second.insert(allergen);
}
std::cout << "\n";
}
unsigned count = 0;
for (auto const &i : ingredients_) {
if (i.second.second.size() == 0) {
std::cout << i.first << " is not an allergen, appears "
<< i.second.first << ".\n";
count += i.second.first;
}
}
return count;
}
std::string dangerous_ingredients() {
bool changed = true;
Ingredients i;
while (changed) {
changed = false;
for (auto const &kv : allergens_) {
if (kv.second.size() == 1) {
auto it = kv.second.begin();
Ingredient const &ing = *it;
auto [iit, success] = i.insert(ing);
if (!success) {
continue;
}
changed = true;
std::cout << "Allergen " << kv.first << " is in ingredient " << ing
<< "\n";
for (auto &kv2 : allergens_) {
if (kv2.second.size() != 1) {
kv2.second.erase(ing);
}
}
}
}
}
std::string result;
for (auto const &kv : allergens_) {
result += ",";
result += *kv.second.begin();
}
return result.substr(1);
}
private:
IngredientMap ingredients_;
AllergenMap allergens_;
};
int main(void) {
std::string line;
IngredientParser parser;
while (std::getline(std::cin, line)) {
parser.add_recipe(line);
}
auto clean = parser.clean_ingredients();
std::cout << "Number of clean ingredients: " << clean << "\n";
auto dangerous = parser.dangerous_ingredients();
std::cout << "Dangerous ingredients: " << dangerous << "\n";
return 0;
}

76
2020/puzzle-22-01.cc Normal file
View File

@@ -0,0 +1,76 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
#include <vector>
using Card = unsigned;
using Cards = std::list<Card>;
using Score = unsigned long;
struct Player {
Player(std::istream &is) {
std::string line;
if (!std::getline(is, name_)) {
assert(false);
}
while (std::getline(is, line)) {
if (line == "") {
break;
}
cards_.push_back(std::stoul(line));
}
}
bool empty() const { return cards_.empty(); }
Card front() const { return cards_.front(); }
void pop_front() { cards_.pop_front(); }
void push_back(Card c) { cards_.push_back(c); }
Score score() const {
Score r = 0;
unsigned idx = 1;
for (auto it = cards_.rbegin(); it != cards_.rend(); ++it) {
r += idx * *it;
++idx;
}
return r;
}
private:
std::string name_;
Cards cards_;
};
Score play_game(Player &p1, Player &p2) {
while (!p1.empty() && !p2.empty()) {
Card c1 = p1.front();
Card c2 = p2.front();
p1.pop_front();
p2.pop_front();
if (c1 > c2) {
p1.push_back(c1);
p1.push_back(c2);
} else if (c1 < c2) {
p2.push_back(c2);
p2.push_back(c1);
}
}
return p1.score() + p2.score();
}
int main(void) {
Player player1(std::cin);
Player player2(std::cin);
Score final_score = play_game(player1, player2);
std::cout << "Final score: " << final_score << "\n";
return 0;
}

129
2020/puzzle-22-02.cc Normal file
View File

@@ -0,0 +1,129 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
#include <vector>
using Card = unsigned;
using Cards = std::list<Card>;
using Score = unsigned long;
using Hash = std::string;
struct Player {
Player(std::istream &is) {
std::string line;
if (!std::getline(is, name_)) {
assert(false);
}
while (std::getline(is, line)) {
if (line == "") {
break;
}
cards_.push_back(std::stoul(line));
}
}
bool empty() const { return cards_.empty(); }
Card front() const { return cards_.front(); }
void pop_front() { cards_.pop_front(); }
void push_back(Card c) { cards_.push_back(c); }
std::size_t size() const { return cards_.size(); }
Score score() const {
Score r = 0;
unsigned idx = 1;
for (auto it = cards_.rbegin(); it != cards_.rend(); ++it) {
r += idx * *it;
++idx;
}
return r;
}
Hash hash() const {
std::string r = name_;
for (auto c : cards_) {
assert(c > 0);
assert(c < 128);
r += (char)c;
}
return r;
}
Player(Player const &prev, Card size) : name_(prev.name_ + "-") {
auto it = prev.cards_.begin();
for (Card i = 0; i < size; ++i) {
cards_.push_back(*it++);
}
}
friend std::ostream &operator<<(std::ostream &os, Player const &p);
private:
std::string name_;
Cards cards_;
};
std::ostream &operator<<(std::ostream &os, Player const &p) {
os << p.name_;
for (auto c : p.cards_) {
os << " " << c;
}
return os;
}
// False = p1 wins, true = p2 wins.
bool play_game(Player &p1, Player &p2) {
std::set<Hash> hashes;
while (!p1.empty() && !p2.empty()) {
// Ensure we've not seen this configuration before.
auto [it, success] = hashes.insert(p1.hash() + p2.hash());
if (!success) {
return false;
}
Card c1 = p1.front();
Card c2 = p2.front();
p1.pop_front();
p2.pop_front();
if (p1.size() >= c1 && p2.size() >= c2) {
Player sp1(p1, c1);
Player sp2(p2, c2);
bool result = play_game(sp1, sp2);
if (!result) {
p1.push_back(c1);
p1.push_back(c2);
} else {
p2.push_back(c2);
p2.push_back(c1);
}
} else if (c1 > c2) {
p1.push_back(c1);
p1.push_back(c2);
} else if (c1 < c2) {
p2.push_back(c2);
p2.push_back(c1);
} else {
assert(false);
}
}
return p1.empty();
}
int main(void) {
Player player1(std::cin);
Player player2(std::cin);
play_game(player1, player2);
Score final_score = player1.score() + player2.score();
std::cout << "Final score: " << final_score << "\n";
return 0;
}

52
2020/puzzle-23-01.cc Normal file
View File

@@ -0,0 +1,52 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
#include <vector>
struct Cups {
Cups(std::string const &s) : s_(s) {
std::cout << "Initial state: " << s_ << "\n";
}
void play(unsigned moves) {
// We assume the current value is always at index 0.
// So the values to remove are in offsets [1, 4).
while (moves-- > 0) {
char current = s_[0];
char d = s_[0] == '1' ? '9' : s_[0] - 1;
std::string::size_type dest = std::string::npos;
while (dest == std::string::npos) {
dest = s_.find(d, 4);
d = d == '1' ? '9' : d - 1;
}
s_ = s_.substr(4, dest - 3) + s_.substr(1, 3) + s_.substr(dest + 1) +
s_[0];
std::cout << moves << ": " << s_ << "\n";
}
}
std::string result() const {
auto split = s_.find('1');
return s_.substr(split + 1) + s_.substr(0, split);
}
private:
std::string s_;
};
int main(void) {
std::string line;
std::getline(std::cin, line);
Cups cups(line);
cups.play(100);
std::cout << "Result: " << cups.result() << "\n";
return 0;
}

131
2020/puzzle-23-02.cc Normal file
View File

@@ -0,0 +1,131 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
#include <vector>
using Value = unsigned long;
struct Cup {
Cup() : value_(0), next_(nullptr) {}
Value value() const { return value_; }
void value(Value v) { value_ = v; }
Cup *next() const { return next_; }
void next(Cup *n) { next_ = n; }
private:
Value value_;
Cup *next_;
};
struct Cups {
static constexpr std::size_t num_cups_ = 1000000;
static constexpr std::size_t num_moves_ = 10000000;
Cups(std::string const &s) : cups_(new Cup[num_cups_]), current_(cups_) {
std::size_t idx = 0;
for (auto c : s) {
cups_[idx].value(c - '0');
cups_[idx].next(cups_ + idx + 1);
++idx;
}
Value v = 10;
while (idx < num_cups_) {
cups_[idx].value(v++);
cups_[idx].next(cups_ + idx + 1);
++idx;
}
cups_[num_cups_ - 1].next(cups_);
assert(v == num_cups_ + 1);
}
~Cups() { delete[] cups_; }
void play() {
for (std::size_t move = 0; move < num_moves_; ++move) {
if (move % 1000 == 0) {
std::cout << "A" << move << " " << current_ - cups_ << " "
<< current_->value() << " " << result() << "\n";
// print();
}
// Remove first three after current.
Cup *rb = current_->next();
Cup *re = rb->next()->next();
current_->next(re->next());
re->next(nullptr);
// Work out the destination
auto vm1 = current_->value();
do {
--vm1;
if (vm1 == 0) {
vm1 = num_cups_;
}
} while (vm1 == rb->value() || vm1 == rb->next()->value() ||
vm1 == rb->next()->next()->value());
// Where do we insert? Note that we use the hack that all the values are
// actually in an index and we know what the value is (roughly) based on
// the index.
Cup *ins = nullptr;
if (vm1 < 10) {
ins = cups_;
while (ins->value() != vm1) {
assert(ins != cups_ + 10);
++ins;
}
} else {
ins = cups_ + vm1 - 1;
}
assert(ins->value() == vm1);
// Insert the removed elements.
re->next(ins->next());
ins->next(rb);
// Move to next element.
current_ = current_->next();
}
}
Value result() const {
Cup *one = cups_;
while (one->value() != 1) {
++one;
assert(one != cups_ + num_cups_);
}
return one->next()->value() * one->next()->next()->value();
}
void print() const {
std::cout << "State:";
Cup const *c = current_;
do {
std::cout << " " << c->value();
c = c->next();
} while (c != current_);
std::cout << " loop \n";
}
private:
Cup *cups_;
Cup *current_;
};
int main(void) {
std::string line;
std::getline(std::cin, line);
Cups cups(line);
cups.play();
std::cout << "Result: " << cups.result() << "\n";
return 0;
}

64
2020/puzzle-24-01.cc Normal file
View File

@@ -0,0 +1,64 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
#include <vector>
using Coord = long;
using Position = std::pair<Coord, Coord>;
using Tiles = std::set<Position>;
struct Tiler {
void flip_tile(std::string const &s) {
Coord x = 0;
Coord y = 0;
auto it = s.begin();
while (it != s.end()) {
Coord dx = 2;
if (*it == 's' || *it == 'n') {
y += (*it == 'n') - (*it == 's');
++it;
dx = 1;
assert(it != s.end());
}
if (*it == 'e') {
x += dx;
} else if (*it == 'w') {
x -= dx;
} else {
assert(false);
}
++it;
}
// Insert tile to be flipped - if we fail remove it.
auto [iit, success] = black_tiles_.insert({x, y});
if (!success) {
black_tiles_.erase(iit);
}
}
std::size_t black_count() const noexcept { return black_tiles_.size(); }
private:
Tiles black_tiles_;
};
int main(void) {
Tiler tiler;
std::string line;
while (std::getline(std::cin, line)) {
tiler.flip_tile(line);
}
std::cout << "Number of black tiles: " << tiler.black_count() << "\n";
return 0;
}

120
2020/puzzle-24-02.cc Normal file
View File

@@ -0,0 +1,120 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
#include <vector>
using Coord = long;
using Position = std::pair<Coord, Coord>;
struct State {
bool black_ = false;
int neighbours_ = 0;
};
using Tiles = std::map<Position, State>;
struct Tiler {
void flip_tile(std::string const &s) {
Coord x = 0;
Coord y = 0;
auto it = s.begin();
while (it != s.end()) {
Coord dx = 2;
if (*it == 's' || *it == 'n') {
y += (*it == 'n') - (*it == 's');
++it;
dx = 1;
assert(it != s.end());
}
if (*it == 'e') {
x += dx;
} else if (*it == 'w') {
x -= dx;
} else {
assert(false);
}
++it;
}
// Insert tile to be flipped - if we fail remove it.
flip_tile(x, y);
}
Tiler next_state() const {
Tiler next(*this);
for (auto const &kv : tiles_) {
if (kv.second.black_ &&
(kv.second.neighbours_ == 0 || kv.second.neighbours_ > 2)) {
next.flip_tile(kv.first.first, kv.first.second);
} else if (!kv.second.black_ && kv.second.neighbours_ == 2) {
next.flip_tile(kv.first.first, kv.first.second);
}
}
return next;
}
std::size_t black_count() const noexcept {
std::size_t count = 0;
for (auto const &kv : tiles_) {
count += kv.second.black_;
}
return count;
}
private:
void set_neighbours(Coord x, Coord y, int delta) {
set_neighbour(x + 2, y, delta);
set_neighbour(x + 1, y - 1, delta);
set_neighbour(x - 1, y - 1, delta);
set_neighbour(x - 2, y, delta);
set_neighbour(x - 1, y + 1, delta);
set_neighbour(x + 1, y + 1, delta);
}
void set_neighbour(Coord x, Coord y, int delta) {
auto [it, success] = tiles_.insert({{x, y}, {false, delta}});
if (!success) {
it->second.neighbours_ += delta;
}
assert(it->second.neighbours_ >= 0);
}
void flip_tile(Coord x, Coord y) {
auto [it, success] = tiles_.insert({{x, y}, {true, 0}});
if (!success) {
it->second.black_ = !it->second.black_;
}
if (it->second.black_) {
set_neighbours(x, y, 1);
} else {
set_neighbours(x, y, -1);
}
}
private:
Tiles tiles_;
};
int main(void) {
Tiler tiler;
std::string line;
while (std::getline(std::cin, line)) {
tiler.flip_tile(line);
}
std::cout << "Day 0: " << tiler.black_count() << "\n";
for (unsigned i = 1; i < 101; ++i) {
tiler = tiler.next_state();
std::cout << "Day " << i << ": " << tiler.black_count() << "\n";
}
return 0;
}

57
2020/puzzle-25-01.cc Normal file
View File

@@ -0,0 +1,57 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <set>
#include <string>
#include <tuple>
#include <vector>
constexpr unsigned mod = 20201227;
constexpr unsigned initial_subject = 7;
unsigned lcg(unsigned i, unsigned subject) {
return ((unsigned long)i * (unsigned long)subject) % mod;
}
unsigned find_loop_length(unsigned pk) {
unsigned v = 1;
unsigned loop = 0;
while (v != pk) {
v = lcg(v, initial_subject);
++loop;
}
return loop;
}
unsigned apply(unsigned pk, unsigned loop_size) {
unsigned v = 1;
for (unsigned i = 0; i < loop_size; ++i) {
v = lcg(v, pk);
}
return v;
}
int main(void) {
std::string line;
std::getline(std::cin, line);
unsigned pk1 = std::stoul(line);
std::getline(std::cin, line);
unsigned pk2 = std::stoul(line);
std::cout << "Public keys: " << pk1 << " " << pk2 << "\n";
auto l1 = find_loop_length(pk1);
auto l2 = find_loop_length(pk2);
std::cout << "Loop lengths: " << l1 << " " << l2 << "\n";
auto ek1 = apply(pk1, l2);
auto ek2 = apply(pk2, l1);
std::cout << "Encryption key: " << ek1 << " " << ek2 << "\n";
return 0;
}