mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Remove C++ solutions
This commit is contained in:
1
2019/.gitignore
vendored
1
2019/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
cmake-build-*
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
# Advent of Code 2019
|
||||
|
||||
This project contains my implementations for Advent of Code 2019. The
|
||||
goal is to create reasonably fast C++ implementations in readable and
|
||||
ergonomic C++. At the end of the contest, I will probably do a write-
|
||||
up of some sorts.
|
||||
|
||||
|
||||
## How to compile
|
||||
|
||||
Install the dependencies:
|
||||
|
||||
- [GTest](https://github.com/google/googletest) **Note:** this project
|
||||
by default tries to dynamically link GTest, and the Ubuntu packages
|
||||
only provide a statically linked archive. You may need to compile it
|
||||
for yourself.
|
||||
|
||||
```
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
You can then use the generated executable `runner`.
|
||||
|
||||
## Running tests
|
||||
|
||||
Tests can be executed with `make test`. The `tests` folder contains a
|
||||
`samples` folder. This folder contains pairs of `XX-Y-something.in` and
|
||||
`XX-Y-something.out`, which will be taken as the expected input and
|
||||
output of the implementations. You can add your own samples to this mix.
|
||||
@@ -1,26 +0,0 @@
|
||||
#include <iostream>
|
||||
#include "days.hpp"
|
||||
|
||||
static inline int required(int weight) {
|
||||
return weight / 3 - 2;
|
||||
}
|
||||
|
||||
void aoc2019::day01_part1(std::istream &input, std::ostream &output) {
|
||||
int total = 0;
|
||||
for (int current; input >> current;) {
|
||||
total += required(current);
|
||||
}
|
||||
|
||||
output << total << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day01_part2(std::istream &input, std::ostream &output) {
|
||||
int total = 0;
|
||||
for (int current; input >> current;) {
|
||||
for (int fuel = required(current); fuel > 0; fuel = required(fuel)) {
|
||||
total += fuel;
|
||||
}
|
||||
}
|
||||
|
||||
output << total << std::endl;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
static int run_program(std::vector<std::int64_t> program) {
|
||||
aoc2019::IntCodeComputer computer(std::move(program));
|
||||
computer.run();
|
||||
|
||||
return computer[0];
|
||||
}
|
||||
|
||||
void aoc2019::day02_part1(std::istream &input, std::ostream &output) {
|
||||
auto program = IntCodeComputer::read_intcode(input);
|
||||
program[1] = 12;
|
||||
program[2] = 2;
|
||||
output << run_program(std::move(program)) << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day02_part2(std::istream &input, std::ostream &output) {
|
||||
auto program = IntCodeComputer::read_intcode(input);
|
||||
|
||||
for (int noun = 0; noun < 100; ++noun) {
|
||||
for (int verb = 0; verb < 100; ++verb) {
|
||||
program[1] = noun;
|
||||
program[2] = verb;
|
||||
if (run_program(program) == 19690720) {
|
||||
output << 100 * noun + verb << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw std::domain_error("No valid solution.");
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
#include <cassert>
|
||||
#include <charconv>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "days.hpp"
|
||||
#include "point.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace {
|
||||
typedef aoc2019::Point<int, 2> point_t;
|
||||
|
||||
const std::unordered_map<char, point_t> DIRECTION_MAP = {
|
||||
{'U', {0, -1}},
|
||||
{'D', {0, 1}},
|
||||
{'L', {-1, 0}},
|
||||
{'R', {1, 0}},
|
||||
};
|
||||
|
||||
std::unordered_map<point_t, int> get_points(std::string_view line) {
|
||||
std::unordered_map<point_t, int> points{};
|
||||
point_t pos = {};
|
||||
|
||||
int steps = 0;
|
||||
for (auto entry = aoc2019::strtok(line); !line.empty() || !entry.empty(); entry = aoc2019::strtok(line)) {
|
||||
const auto dir = DIRECTION_MAP.at(entry[0]);
|
||||
std::size_t amount = 0;
|
||||
aoc2019::from_chars(entry.substr(1), amount);
|
||||
assert(amount > 0 && "Must have some valid direction");
|
||||
|
||||
for (std::size_t i = 0; i < amount; ++i) {
|
||||
++steps;
|
||||
pos += dir;
|
||||
if (!points.count(pos)) {
|
||||
points[pos] = steps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
std::pair<std::unordered_map<point_t, int>, std::unordered_map<point_t, int>> read_input(std::istream& input) {
|
||||
std::string buffer;
|
||||
std::getline(input, buffer);
|
||||
auto a = get_points(buffer);
|
||||
std::getline(input, buffer);
|
||||
auto b = get_points(buffer);
|
||||
|
||||
return { std::move(a), std::move(b) };
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day03_part1(std::istream &input, std::ostream &output) {
|
||||
auto [a, b] = read_input(input);
|
||||
|
||||
int best = std::numeric_limits<int>::max();
|
||||
|
||||
for (const auto& point : a) {
|
||||
if (b.count(point.first) && point.first.l1() < best) {
|
||||
best = point.first.l1();
|
||||
}
|
||||
}
|
||||
|
||||
output << best << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day03_part2(std::istream &input, std::ostream &output) {
|
||||
auto [a, b] = read_input(input);
|
||||
|
||||
int best = std::numeric_limits<int>::max();
|
||||
|
||||
for (const auto& ap : a) {
|
||||
const auto bp = b.find(ap.first);
|
||||
|
||||
if (bp != b.cend() && (ap.second + bp->second) < best) {
|
||||
best = ap.second + bp->second;
|
||||
}
|
||||
}
|
||||
|
||||
output << best << std::endl;
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include "days.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr bool is_valid_pass(int num) {
|
||||
bool has_double = false;
|
||||
int prev = 11;
|
||||
|
||||
for (; num != 0; num /= 10) {
|
||||
int digit = num % 10;
|
||||
|
||||
if (digit == prev) {
|
||||
has_double = true;
|
||||
}
|
||||
|
||||
if (digit > prev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prev = digit;
|
||||
}
|
||||
|
||||
return has_double;
|
||||
}
|
||||
|
||||
constexpr bool is_valid_pass2(int num) {
|
||||
int prev = 11;
|
||||
bool has_double = false;
|
||||
int run = 1;
|
||||
|
||||
for (; num != 0; num /= 10) {
|
||||
int digit = num % 10;
|
||||
|
||||
if (digit == prev) {
|
||||
++run;
|
||||
} else {
|
||||
if (run == 2) {
|
||||
has_double = true;
|
||||
}
|
||||
run = 1;
|
||||
}
|
||||
|
||||
if (digit > prev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prev = digit;
|
||||
}
|
||||
|
||||
return has_double || run == 2;
|
||||
}
|
||||
|
||||
std::pair<int, int> read_input(std::istream& input) {
|
||||
int a, b;
|
||||
input >> a;
|
||||
input.ignore();
|
||||
input >> b;
|
||||
|
||||
return {a, b};
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day04_part1(std::istream &input, std::ostream &output) {
|
||||
auto [start_range, end_range] = read_input(input);
|
||||
|
||||
int num_valid = 0;
|
||||
for (; start_range <= end_range; ++start_range) {
|
||||
num_valid += is_valid_pass(start_range);
|
||||
}
|
||||
|
||||
output << num_valid << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day04_part2(std::istream &input, std::ostream &output) {
|
||||
auto [start_range, end_range] = read_input(input);
|
||||
|
||||
int num_valid = 0;
|
||||
for (; start_range <= end_range; ++start_range) {
|
||||
num_valid += is_valid_pass2(start_range);
|
||||
}
|
||||
|
||||
output << num_valid << std::endl;
|
||||
}
|
||||
|
||||
// Poor man's unit tests
|
||||
static_assert(is_valid_pass(122345));
|
||||
static_assert(is_valid_pass(111111));
|
||||
static_assert(!is_valid_pass(223450));
|
||||
static_assert(!is_valid_pass(123678));
|
||||
|
||||
static_assert(is_valid_pass2(112233));
|
||||
static_assert(!is_valid_pass2(123444));
|
||||
static_assert(is_valid_pass2(111122));
|
||||
@@ -1,15 +0,0 @@
|
||||
#include <iostream>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
void aoc2019::day05_part1(std::istream &input, std::ostream &output) {
|
||||
auto program = IntCodeComputer::read_intcode(input);
|
||||
auto result = run_intcode(program, { 1 });
|
||||
output << result.back() << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day05_part2(std::istream &input, std::ostream &output) {
|
||||
auto program = IntCodeComputer::read_intcode(input);
|
||||
auto result = run_intcode(program, { 5 });
|
||||
output << result.back() << std::endl;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "days.hpp"
|
||||
|
||||
namespace {
|
||||
std::vector<std::pair<std::string, std::string>> read_orbits(std::istream &input) {
|
||||
std::vector<std::pair<std::string, std::string>> result;
|
||||
std::string name1, name2;
|
||||
|
||||
while (std::getline(input, name1, ')')) {
|
||||
std::getline(input, name2);
|
||||
|
||||
result.emplace_back(name1, name2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day06_part1(std::istream &input, std::ostream &output) {
|
||||
std::unordered_map<std::string, std::vector<std::string>> orbits;
|
||||
|
||||
for (auto[a, b] : read_orbits(input)) {
|
||||
orbits[std::move(a)].emplace_back(std::move(b));
|
||||
}
|
||||
|
||||
std::deque<std::pair<std::string, int>> todo = {{"COM", 0}};
|
||||
int total_orbits = 0;
|
||||
|
||||
while (!todo.empty()) {
|
||||
auto[name, offset] = todo.front();
|
||||
todo.pop_front();
|
||||
|
||||
total_orbits += offset;
|
||||
|
||||
for (const auto& partner : orbits[name]) {
|
||||
todo.emplace_back(partner, offset + 1);
|
||||
}
|
||||
}
|
||||
|
||||
output << total_orbits << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day06_part2(std::istream &input, std::ostream &output) {
|
||||
std::unordered_map<std::string, std::string> ancestors;
|
||||
|
||||
for (auto[a, b] : read_orbits(input)) {
|
||||
ancestors[std::move(b)] = std::move(a);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, int> santa_ancestors;
|
||||
|
||||
for (auto current = ancestors["SAN"]; current != "COM"; current = ancestors[current]) {
|
||||
santa_ancestors[ancestors[current]] = santa_ancestors[current] + 1;
|
||||
}
|
||||
|
||||
int dist = 0;
|
||||
for (auto current = ancestors["YOU"]; current != "COM"; current = ancestors[current], ++dist) {
|
||||
if (auto it = santa_ancestors.find(current); it != santa_ancestors.end()) {
|
||||
output << dist + it->second << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::domain_error("No valid path.");
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace {
|
||||
using aoc2019::IntCodeComputer;
|
||||
|
||||
std::int64_t simulate(const std::vector<std::int64_t> &program, const std::array<std::int64_t, 5> &phases) {
|
||||
std::int64_t state = 0;
|
||||
for (auto phase : phases) {
|
||||
std::deque<std::int64_t> outputs;
|
||||
IntCodeComputer computer{program, {phase, state}};
|
||||
computer.connectOutput(outputs);
|
||||
computer.run();
|
||||
state = outputs.front();
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
int simulate2(const std::vector<std::int64_t> &program, const std::array<int, 5> &phases) {
|
||||
std::vector<IntCodeComputer> computers;
|
||||
for (int phase : phases) {
|
||||
computers.emplace_back(program, std::deque<int64_t>{phase});
|
||||
}
|
||||
|
||||
for (int i = 0; i < computers.size(); ++i) {
|
||||
computers[i].connectOutput(computers[(i + 1) % 5]);
|
||||
}
|
||||
|
||||
computers[0].sendInput(0);
|
||||
|
||||
while (std::any_of(computers.begin(), computers.end(), [](const auto &c) { return !c.isTerminated();})) {
|
||||
for (auto& computer : computers) {
|
||||
computer.run();
|
||||
}
|
||||
}
|
||||
|
||||
return computers[0].currentInputs().back();
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day07_part1(std::istream &input, std::ostream &output) {
|
||||
const auto program = aoc2019::IntCodeComputer::read_intcode(input);
|
||||
std::array<std::int64_t, 5> phases{0, 1, 2, 3, 4};
|
||||
|
||||
std::int64_t best = 0;
|
||||
|
||||
do {
|
||||
best = std::max(simulate(program, phases), best);
|
||||
} while (std::next_permutation(phases.begin(), phases.end()));
|
||||
|
||||
output << best << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day07_part2(std::istream &input, std::ostream &output) {
|
||||
const auto program = aoc2019::IntCodeComputer::read_intcode(input);
|
||||
std::array<int, 5> phases{5, 6, 7, 8, 9};
|
||||
|
||||
int best = 0;
|
||||
|
||||
do {
|
||||
best = std::max(simulate2(program, phases), best);
|
||||
} while (std::next_permutation(phases.begin(), phases.end()));
|
||||
|
||||
output << best << std::endl;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "days.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr std::size_t WIDTH = 25;
|
||||
constexpr std::size_t HEIGHT = 6;
|
||||
constexpr std::size_t TILE_SIZE = WIDTH * HEIGHT;
|
||||
|
||||
enum Color {
|
||||
BLACK = '0',
|
||||
WHITE = '1',
|
||||
TRANSPARENT = '2',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void aoc2019::day08_part1(std::istream &input, std::ostream &output) {
|
||||
std::string buffer;
|
||||
std::getline(input, buffer);
|
||||
|
||||
std::string_view image = buffer;
|
||||
auto best = std::numeric_limits<int>::max();
|
||||
auto best_score = 0;
|
||||
|
||||
for (std::size_t i = 0; i < buffer.length(); i += TILE_SIZE) {
|
||||
auto tile = image.substr(i, TILE_SIZE);
|
||||
|
||||
auto zeros = std::count(tile.begin(), tile.end(), '0');
|
||||
|
||||
if (zeros < best) {
|
||||
best = zeros;
|
||||
|
||||
best_score = std::count(tile.begin(), tile.end(), '1') * std::count(tile.begin(), tile.end(), '2');
|
||||
}
|
||||
}
|
||||
|
||||
output << best_score << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day08_part2(std::istream &input, std::ostream &output) {
|
||||
std::string buffer;
|
||||
std::getline(input, buffer);
|
||||
|
||||
std::string_view image = buffer;
|
||||
|
||||
std::array<Color, TILE_SIZE> final_image;
|
||||
std::fill(final_image.begin(), final_image.end(), TRANSPARENT);
|
||||
|
||||
for (std::size_t i = 0; i < buffer.length(); i += TILE_SIZE) {
|
||||
auto tile = image.substr(i, TILE_SIZE);
|
||||
|
||||
for (int j = 0; j < TILE_SIZE; ++j) {
|
||||
if (final_image[j] == TRANSPARENT) {
|
||||
final_image[j] = static_cast<Color>(tile[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < final_image.size(); ++i) {
|
||||
output << (final_image[i] == WHITE ? '#' : ' ');
|
||||
if (i % WIDTH == WIDTH - 1) {
|
||||
output << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <deque>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
void aoc2019::day09_part1(std::istream &input, std::ostream &output) {
|
||||
std::deque<std::int64_t> outputs;
|
||||
|
||||
IntCodeComputer computer(input, { 1 });
|
||||
computer.connectOutput(outputs);
|
||||
|
||||
computer.run();
|
||||
|
||||
if (outputs.size() != 1) {
|
||||
std::cerr << "Error: " << outputs.size() << std::endl;
|
||||
for (auto c : outputs) {
|
||||
std::cerr << c << std::endl;
|
||||
}
|
||||
} else {
|
||||
output << outputs.front() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day09_part2(std::istream &input, std::ostream &output) {
|
||||
std::deque<std::int64_t> outputs;
|
||||
|
||||
IntCodeComputer computer(input, { 2 });
|
||||
computer.connectOutput(outputs);
|
||||
|
||||
computer.run();
|
||||
|
||||
output << outputs.front() << std::endl;
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <unordered_set>
|
||||
#include <cmath>
|
||||
#include "days.hpp"
|
||||
#include "point.hpp"
|
||||
|
||||
namespace {
|
||||
typedef aoc2019::Point<int, 2> point_t;
|
||||
|
||||
std::vector<point_t> read_points(std::istream &input) {
|
||||
std::vector<point_t> result;
|
||||
|
||||
int y = 0;
|
||||
|
||||
for (std::string buffer; std::getline(input, buffer); ++y) {
|
||||
std::size_t x = 0;
|
||||
|
||||
while ((x = buffer.find('#', x)) != std::string::npos) {
|
||||
result.push_back({(int) x, y});
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
point_t simplify(point_t x) {
|
||||
auto gcd = std::abs(std::gcd(x[0], x[1]));
|
||||
if (gcd > 1) {
|
||||
return {x[0] / gcd, x[1] / gcd};
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
std::pair<std::size_t, std::size_t> part1(const std::vector<point_t> &points) {
|
||||
std::size_t best = 0;
|
||||
std::size_t best_index = 0;
|
||||
std::unordered_set<point_t> visible;
|
||||
|
||||
for (std::size_t i = 0; i < points.size(); ++i) {
|
||||
visible.clear();
|
||||
|
||||
const auto point = points[i];
|
||||
|
||||
for (auto asteroid : points) {
|
||||
if (asteroid == point) continue;
|
||||
visible.insert(simplify(asteroid - point));
|
||||
}
|
||||
|
||||
if (visible.size() > best) {
|
||||
best = visible.size();
|
||||
best_index = i;
|
||||
}
|
||||
|
||||
best = std::max(visible.size(), best);
|
||||
}
|
||||
|
||||
return {best, best_index};
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day10_part1(std::istream &input, std::ostream &output) {
|
||||
const auto points = read_points(input);
|
||||
|
||||
auto[best, _] = part1(points);
|
||||
|
||||
output << best << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day10_part2(std::istream &input, std::ostream &output) {
|
||||
const auto points = read_points(input);
|
||||
const auto[_, base] = part1(points);
|
||||
const auto base_point = points[base];
|
||||
|
||||
std::unordered_map<point_t, std::vector<point_t>> angle_points;
|
||||
|
||||
for (auto point : points) {
|
||||
if (point == base_point) continue;
|
||||
auto diff = point - base_point;
|
||||
|
||||
angle_points[simplify(diff)].push_back(diff);
|
||||
}
|
||||
|
||||
std::vector<std::pair<float, point_t>> angles;
|
||||
|
||||
for (auto &entry : angle_points) {
|
||||
angles.emplace_back(std::atan2(entry.first[1], entry.first[0]), entry.first);
|
||||
// Sort entries in descending order of distance so we can pop_back() them
|
||||
std::sort(entry.second.begin(), entry.second.end(), [](auto a, auto b) { return a.l1() > b.l1(); });
|
||||
}
|
||||
|
||||
std::sort(angles.begin(), angles.end(), std::greater<>{});
|
||||
|
||||
const auto starting_point = std::make_pair(float(0.5 * M_PI),
|
||||
point_t{std::numeric_limits<int>::max(),
|
||||
std::numeric_limits<int>::max()});
|
||||
|
||||
auto it = std::lower_bound(angles.begin(), angles.end(), starting_point, std::greater<>{});
|
||||
|
||||
for (int hits = 0; hits < 199; ++hits) {
|
||||
angle_points[it->second].pop_back();
|
||||
|
||||
// Advance it to the next asteroid we can hit.
|
||||
while (angle_points[it->second].empty()) {
|
||||
++it;
|
||||
if (it == angles.end()) {
|
||||
it = angles.begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto final_asteroid = angle_points[it->second].back() + base_point;
|
||||
|
||||
output << final_asteroid[0] * 100 + final_asteroid[1] << std::endl;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "point.hpp"
|
||||
|
||||
namespace {
|
||||
typedef aoc2019::Point<int, 2> point_t;
|
||||
using aoc2019::IntCodeComputer;
|
||||
|
||||
inline point_t turn_right(point_t direction) {
|
||||
return {-direction[1], direction[0]};
|
||||
}
|
||||
|
||||
inline point_t turn_left(point_t direction) {
|
||||
return {direction[1], -direction[0]};
|
||||
}
|
||||
|
||||
std::unordered_map<point_t, bool> simulate(std::istream &input, bool background = false) {
|
||||
std::unordered_map<point_t, bool> image;
|
||||
|
||||
point_t direction{0, -1};
|
||||
point_t pos = {0, 0};
|
||||
|
||||
IntCodeComputer computer(IntCodeComputer::read_intcode(input), {});
|
||||
std::deque<std::int64_t> outputs;
|
||||
|
||||
computer.connectOutput(outputs);
|
||||
|
||||
while (!computer.isTerminated()) {
|
||||
const auto it = image.find(pos);
|
||||
computer.sendInput(it != image.end() ? it->second : background);
|
||||
computer.run();
|
||||
|
||||
if (!outputs.empty()) {
|
||||
assert(outputs.size() == 2);
|
||||
auto color = outputs.front();
|
||||
auto turn = outputs.back();
|
||||
outputs.clear();
|
||||
|
||||
image[pos] = color;
|
||||
|
||||
if (turn) {
|
||||
direction = turn_right(direction);
|
||||
} else {
|
||||
direction = turn_left(direction);
|
||||
}
|
||||
|
||||
pos += direction;
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day11_part1(std::istream &input, std::ostream &output) {
|
||||
const auto result = simulate(input);
|
||||
|
||||
output << result.size() << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day11_part2(std::istream &input, std::ostream &output) {
|
||||
const auto result = simulate(input, true);
|
||||
|
||||
// Determine bounding box
|
||||
auto[lower,upper] = aoc2019::bounding_box(result);
|
||||
|
||||
for (int y = lower[1]; y <= upper[1]; ++y) {
|
||||
for (int x = lower[0]; x <= upper[0]; ++x) {
|
||||
if (auto it = result.find({x, y}); it != result.end() && it->second) {
|
||||
output << '#';
|
||||
} else {
|
||||
output << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
output << '\n';
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include "days.hpp"
|
||||
#include "point.hpp"
|
||||
|
||||
namespace {
|
||||
typedef aoc2019::Point<int, 3> point_t;
|
||||
using aoc2019::from_chars;
|
||||
|
||||
std::vector<point_t> read_moons(std::istream &input) {
|
||||
std::vector<point_t> moons;
|
||||
|
||||
point_t moon;
|
||||
|
||||
while (aoc2019::read_line_numbers_and_garbage<int>(input, moon.begin())) {
|
||||
moons.push_back(moon);
|
||||
}
|
||||
|
||||
return moons;
|
||||
}
|
||||
|
||||
void update_velocity(const point_t &a, point_t &va, const point_t &b, point_t &vb) {
|
||||
for (int i = 0; i < a.size(); ++i) {
|
||||
if (a[i] < b[i]) {
|
||||
va[i]++;
|
||||
vb[i]--;
|
||||
} else if (a[i] > b[i]) {
|
||||
va[i]--;
|
||||
vb[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_velocities(const std::vector<point_t> &positions, std::vector<point_t> &velocities) {
|
||||
for (int i = 0; i < positions.size(); ++i) {
|
||||
for (int j = i + 1; j < positions.size(); ++j) {
|
||||
update_velocity(positions[i], velocities[i], positions[j], velocities[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void simulate_step(std::vector<point_t> &moons, std::vector<point_t> &velocities) {
|
||||
update_velocities(moons, velocities);
|
||||
|
||||
for (int j = 0; j < moons.size(); ++j) {
|
||||
moons[j] += velocities[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day12_part1(std::istream &input, std::ostream &output) {
|
||||
auto moons = read_moons(input);
|
||||
std::vector<point_t> velocities(moons.size());
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
simulate_step(moons, velocities);
|
||||
}
|
||||
|
||||
int energy = 0;
|
||||
|
||||
for (int i = 0; i < moons.size(); ++i) {
|
||||
energy += moons[i].l1() * velocities[i].l1();
|
||||
}
|
||||
|
||||
output << energy << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day12_part2(std::istream &input, std::ostream &output) {
|
||||
const auto moons = read_moons(input);
|
||||
auto moons_mut = moons;
|
||||
std::vector<point_t> velocities(moons.size());
|
||||
|
||||
std::array<uint64_t, 3> recurrence = {0, 0, 0};
|
||||
|
||||
std::uint64_t steps = 0;
|
||||
|
||||
while (!std::all_of(recurrence.begin(), recurrence.end(), [](auto x) { return x > 0; })) {
|
||||
simulate_step(moons_mut, velocities);
|
||||
++steps;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (!recurrence[i]) {
|
||||
bool back_again =
|
||||
std::all_of(velocities.begin(), velocities.end(), [i](const auto &x) { return !x[i]; })
|
||||
&& std::equal(moons_mut.begin(), moons_mut.end(), moons.begin(),
|
||||
[i](const auto &a, const auto &b) {
|
||||
return a[i] == b[i];
|
||||
});
|
||||
|
||||
if (back_again) {
|
||||
recurrence[i] = steps;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto result = std::lcm(recurrence[0], std::lcm(recurrence[1], recurrence[2]));
|
||||
output << result << std::endl;
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
#include <iostream>
|
||||
#ifdef ANIMATE_DAY13
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#endif
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "point.hpp"
|
||||
|
||||
namespace {
|
||||
typedef aoc2019::Point<int64_t, 2> point_t;
|
||||
|
||||
enum class Tile {
|
||||
EMPTY,
|
||||
WALL,
|
||||
BLOCK,
|
||||
PADDLE,
|
||||
BALL,
|
||||
};
|
||||
|
||||
typedef std::unordered_map<point_t, Tile> Screen;
|
||||
|
||||
std::optional<std::int64_t> update_screen(std::deque<std::int64_t> &output_buffer, Screen &screen) {
|
||||
std::optional<std::int64_t> score;
|
||||
while (!output_buffer.empty()) {
|
||||
auto x = output_buffer.front(); output_buffer.pop_front();
|
||||
auto y = output_buffer.front(); output_buffer.pop_front();
|
||||
auto type = output_buffer.front(); output_buffer.pop_front();
|
||||
|
||||
if (x == -1 && y == 0) {
|
||||
score = type;
|
||||
continue;
|
||||
}
|
||||
|
||||
screen[{x, y}] = static_cast<Tile>(type);
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
void draw_screen(const Screen &screen, std::ostream& output) {
|
||||
// Determine bounding box
|
||||
using limits = std::numeric_limits<int>;
|
||||
|
||||
const auto [lower, upper] = aoc2019::bounding_box(screen);
|
||||
|
||||
for (auto y = lower[1]; y <= upper[1]; ++y) {
|
||||
for (auto x = lower[0]; x <= upper[0]; ++x) {
|
||||
char c = ' ';
|
||||
if (auto it = screen.find({x, y}); it != screen.end()) {
|
||||
switch (it->second) {
|
||||
case Tile::EMPTY:
|
||||
c = ' ';
|
||||
break;
|
||||
|
||||
case Tile::BALL:
|
||||
c = '*';
|
||||
break;
|
||||
|
||||
case Tile::BLOCK:
|
||||
c = '=';
|
||||
break;
|
||||
|
||||
case Tile::PADDLE:
|
||||
c = '_';
|
||||
break;
|
||||
|
||||
case Tile::WALL:
|
||||
c = '#';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output << c;
|
||||
}
|
||||
|
||||
output << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
auto find_pos(const Screen &screen, Tile to_find) {
|
||||
return std::find_if(screen.begin(), screen.end(), [to_find](const auto& x) {
|
||||
return x.second == to_find;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day13_part1(std::istream &input, std::ostream &output) {
|
||||
Screen screen;
|
||||
aoc2019::IntCodeComputer computer(aoc2019::IntCodeComputer::read_intcode(input));
|
||||
std::deque<std::int64_t> output_buffer;
|
||||
computer.connectOutput(output_buffer);
|
||||
computer.run();
|
||||
update_screen(output_buffer, screen);
|
||||
|
||||
output << std::count_if(screen.begin(), screen.end(), [](const auto &x) { return x.second == Tile::BLOCK; })
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day13_part2(std::istream &input, std::ostream &output) {
|
||||
auto program = aoc2019::IntCodeComputer::read_intcode(input);
|
||||
program[0] = 2;
|
||||
|
||||
aoc2019::IntCodeComputer computer(std::move(program));
|
||||
std::deque<std::int64_t> output_buffer;
|
||||
computer.connectOutput(output_buffer);
|
||||
computer.run();
|
||||
|
||||
Screen screen;
|
||||
|
||||
std::int64_t score = 0;
|
||||
|
||||
while (!computer.isTerminated()) {
|
||||
computer.run();
|
||||
auto new_score = update_screen(output_buffer, screen);
|
||||
if (new_score) {
|
||||
score = *new_score;
|
||||
}
|
||||
|
||||
#ifdef ANIMATE_DAY13
|
||||
output << "Score: " << score << std::endl;
|
||||
draw_screen(screen, output);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(40));
|
||||
#endif
|
||||
|
||||
auto ball_pos = find_pos(screen, Tile::BALL)->first;
|
||||
auto paddle_pos = find_pos(screen, Tile::PADDLE)->first;
|
||||
|
||||
if (ball_pos[0] < paddle_pos[0]) {
|
||||
computer.sendInput(-1);
|
||||
} else if (ball_pos[0] > paddle_pos[0]) {
|
||||
computer.sendInput(1);
|
||||
} else {
|
||||
computer.sendInput(0);
|
||||
}
|
||||
}
|
||||
|
||||
output << score << std::endl;
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
#include <charconv>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace {
|
||||
typedef std::pair<std::string, std::int64_t> requirement_t;
|
||||
typedef std::vector<requirement_t> reqlist_t;
|
||||
|
||||
std::map<reqlist_t, reqlist_t> read_recipes(std::istream &input) {
|
||||
std::map<reqlist_t, reqlist_t> recipes;
|
||||
|
||||
std::string buffer;
|
||||
std::regex listing_regex("(\\d+) ([A-Z]+)");
|
||||
|
||||
std::int64_t amount;
|
||||
|
||||
while (std::getline(input, buffer)) {
|
||||
reqlist_t requirements, production;
|
||||
|
||||
std::string_view line = buffer;
|
||||
|
||||
auto split_point = line.find(" => ");
|
||||
|
||||
auto requirements_part = line.substr(0, split_point);
|
||||
auto production_part = line.substr(split_point + 4);
|
||||
|
||||
for (auto it = std::regex_token_iterator(requirements_part.begin(), requirements_part.end(), listing_regex,
|
||||
{1, 2}); it != std::cregex_token_iterator(); ++it) {
|
||||
std::from_chars(it->first, it->second, amount);
|
||||
++it;
|
||||
|
||||
requirements.emplace_back(*it, amount);
|
||||
}
|
||||
|
||||
for (auto it = std::regex_token_iterator(production_part.begin(), production_part.end(), listing_regex,
|
||||
{1, 2}); it != std::cregex_token_iterator(); ++it) {
|
||||
std::from_chars(it->first, it->second, amount);
|
||||
++it;
|
||||
|
||||
production.emplace_back(*it, amount);
|
||||
}
|
||||
|
||||
recipes[std::move(production)] = std::move(requirements);
|
||||
}
|
||||
|
||||
return recipes;
|
||||
}
|
||||
|
||||
template<class Map>
|
||||
std::unordered_map<std::string, reqlist_t> element_creators(const Map &map) {
|
||||
std::unordered_map<std::string, reqlist_t> inverted;
|
||||
|
||||
for (auto &entry : map) {
|
||||
for (auto &x : entry.first) {
|
||||
inverted[x.first] = entry.first;
|
||||
}
|
||||
}
|
||||
|
||||
return inverted;
|
||||
}
|
||||
|
||||
std::vector<std::string> topological_order(const std::map<reqlist_t, reqlist_t> &recipes) {
|
||||
std::vector<std::string> order;
|
||||
|
||||
std::unordered_map<std::string, std::vector<std::string>> edges;
|
||||
for (auto &entry : recipes) {
|
||||
for (auto &production : entry.first) {
|
||||
std::transform(entry.second.begin(), entry.second.end(), std::back_inserter(edges[production.first]),
|
||||
[](const auto &x) {
|
||||
return x.first;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return aoc2019::topological_sort(edges);
|
||||
}
|
||||
|
||||
std::int64_t ore_to_fuel(const std::map<reqlist_t, reqlist_t> &recipes, std::int64_t amount = 1) {
|
||||
auto inverted = element_creators(recipes);
|
||||
auto order = topological_order(recipes);
|
||||
|
||||
std::unordered_map<std::string_view, std::int64_t> total_requirements;
|
||||
total_requirements["FUEL"] = amount;
|
||||
|
||||
for (const auto &element : order) {
|
||||
if (element == "ORE") {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto number_required = total_requirements[element];
|
||||
if (number_required <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &productions = inverted.at(element);
|
||||
const auto &requirements = recipes.at(productions);
|
||||
|
||||
auto number_produced = std::find_if(productions.begin(), productions.end(),
|
||||
[element](const auto &x) { return x.first == element; })->second;
|
||||
|
||||
auto productions_needed = number_required / number_produced + (number_required % number_produced ? 1 : 0);
|
||||
|
||||
for (auto &requirement : requirements) {
|
||||
total_requirements[requirement.first] += requirement.second * productions_needed;
|
||||
}
|
||||
|
||||
for (auto &production : productions) {
|
||||
total_requirements[production.first] -= productions_needed * production.second;
|
||||
}
|
||||
}
|
||||
|
||||
return total_requirements["ORE"];
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day14_part1(std::istream &input, std::ostream &output) {
|
||||
auto recipes = read_recipes(input);
|
||||
|
||||
output << ore_to_fuel(recipes) << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day14_part2(std::istream &input, std::ostream &output) {
|
||||
auto recipes = read_recipes(input);
|
||||
|
||||
constexpr std::int64_t ore_stock = 1000000000000;
|
||||
|
||||
std::int64_t min = 1, max = ore_stock + 1; // assumption: 1 ore produces < 1 fuel.
|
||||
while (max - min > 1) {
|
||||
auto cur = (max + min) / 2;
|
||||
|
||||
if (ore_to_fuel(recipes, cur) < ore_stock) {
|
||||
min = cur;
|
||||
} else {
|
||||
max = cur - 1;
|
||||
}
|
||||
}
|
||||
|
||||
output << (max + min) / 2 << std::endl;
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <unordered_set>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "point.hpp"
|
||||
|
||||
namespace {
|
||||
typedef aoc2019::Point<int, 2> point_t;
|
||||
|
||||
enum class Tile {
|
||||
Wall,
|
||||
Empty,
|
||||
Oxygen,
|
||||
};
|
||||
|
||||
enum class Mark {
|
||||
None,
|
||||
Temp,
|
||||
Permanent,
|
||||
};
|
||||
|
||||
const std::unordered_map<point_t, std::int64_t> DIRECTIONS{
|
||||
{{0, -1}, 1},
|
||||
{{0, 1}, 2},
|
||||
{{-1, 0}, 3},
|
||||
{{1, 0}, 4},
|
||||
};
|
||||
|
||||
std::unordered_map<point_t, Tile> read_map(std::istream &input) {
|
||||
aoc2019::IntCodeComputer computer(input);
|
||||
std::deque<std::int64_t> output_buffer;
|
||||
computer.connectOutput(output_buffer);
|
||||
|
||||
point_t pos = {0, 0};
|
||||
std::deque<point_t> prev;
|
||||
std::unordered_map<point_t, Tile> map{{pos, Tile::Empty}};
|
||||
std::unordered_map<point_t, Mark> markings{{pos, Mark::Temp}};
|
||||
|
||||
computer.run();
|
||||
|
||||
while (true) {
|
||||
std::optional<point_t> next_step;
|
||||
|
||||
for (auto &direction : DIRECTIONS) {
|
||||
if (markings[pos + direction.first] == Mark::None) {
|
||||
next_step = direction.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (next_step) {
|
||||
const auto next_pos = pos + *next_step;
|
||||
computer.sendInput(DIRECTIONS.at(*next_step));
|
||||
computer.run();
|
||||
|
||||
assert(!output_buffer.empty());
|
||||
|
||||
switch (output_buffer.front()) {
|
||||
case 0:
|
||||
markings[next_pos] = Mark::Permanent;
|
||||
map[next_pos] = Tile::Wall;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
prev.push_front(pos);
|
||||
markings[next_pos] = Mark::Temp;
|
||||
map[next_pos] = static_cast<Tile>(output_buffer.front());
|
||||
pos = next_pos;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::domain_error("Invalid data from remote");
|
||||
}
|
||||
output_buffer.pop_front();
|
||||
assert(output_buffer.empty());
|
||||
} else {
|
||||
markings[pos] = Mark::Permanent;
|
||||
// Nowhere left to go, move back.
|
||||
if (prev.empty()) {
|
||||
return map;
|
||||
}
|
||||
|
||||
auto prev_pos = prev.front();
|
||||
auto step = DIRECTIONS.at(prev_pos - pos);
|
||||
prev.pop_front();
|
||||
computer.sendInput(step);
|
||||
computer.run();
|
||||
// We should be able to travel back
|
||||
assert(output_buffer.front() == 1);
|
||||
output_buffer.pop_front();
|
||||
pos = prev_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Callback>
|
||||
int bfs(const std::unordered_map<point_t, Tile> &map, point_t starting_point, Callback callback) {
|
||||
std::deque<std::pair<point_t, int>> todo{{starting_point, 0}};
|
||||
std::unordered_set<point_t> visited{{0, 0}};
|
||||
|
||||
int max_dist = 0;
|
||||
|
||||
while (!todo.empty()) {
|
||||
auto[cur, dist] = todo.front();
|
||||
todo.pop_front();
|
||||
|
||||
max_dist = std::max(max_dist, dist);
|
||||
|
||||
for (auto &dir : DIRECTIONS) {
|
||||
auto new_pos = cur + dir.first;
|
||||
if (!visited.count(new_pos)) {
|
||||
visited.insert(new_pos);
|
||||
|
||||
if (callback(map.at(new_pos))) {
|
||||
return dist + 1;
|
||||
}
|
||||
|
||||
switch (map.at(new_pos)) {
|
||||
case Tile::Oxygen:
|
||||
case Tile::Empty:
|
||||
todo.emplace_back(new_pos, dist + 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max_dist;
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day15_part1(std::istream &input, std::ostream &output) {
|
||||
const auto map = read_map(input);
|
||||
|
||||
auto dist = bfs(map, {0, 0}, [](Tile x) { return x == Tile::Oxygen; });
|
||||
|
||||
output << dist << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day15_part2(std::istream &input, std::ostream &output) {
|
||||
const auto map = read_map(input);
|
||||
|
||||
auto starting_point = std::find_if(map.begin(), map.end(), [](auto &x) { return x.second == Tile::Oxygen; })->first;
|
||||
|
||||
auto dist = bfs(map, starting_point, [](Tile x) { return false; });
|
||||
|
||||
output << dist << std::endl;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace {
|
||||
std::array<int, 4> base_pattern{0, 1, 0, -1};
|
||||
|
||||
int get_modifier(int rank, int pos) {
|
||||
pos += 1;
|
||||
pos /= rank + 1;
|
||||
|
||||
return base_pattern[pos % 4];
|
||||
}
|
||||
|
||||
std::vector<int> read_input(std::istream &input) {
|
||||
std::vector<int> result;
|
||||
|
||||
for (char c; input >> c;) {
|
||||
assert(std::isdigit(c));
|
||||
result.push_back(c - '0');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void simulate(std::vector<int> numbers, std::ostream &output) {
|
||||
std::vector<int> new_numbers(numbers.size());
|
||||
std::vector<int> partial_sums(numbers.size());
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
for (int rank = 0; rank < numbers.size(); ++rank) {
|
||||
std::partial_sum(numbers.begin() + rank, numbers.end(), partial_sums.begin() + rank);
|
||||
int n = 0;
|
||||
for (int pos = rank; pos < numbers.size(); pos += rank + 1) {
|
||||
int run = std::min(rank + 1, (int) numbers.size() - pos);
|
||||
if (int modifier = get_modifier(rank, pos); modifier) {
|
||||
n += modifier * (partial_sums[pos + run - 1] - partial_sums[pos] + numbers[pos]);
|
||||
}
|
||||
}
|
||||
|
||||
n = std::abs(n % 10);
|
||||
|
||||
new_numbers[rank] = n;
|
||||
}
|
||||
|
||||
std::swap(numbers, new_numbers);
|
||||
}
|
||||
|
||||
std::copy(numbers.begin(), numbers.begin() + 8, std::ostream_iterator<int>(output));
|
||||
output << std::endl;
|
||||
}
|
||||
|
||||
int get_offset(const std::vector<int> &numbers) {
|
||||
int offset = 0;
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
offset *= 10;
|
||||
offset += numbers[i];
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::vector<int> numbers_from_offset(const std::vector<int> &numbers, unsigned int offset) {
|
||||
constexpr auto repetitions = 10000;
|
||||
const auto desired_length = repetitions * numbers.size() - offset;
|
||||
|
||||
std::vector<int> numbers_after;
|
||||
numbers_after.reserve(desired_length);
|
||||
numbers_after.insert(numbers_after.end(), numbers.begin() + (offset % numbers.size()), numbers.end());
|
||||
|
||||
while (numbers_after.size() < desired_length) {
|
||||
auto remaining = desired_length - numbers_after.size();
|
||||
if (remaining >= numbers.size()) {
|
||||
numbers_after.insert(numbers_after.end(), numbers.begin(), numbers.end());
|
||||
} else {
|
||||
numbers_after.insert(numbers_after.end(), numbers.begin(), numbers.end() + remaining);
|
||||
}
|
||||
}
|
||||
|
||||
return numbers_after;
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day16_part1(std::istream &input, std::ostream &output) {
|
||||
auto numbers = read_input(input);
|
||||
|
||||
simulate(std::move(numbers), output);
|
||||
}
|
||||
|
||||
void aoc2019::day16_part2(std::istream &input, std::ostream &output) {
|
||||
auto numbers = read_input(input);
|
||||
|
||||
const int offset = get_offset(numbers);
|
||||
|
||||
numbers = numbers_from_offset(numbers, offset);
|
||||
|
||||
std::vector<int> new_numbers(numbers.size());
|
||||
std::vector<int> partial_sums(numbers.size());
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
std::partial_sum(numbers.rbegin(), numbers.rend(), partial_sums.rbegin());
|
||||
|
||||
std::transform(partial_sums.begin(), partial_sums.end(), new_numbers.begin(), [](int x) {
|
||||
return x % 10;
|
||||
});
|
||||
|
||||
std::swap(numbers, new_numbers);
|
||||
}
|
||||
|
||||
std::copy(numbers.begin(), numbers.begin() + 8, std::ostream_iterator<int>(output));
|
||||
output << std::endl;
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
#include <cassert>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "point.hpp"
|
||||
|
||||
namespace {
|
||||
typedef aoc2019::Point<int, 2> point_t;
|
||||
|
||||
const std::unordered_map<char, point_t> DIRECTIONS{
|
||||
{'^', {0, -1}},
|
||||
{'>', {0, 1}},
|
||||
{'v', {1, 0}},
|
||||
{'<', {-1, 0}},
|
||||
};
|
||||
|
||||
std::unordered_map<point_t, char> read_scaffold(const std::deque<std::int64_t> &data) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
std::unordered_map<point_t, char> map;
|
||||
for (auto n : data) {
|
||||
if (n == '\n') {
|
||||
if (x == 0) {
|
||||
// Double newline, end of map
|
||||
break;
|
||||
}
|
||||
++y;
|
||||
x = 0;
|
||||
continue;
|
||||
} else {
|
||||
map[{x, y}] = (char) n;
|
||||
++x;
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day17_part1(std::istream &input, std::ostream &output) {
|
||||
IntCodeComputer computer(input);
|
||||
std::deque<std::int64_t> output_buffer;
|
||||
computer.connectOutput(output_buffer);
|
||||
|
||||
computer.run();
|
||||
|
||||
const auto map = read_scaffold(output_buffer);
|
||||
|
||||
std::int64_t total = 0;
|
||||
|
||||
for (auto &entry : map) {
|
||||
if (entry.second == '.') continue;
|
||||
|
||||
bool is_intersection = std::all_of(DIRECTIONS.begin(), DIRECTIONS.end(), [&map, &entry](auto &x) {
|
||||
auto it = map.find(x.second + entry.first);
|
||||
return it != map.end() && it->second != '.';
|
||||
});
|
||||
|
||||
if (is_intersection) {
|
||||
total += entry.first[0] * entry.first[1];
|
||||
}
|
||||
}
|
||||
|
||||
output << total << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day17_part2(std::istream &input, std::ostream &output) {
|
||||
using namespace std::literals;
|
||||
|
||||
aoc2019::IntCodeComputer computer(input);
|
||||
computer[0] = 2;
|
||||
std::deque<std::int64_t> output_buffer;
|
||||
computer.connectOutput(output_buffer);
|
||||
|
||||
std::array<std::string_view, 3> programs = {
|
||||
"L,6,R,8,L,4,R,8,L,12\n",
|
||||
"L,12,R,10,L,4\n",
|
||||
"L,12,L,6,L,4,L,4\n",
|
||||
};
|
||||
|
||||
auto combined_programs = "A,B,B,C,B,C,B,C,A,A\n"sv;
|
||||
|
||||
computer.sendInputs(combined_programs);
|
||||
|
||||
for (auto program : programs) {
|
||||
computer.sendInputs(program);
|
||||
}
|
||||
|
||||
// Don't give me output.
|
||||
computer.sendInputs("n\n");
|
||||
|
||||
computer.run();
|
||||
|
||||
assert(!output_buffer.empty());
|
||||
|
||||
if (output_buffer.size() == 1) {
|
||||
output << output_buffer.front() << std::endl;
|
||||
} else {
|
||||
std::copy(output_buffer.begin(), output_buffer.end(), std::ostreambuf_iterator<char>(output));
|
||||
output << output_buffer.back() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import fileinput
|
||||
import sys
|
||||
|
||||
|
||||
def turn_left(direction):
|
||||
x, y = direction
|
||||
return (y, -x)
|
||||
|
||||
|
||||
def turn_right(direction):
|
||||
x, y = direction
|
||||
return (-y, x)
|
||||
|
||||
|
||||
def add(pos, direction):
|
||||
return tuple(a + b for a, b in zip(pos, direction))
|
||||
|
||||
|
||||
def main():
|
||||
chart = [line.strip() for line in fileinput.input()]
|
||||
|
||||
pos = None
|
||||
|
||||
for y, line in enumerate(chart):
|
||||
x = line.find('^')
|
||||
if x >= 0:
|
||||
pos = (x, y)
|
||||
break
|
||||
|
||||
if not pos:
|
||||
sys.exit('starting point not found')
|
||||
|
||||
route = ['L']
|
||||
|
||||
direction = (-1, 0)
|
||||
|
||||
def bounds_check(pos):
|
||||
x, y = pos
|
||||
|
||||
return x >= 0 and y >= 0 and y < len(chart)
|
||||
|
||||
while True:
|
||||
# try to move forward
|
||||
next_pos = add(direction, pos)
|
||||
dist = 0
|
||||
|
||||
while bounds_check(next_pos) and chart[next_pos[1]][next_pos[0]] == '#':
|
||||
dist += 1
|
||||
pos = next_pos
|
||||
next_pos = add(direction, pos)
|
||||
|
||||
if dist:
|
||||
route.append(dist)
|
||||
else:
|
||||
break
|
||||
|
||||
for move, new_dir in zip(('L', 'R'), (turn_left(direction), turn_right(direction))):
|
||||
next_pos = add(pos, new_dir)
|
||||
if bounds_check(next_pos) and chart[next_pos[1]][next_pos[0]] == '#':
|
||||
route.append(move)
|
||||
direction = new_dir
|
||||
break
|
||||
|
||||
printable_route = []
|
||||
for x in route:
|
||||
if x == 'L' or x == 'R':
|
||||
printable_route.append(x)
|
||||
else:
|
||||
printable_route += ['M'] * x
|
||||
|
||||
print(','.join(str(x) for x in route))
|
||||
print(','.join(printable_route))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,226 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <bit>
|
||||
#include <map>
|
||||
#include "days.hpp"
|
||||
#include "point.hpp"
|
||||
|
||||
static_assert(sizeof(int) >= 4, "Int should be at least 32 bits.");
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace {
|
||||
typedef aoc2019::Point<int, 2> point_t;
|
||||
|
||||
typedef std::vector<std::string> map_t;
|
||||
|
||||
std::array<point_t, 4> DIRECTIONS = {{
|
||||
{0, -1},
|
||||
{0, 1},
|
||||
{-1, 0},
|
||||
{1, 0},
|
||||
}};
|
||||
|
||||
map_t read_map(std::istream &input) {
|
||||
std::string buffer;
|
||||
map_t map;
|
||||
|
||||
while (std::getline(input, buffer)) {
|
||||
map.push_back(buffer);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
point_t find(const std::vector<std::string> &map, char needle) {
|
||||
for (int y = 0; y < map.size(); ++y) {
|
||||
auto x = map[y].find(needle);
|
||||
if (x != std::string::npos) {
|
||||
return {(int) x, y};
|
||||
}
|
||||
}
|
||||
|
||||
throw std::invalid_argument("Can't find it!");
|
||||
}
|
||||
|
||||
std::vector<std::pair<char, int>> find_edges(const map_t &map, point_t starting_point) {
|
||||
std::vector<std::pair<char, int>> edges;
|
||||
std::queue<std::pair<int, point_t>> todo;
|
||||
todo.emplace(0, starting_point);
|
||||
|
||||
std::unordered_set<point_t> visited{starting_point};
|
||||
|
||||
while (!todo.empty()) {
|
||||
const auto[dist, pos] = todo.front();
|
||||
todo.pop();
|
||||
|
||||
for (auto &direction : DIRECTIONS) {
|
||||
auto next_pos = pos + direction;
|
||||
const char at = map[next_pos[1]][next_pos[0]];
|
||||
|
||||
if (at == '#' || visited.count(next_pos)) {
|
||||
// Wall or already visited, ignore
|
||||
continue;
|
||||
}
|
||||
|
||||
visited.insert(next_pos);
|
||||
|
||||
if (std::isalpha(at)) {
|
||||
// Don't walk through stuff
|
||||
edges.emplace_back(at, dist + 1);
|
||||
} else {
|
||||
todo.emplace(dist + 1, next_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
auto compute_implied_graph(const map_t &map) {
|
||||
std::unordered_map<char, std::vector<std::pair<char, int>>> implied_graph;
|
||||
|
||||
for (int y = 0; y < map.size(); ++y) {
|
||||
for (int x = 0; x < map[y].size(); ++x) {
|
||||
char at = map[y][x];
|
||||
if ("@/^*"sv.find(at) != std::string_view::npos || std::isalpha(at)) {
|
||||
implied_graph[at] = find_edges(map, {x, y});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return implied_graph;
|
||||
}
|
||||
|
||||
inline unsigned int key_index(char c) {
|
||||
return 1u << static_cast<unsigned int>(c - 'A');
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day18_part1(std::istream &input, std::ostream &output) {
|
||||
using state_t = std::tuple<unsigned int, char>;
|
||||
|
||||
const auto map = read_map(input);
|
||||
|
||||
auto implied_graph = compute_implied_graph(map);
|
||||
|
||||
std::priority_queue<std::pair<int, state_t>, std::vector<std::pair<int, state_t>>, std::greater<>> todo;
|
||||
std::map<state_t, int> visited;
|
||||
todo.emplace(0, std::make_pair(0, '@'));
|
||||
|
||||
auto target_size = std::count_if(implied_graph.cbegin(), implied_graph.cend(),
|
||||
[](auto &x) { return std::islower(x.first); });
|
||||
|
||||
while (!todo.empty()) {
|
||||
const auto[dist, state] = todo.top();
|
||||
todo.pop();
|
||||
|
||||
if (visited[state] < dist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto[keys, pos] = state;
|
||||
|
||||
if (std::__popcount(keys) == target_size) {
|
||||
output << dist << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &edge : implied_graph.at(pos)) {
|
||||
auto next_dist = dist + edge.second;
|
||||
auto next_keys = keys;
|
||||
if (std::islower(edge.first)) {
|
||||
// Add the key to our collection
|
||||
next_keys |= key_index(edge.first);;
|
||||
} else if (std::isupper(edge.first)) {
|
||||
// Check if we have the required key already
|
||||
if (!(next_keys & key_index(edge.first))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
state_t next_state = {next_keys, edge.first};
|
||||
if (auto it = visited.find(next_state); it == visited.end() || it->second > next_dist) {
|
||||
visited[next_state] = next_dist;
|
||||
todo.emplace(next_dist, next_state);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
throw std::logic_error("Should have terminated by now.");
|
||||
}
|
||||
|
||||
void aoc2019::day18_part2(std::istream &input, std::ostream &output) {
|
||||
using state_t = std::pair<unsigned int, std::array<char, 4>>;
|
||||
|
||||
auto map = read_map(input);
|
||||
|
||||
// problem statement says to duplicate @ but where's the fun in that
|
||||
const auto initial_pos = find(map, '@');
|
||||
|
||||
// problem statement says to duplicate @ but where's the fun in that, let's have different starting positions
|
||||
std::array<std::string_view, 3> overlay = {
|
||||
"@#*",
|
||||
"###",
|
||||
"^#/",
|
||||
};
|
||||
|
||||
for (int y = 0; y < 3; ++y) {
|
||||
auto &row = map[initial_pos[1] + y - 1];
|
||||
std::copy(overlay[y].begin(), overlay[y].end(), row.data() + initial_pos[0] - 1);
|
||||
}
|
||||
|
||||
const auto implied_graph = compute_implied_graph(map);
|
||||
|
||||
std::priority_queue<std::pair<int, state_t>, std::vector<std::pair<int, state_t>>, std::greater<>> todo;
|
||||
std::map<std::pair<unsigned int, char>, int> visited;
|
||||
todo.emplace(0, state_t(0, {'@', '*', '^', '/'}));
|
||||
|
||||
auto target_size = std::count_if(implied_graph.cbegin(), implied_graph.cend(),
|
||||
[](auto &x) { return std::islower(x.first); });
|
||||
|
||||
|
||||
while (!todo.empty()) {
|
||||
const auto[dist, state] = todo.top();
|
||||
todo.pop();
|
||||
|
||||
auto[keys, pos] = state;
|
||||
|
||||
if (std::__popcount(keys) == target_size) {
|
||||
output << dist << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
auto next_pos = pos;
|
||||
for (const auto &edge : implied_graph.at(pos[i])) {
|
||||
auto next_dist = dist + edge.second;
|
||||
auto next_keys = keys;
|
||||
if (std::islower(edge.first)) {
|
||||
// Add the key to our collection
|
||||
next_keys |= key_index(edge.first);;
|
||||
} else if (std::isupper(edge.first)) {
|
||||
// Check if we have the required key already
|
||||
if (!(next_keys & key_index(edge.first))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
next_pos[i] = edge.first;
|
||||
|
||||
state_t next_state = {next_keys, next_pos};
|
||||
if (auto it = visited.find({next_keys, next_pos[i]}); it == visited.end() || it->second > next_dist) {
|
||||
visited[{next_keys, next_pos[i]}] = next_dist;
|
||||
todo.emplace(next_dist, next_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
output << "Not implemented\n";
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <queue>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace {
|
||||
bool bounds_check(aoc2019::IntCodeComputer computer, std::int64_t x, std::int64_t y) {
|
||||
std::deque<std::int64_t> output_buffer;
|
||||
computer.connectOutput(output_buffer);
|
||||
|
||||
computer.sendInput(x);
|
||||
computer.sendInput(y);
|
||||
|
||||
computer.run();
|
||||
assert(computer.isTerminated());
|
||||
assert(!output_buffer.empty());
|
||||
|
||||
return output_buffer.front();
|
||||
}
|
||||
|
||||
class Beam {
|
||||
private:
|
||||
aoc2019::IntCodeComputer computer;
|
||||
std::int64_t last_width = 1;
|
||||
std::int64_t last_start = 0;
|
||||
std::int64_t y = 0;
|
||||
|
||||
public:
|
||||
Beam(std::istream &input) : computer(input) {};
|
||||
|
||||
std::pair<std::int64_t, std::int64_t> next() {
|
||||
auto x = last_start;
|
||||
|
||||
while (!bounds_check(computer, x, y)) {
|
||||
++x;
|
||||
}
|
||||
|
||||
last_start = x;
|
||||
x += last_width - 1;
|
||||
|
||||
while (bounds_check(computer, x, y)) {
|
||||
++x;
|
||||
}
|
||||
|
||||
last_width = x - last_start;
|
||||
++y;
|
||||
|
||||
return {last_start, last_width};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void aoc2019::day19_part1(std::istream &input, std::ostream &output) {
|
||||
Beam beam(input);
|
||||
|
||||
std::int64_t covered = 0;
|
||||
for (std::int64_t y = 0; y < 50; ++y) {
|
||||
const auto[start, width] = beam.next();
|
||||
|
||||
if (start >= 50) break;
|
||||
|
||||
covered += std::min(50 - start, width);
|
||||
}
|
||||
|
||||
output << covered << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day19_part2(std::istream &input, std::ostream &output) {
|
||||
Beam beam(input);
|
||||
std::queue<std::int64_t> beam_ends;
|
||||
|
||||
constexpr std::int64_t DIMENSION = 100;
|
||||
|
||||
for (std::int64_t y = 0; true; ++y) {
|
||||
const auto[start, width] = beam.next();
|
||||
|
||||
beam_ends.push(start + width);
|
||||
if (beam_ends.size() == DIMENSION) {
|
||||
auto end = beam_ends.front();
|
||||
if (end - start >= DIMENSION) {
|
||||
auto result = start * 10000 + y - DIMENSION + 1;
|
||||
output << result << std::endl;
|
||||
return;
|
||||
}
|
||||
beam_ends.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include "days.hpp"
|
||||
#include "point.hpp"
|
||||
|
||||
namespace {
|
||||
typedef aoc2019::Point<int, 2> point_t;
|
||||
typedef std::vector<std::string> map_t;
|
||||
|
||||
std::array<point_t, 4> DIRECTIONS = {{
|
||||
{0, -1},
|
||||
{0, 1},
|
||||
{-1, 0},
|
||||
{1, 0},
|
||||
}};
|
||||
|
||||
std::vector<std::string> read_map(std::istream &input) {
|
||||
std::string buffer;
|
||||
std::vector<std::string> map;
|
||||
|
||||
while (std::getline(input, buffer)) {
|
||||
map.push_back(buffer);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
auto get_portals(const map_t &map) {
|
||||
std::unordered_map<point_t, std::string> portals;
|
||||
|
||||
// First find horizontal portals
|
||||
for (int y = 0; y < map.size(); ++y) {
|
||||
for (int x = 0; x < map[y].size() - 1; ++x) {
|
||||
if (std::isalpha(map[y][x]) && std::isalpha(map[y][x + 1])) {
|
||||
// find out the entry point
|
||||
point_t entry_point = {0, y};
|
||||
if (x > 0 && map[y][x - 1] == '.') {
|
||||
entry_point[0] = x - 1;
|
||||
} else {
|
||||
entry_point[0] = x + 2;
|
||||
}
|
||||
|
||||
portals[entry_point] = map[y].substr(x, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char name[3] = {0, 0, 0};
|
||||
for (int x = 0; x < map[0].size(); ++x) {
|
||||
for (int y = 0; y < map.size() - 1; ++y) {
|
||||
if (std::isalpha(map[y][x]) && std::isalpha(map[y + 1][x])) {
|
||||
name[0] = map[y][x];
|
||||
name[1] = map[y + 1][x];
|
||||
|
||||
point_t entry_point = {x, 0};
|
||||
if (y > 0 && map[y - 1][x] == '.') {
|
||||
entry_point[1] = y - 1;
|
||||
} else {
|
||||
entry_point[1] = y + 2;
|
||||
}
|
||||
|
||||
portals[entry_point] = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return portals;
|
||||
}
|
||||
|
||||
std::unordered_map<point_t, std::vector<std::pair<int, point_t>>>
|
||||
get_implicit_graph(const map_t &map, const std::unordered_map<point_t, std::string> &portals) {
|
||||
std::unordered_map<std::string, point_t> half_links;
|
||||
|
||||
std::unordered_map<point_t, std::vector<std::pair<int, point_t>>> graph;
|
||||
|
||||
for (auto &entry : portals) {
|
||||
if (auto it = half_links.find(entry.second); it != half_links.end()) {
|
||||
// Connect up the portals
|
||||
graph[it->second].emplace_back(1, entry.first);
|
||||
graph[entry.first].emplace_back(1, it->second);
|
||||
} else {
|
||||
half_links[entry.second] = entry.first;
|
||||
}
|
||||
|
||||
// Do a BFS from the node to see what we can reach.
|
||||
std::deque<std::pair<int, point_t>> todo{{0, entry.first}};
|
||||
std::unordered_set<point_t> visited{entry.first};
|
||||
|
||||
while (!todo.empty()) {
|
||||
const auto[dist, pos] = todo.front();
|
||||
todo.pop_front();
|
||||
|
||||
for (auto &direction : DIRECTIONS) {
|
||||
auto next_pos = pos + direction;
|
||||
|
||||
if (map[next_pos[1]][next_pos[0]] != '.' || visited.count(next_pos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (portals.count(next_pos)) {
|
||||
graph[entry.first].emplace_back(dist + 1, next_pos);
|
||||
}
|
||||
|
||||
todo.emplace_back(dist + 1, next_pos);
|
||||
visited.insert(next_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day20_part1(std::istream &input, std::ostream &output) {
|
||||
const auto map = read_map(input);
|
||||
const auto portals = get_portals(map);
|
||||
|
||||
const auto starting_point = std::find_if(portals.begin(), portals.end(), [](auto &x) {
|
||||
return x.second == "AA";
|
||||
})->first;
|
||||
|
||||
auto graph = get_implicit_graph(map, portals);
|
||||
|
||||
std::unordered_set<point_t> visited;
|
||||
std::priority_queue<std::pair<int, point_t>, std::vector<std::pair<int, point_t>>, std::greater<>> todo;
|
||||
todo.emplace(0, starting_point);
|
||||
|
||||
while (!todo.empty()) {
|
||||
const auto[dist, pos] = todo.top();
|
||||
todo.pop();
|
||||
|
||||
if (visited.count(pos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visited.insert(pos);
|
||||
|
||||
if (portals.at(pos) == "ZZ") {
|
||||
output << dist << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &edge : graph[pos]) {
|
||||
if (visited.count(edge.second)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
todo.emplace(dist + edge.first, edge.second);
|
||||
}
|
||||
}
|
||||
|
||||
throw std::domain_error("No valid route.");
|
||||
}
|
||||
|
||||
void aoc2019::day20_part2(std::istream &input, std::ostream &output) {
|
||||
const auto map = read_map(input);
|
||||
const auto portals = get_portals(map);
|
||||
|
||||
using state_t = std::pair<int, point_t>;
|
||||
|
||||
const auto starting_point = std::find_if(portals.begin(), portals.end(), [](auto &x) {
|
||||
return x.second == "AA";
|
||||
})->first;
|
||||
|
||||
auto graph = get_implicit_graph(map, portals);
|
||||
|
||||
std::set<state_t> visited;
|
||||
std::priority_queue<std::tuple<int, int, point_t>, std::vector<std::tuple<int, int, point_t>>, std::greater<>> todo;
|
||||
todo.emplace(0, 0, starting_point);
|
||||
|
||||
const int outer_x_min = 2;
|
||||
const int outer_x_max = map[0].size() - 3;
|
||||
const int outer_y_min = 2;
|
||||
const int outer_y_max = map.size() - 3;
|
||||
|
||||
while (!todo.empty()) {
|
||||
const auto[dist, level, pos] = todo.top();
|
||||
todo.pop();
|
||||
|
||||
if (visited.count({level, pos})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visited.emplace(level, pos);
|
||||
|
||||
if (level == 0 && portals.at(pos) == "ZZ") {
|
||||
output << dist << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &edge : graph[pos]) {
|
||||
int mod = 0;
|
||||
if (edge.first == 1) {
|
||||
// Taking a portal, determine which level we're going to
|
||||
if (pos[0] == outer_x_max || pos[0] == outer_x_min || pos[1] == outer_y_max || pos[1] == outer_y_min) {
|
||||
mod = -1;
|
||||
} else {
|
||||
mod = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (level + mod < 0 || visited.count({level + mod, edge.second})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
todo.emplace(dist + edge.first, level + mod, edge.second);
|
||||
}
|
||||
}
|
||||
|
||||
throw std::domain_error("No valid route.");
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#include <iostream>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace {
|
||||
void solve(std::istream &input, std::string_view program, std::ostream &output) {
|
||||
aoc2019::IntCodeComputer computer(input);
|
||||
std::deque<std::int64_t> output_buffer;
|
||||
computer.connectOutput(output_buffer);
|
||||
computer.run();
|
||||
output_buffer.clear();
|
||||
|
||||
computer.sendInputs(program);
|
||||
|
||||
computer.run();
|
||||
if (output_buffer.back() < 127) {
|
||||
for (char c : output_buffer) {
|
||||
output << c;
|
||||
}
|
||||
} else {
|
||||
output << output_buffer.back() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day21_part1(std::istream &input, std::ostream &output) {
|
||||
std::string_view program = "OR A J\n" // Check if any of the next 3 places is a hole
|
||||
"AND B J\n"
|
||||
"AND C J\n"
|
||||
"NOT J J\n"
|
||||
"AND D J\n" // Jump if the landing space is clear
|
||||
"WALK\n";
|
||||
|
||||
solve(input, program, output);
|
||||
}
|
||||
|
||||
void aoc2019::day21_part2(std::istream &input, std::ostream &output) {
|
||||
std::string_view program = "NOT H J\n" // If you can safely jump twice
|
||||
"OR C J\n" // And either of the next 3 places contains a hole
|
||||
"AND A J\n"
|
||||
"AND B J\n"
|
||||
"NOT J J\n"
|
||||
"AND D J\n" // And we can land our first jump, then jump.
|
||||
"RUN\n";
|
||||
|
||||
solve(input, program, output);
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include "days.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace {
|
||||
enum class Operation {
|
||||
Stack,
|
||||
Deal,
|
||||
Cut
|
||||
};
|
||||
|
||||
using Move = std::pair<Operation, int>;
|
||||
|
||||
std::vector<Move> read_moves(std::istream &input) {
|
||||
std::string buffer;
|
||||
std::vector<Move> moves;
|
||||
|
||||
while (std::getline(input, buffer)) {
|
||||
std::string_view line = buffer;
|
||||
if (!line.find("deal into new stack")) {
|
||||
moves.emplace_back(Operation::Stack, 0);
|
||||
} else if (!line.find("deal with increment ")) {
|
||||
int new_increment;
|
||||
aoc2019::from_chars(line.substr(20), new_increment);
|
||||
moves.emplace_back(Operation::Deal, new_increment);
|
||||
} else {
|
||||
// cut
|
||||
int new_offset;
|
||||
aoc2019::from_chars(line.substr(4), new_offset);
|
||||
|
||||
moves.emplace_back(Operation::Cut, new_offset);
|
||||
}
|
||||
}
|
||||
|
||||
return moves;
|
||||
}
|
||||
|
||||
constexpr std::int64_t mmi(std::int64_t a, std::int64_t n) {
|
||||
std::int64_t t = 0, newt = 1, r = n, newr = a;
|
||||
|
||||
while (newr != 0) {
|
||||
auto q = r / newr;
|
||||
// Poor man's simultaneous assignment
|
||||
std::tie(t, newt) = std::make_tuple(newt, t - q * newt);
|
||||
std::tie(r, newr) = std::make_tuple(newr, r - q * newr);
|
||||
}
|
||||
|
||||
if (r > 1) {
|
||||
throw std::invalid_argument("Not invertible.");
|
||||
}
|
||||
|
||||
if (t < 0) t += n;
|
||||
|
||||
assert((t * a) % n == 1);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
constexpr std::pair<std::int64_t, std::int64_t> pow(std::int64_t a, std::int64_t b, std::int64_t n, const std::int64_t M) {
|
||||
__int128 ra = 0, rb = 0;
|
||||
|
||||
while (n > 0) {
|
||||
if (n % 2) {
|
||||
ra = (ra + a) % M;
|
||||
rb = (rb + b) % M;
|
||||
}
|
||||
|
||||
// f(x) = ax + b
|
||||
// f(f(x)) = a(ax + b) + b
|
||||
// = aax + ab + b
|
||||
__int128 na = a * (__int128) a;
|
||||
__int128 nb = b * (__int128) a + b;
|
||||
|
||||
a = na % M;
|
||||
b = nb % M;
|
||||
|
||||
n /= 2;
|
||||
}
|
||||
|
||||
return {ra, rb};
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day22_part1(std::istream &input, std::ostream &output) {
|
||||
constexpr int DECK_SIZE = 10007;
|
||||
|
||||
int pos = 2019;
|
||||
|
||||
for (auto move : read_moves(input)) {
|
||||
int argument = move.second;
|
||||
switch (move.first) {
|
||||
case Operation::Stack:
|
||||
pos = DECK_SIZE - 1 - pos;
|
||||
break;
|
||||
|
||||
case Operation::Deal:
|
||||
pos = pos * argument % DECK_SIZE;
|
||||
break;
|
||||
|
||||
case Operation::Cut:
|
||||
pos = (pos - argument) % DECK_SIZE;
|
||||
if (pos < 0) pos += DECK_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output << pos << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day22_part2(std::istream &input, std::ostream &output) {
|
||||
constexpr std::int64_t DECK_SIZE = 119315717514047;
|
||||
constexpr std::int64_t SHUFFLES = 101741582076661;
|
||||
|
||||
assert(mmi(3, 11) == 4);
|
||||
|
||||
std::int64_t a = 1, b = 0;
|
||||
|
||||
for (auto move : read_moves(input)) {
|
||||
std::int64_t argument = move.second;
|
||||
switch (move.first) {
|
||||
case Operation::Stack:
|
||||
a = -a;
|
||||
b = DECK_SIZE - b - 1;
|
||||
break;
|
||||
|
||||
case Operation::Cut:
|
||||
b = (b + argument) % DECK_SIZE;
|
||||
break;
|
||||
|
||||
case Operation::Deal:
|
||||
__int128 inv = mmi(argument, DECK_SIZE);
|
||||
a = (a * inv) % DECK_SIZE;
|
||||
b = (b * inv) % DECK_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto[ra, rb] = pow(a, b, SHUFFLES, DECK_SIZE);
|
||||
|
||||
output << ra << ',' << rb << std::endl;
|
||||
|
||||
auto result = (2020 * ra + rb) % DECK_SIZE;
|
||||
if (result < 0) {
|
||||
result += DECK_SIZE;
|
||||
}
|
||||
|
||||
output << result << std::endl;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <iostream>
|
||||
#include "days.hpp"
|
||||
|
||||
void aoc2019::day23_part1(std::istream &input, std::ostream &output) {
|
||||
output << "Not implemented\n";
|
||||
}
|
||||
|
||||
void aoc2019::day23_part2(std::istream &input, std::ostream &output) {
|
||||
output << "Not implemented\n";
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <numeric>
|
||||
#include "days.hpp"
|
||||
|
||||
namespace {
|
||||
using field_t = std::array<std::array<bool, 5>, 5>;
|
||||
|
||||
constexpr int EDGE = 4;
|
||||
constexpr int MIDPOINT = 2;
|
||||
|
||||
field_t read_input(std::istream &input) {
|
||||
std::string buffer;
|
||||
field_t map;
|
||||
|
||||
int y = 0;
|
||||
|
||||
while (std::getline(input, buffer)) {
|
||||
auto &row = map[y++];
|
||||
|
||||
std::transform(buffer.begin(), buffer.end(), row.begin(), [](char c) { return c == '#'; });
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void next_gen(const field_t &source, field_t &sink) {
|
||||
for (int y = 0; y < source.size(); ++y) {
|
||||
for (int x = 0; x < source[y].size(); ++x) {
|
||||
int neighbours = source[y][x] ? -1 : 0;
|
||||
for (int dy = -1; dy <= 1; ++dy) {
|
||||
if (dy + y < 0 || dy + y >= source.size()) {
|
||||
continue;
|
||||
}
|
||||
for (int dx = -1; dx <= 1; ++dx) {
|
||||
if (dx + x < 0 || dx + x >= source[y].size() || dx * dy) {
|
||||
continue;
|
||||
}
|
||||
neighbours += source[y + dy][x + dx];
|
||||
}
|
||||
}
|
||||
|
||||
sink[y][x] = neighbours == 1 || (!source[y][x] && neighbours == 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int num_bees(const field_t &field) {
|
||||
int total = 0;
|
||||
for (auto &row : field) {
|
||||
total += std::count(row.begin(), row.end(), true);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
std::unordered_map<int, field_t> advance(const std::unordered_map<int, field_t> &state) {
|
||||
const auto dimension_range = std::minmax_element(state.begin(), state.end());
|
||||
const auto min = dimension_range.first->first - 1;
|
||||
const auto max = dimension_range.second->first + 1;
|
||||
|
||||
std::unordered_map<int, field_t> next_gen;
|
||||
|
||||
auto has_bee = [&state](int dimension, int x, int y) {
|
||||
if (auto it = state.find(dimension); it != state.end()) {
|
||||
return it->second[y][x];
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
for (int dimension = min; dimension <= max; ++dimension) {
|
||||
field_t field{};
|
||||
if (auto it = state.find(dimension); it != state.end()) {
|
||||
field = it->second;
|
||||
}
|
||||
|
||||
auto get_neighbours = [has_bee,dimension](int x, int y) {
|
||||
int neighbours = 0;
|
||||
|
||||
// Cell above
|
||||
if (y == 0) {
|
||||
neighbours += has_bee(dimension + 1, MIDPOINT, 1);
|
||||
} else if (y == 3 && x == MIDPOINT) {
|
||||
for (int sx = 0; sx < 5; ++sx) {
|
||||
neighbours += has_bee(dimension - 1, sx, EDGE);
|
||||
}
|
||||
} else {
|
||||
neighbours += has_bee(dimension, x, y - 1);
|
||||
}
|
||||
|
||||
// Cell below
|
||||
if (y == EDGE) {
|
||||
neighbours += has_bee(dimension + 1, MIDPOINT, 3);
|
||||
} else if (y == 1 && x == MIDPOINT) {
|
||||
for (int sx = 0; sx < 5; ++sx) {
|
||||
neighbours += has_bee(dimension - 1, sx, 0);
|
||||
}
|
||||
} else {
|
||||
neighbours += has_bee(dimension, x, y + 1);
|
||||
}
|
||||
|
||||
// Cell left
|
||||
if (x == 0) {
|
||||
neighbours += has_bee(dimension + 1, 1, 2);
|
||||
} else if (x == 3 && y == MIDPOINT) {
|
||||
for (int sy = 0; sy < 5; ++sy) {
|
||||
neighbours += has_bee(dimension - 1, EDGE, sy);
|
||||
}
|
||||
} else {
|
||||
neighbours += has_bee(dimension, x - 1, y);
|
||||
}
|
||||
|
||||
// Cell right
|
||||
if (x == EDGE) {
|
||||
neighbours += has_bee(dimension + 1, 3, MIDPOINT);
|
||||
} else if (x == 1 && y == MIDPOINT) {
|
||||
for (int sy = 0; sy < 5; ++sy) {
|
||||
neighbours += has_bee(dimension - 1, 0, sy);
|
||||
}
|
||||
} else {
|
||||
neighbours += has_bee(dimension, x + 1, y);
|
||||
}
|
||||
|
||||
return neighbours;
|
||||
};
|
||||
|
||||
|
||||
for (int y = 0; y < 5; ++y) {
|
||||
for (int x = 0; x < 5; ++x) {
|
||||
auto neighbours = get_neighbours(x, y);
|
||||
field[y][x] = neighbours == 1 || (neighbours == 2 && !field[y][x]);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't evolve the midpoint.
|
||||
field[2][2] = false;
|
||||
|
||||
if (num_bees(field) || (dimension != min && dimension != max)) {
|
||||
next_gen[dimension] = field;
|
||||
}
|
||||
}
|
||||
|
||||
return next_gen;
|
||||
}
|
||||
}
|
||||
|
||||
void aoc2019::day24_part1(std::istream &input, std::ostream &output) {
|
||||
auto map = read_input(input);
|
||||
auto copy = map;
|
||||
|
||||
std::set<field_t> seen;
|
||||
do {
|
||||
seen.insert(map);
|
||||
next_gen(map, copy);
|
||||
std::swap(map, copy);
|
||||
} while (!seen.count(map));
|
||||
|
||||
unsigned int pow = 1;
|
||||
unsigned int diversity = 0;
|
||||
for (auto &row : map) {
|
||||
for (auto b : row) {
|
||||
if (b) {
|
||||
diversity += pow;
|
||||
}
|
||||
|
||||
pow <<= 1u;
|
||||
}
|
||||
}
|
||||
output << diversity << std::endl;
|
||||
}
|
||||
|
||||
void aoc2019::day24_part2(std::istream &input, std::ostream &output) {
|
||||
std::unordered_map<int, field_t> fields;
|
||||
fields[0] = read_input(input);
|
||||
|
||||
for (int gen = 0; gen < 200; ++gen) {
|
||||
fields = advance(fields);
|
||||
}
|
||||
|
||||
int total = std::accumulate(fields.begin(), fields.end(), 0, [](auto cur, const auto &it) {
|
||||
return cur + num_bees(it.second);
|
||||
});
|
||||
|
||||
output << total << std::endl;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <iostream>
|
||||
#include "days.hpp"
|
||||
|
||||
void aoc2019::day25_part1(std::istream &input, std::ostream &output) {
|
||||
output << "Not implemented\n";
|
||||
}
|
||||
|
||||
void aoc2019::day25_part2(std::istream &input, std::ostream &output) {
|
||||
output << "Not implemented\n";
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
namespace aoc2019 {
|
||||
// Declarations of all implemented days.
|
||||
void day01_part1(std::istream &input, std::ostream &output);
|
||||
void day01_part2(std::istream &input, std::ostream &output);
|
||||
void day02_part1(std::istream &input, std::ostream &output);
|
||||
void day02_part2(std::istream &input, std::ostream &output);
|
||||
void day03_part1(std::istream &input, std::ostream &output);
|
||||
void day03_part2(std::istream &input, std::ostream &output);
|
||||
void day04_part1(std::istream &input, std::ostream &output);
|
||||
void day04_part2(std::istream &input, std::ostream &output);
|
||||
void day05_part1(std::istream &input, std::ostream &output);
|
||||
void day05_part2(std::istream &input, std::ostream &output);
|
||||
void day06_part1(std::istream &input, std::ostream &output);
|
||||
void day06_part2(std::istream &input, std::ostream &output);
|
||||
void day07_part1(std::istream &input, std::ostream &output);
|
||||
void day07_part2(std::istream &input, std::ostream &output);
|
||||
void day08_part1(std::istream &input, std::ostream &output);
|
||||
void day08_part2(std::istream &input, std::ostream &output);
|
||||
void day09_part1(std::istream &input, std::ostream &output);
|
||||
void day09_part2(std::istream &input, std::ostream &output);
|
||||
void day10_part1(std::istream &input, std::ostream &output);
|
||||
void day10_part2(std::istream &input, std::ostream &output);
|
||||
void day11_part1(std::istream &input, std::ostream &output);
|
||||
void day11_part2(std::istream &input, std::ostream &output);
|
||||
void day12_part1(std::istream &input, std::ostream &output);
|
||||
void day12_part2(std::istream &input, std::ostream &output);
|
||||
void day13_part1(std::istream &input, std::ostream &output);
|
||||
void day13_part2(std::istream &input, std::ostream &output);
|
||||
void day14_part1(std::istream &input, std::ostream &output);
|
||||
void day14_part2(std::istream &input, std::ostream &output);
|
||||
void day15_part1(std::istream &input, std::ostream &output);
|
||||
void day15_part2(std::istream &input, std::ostream &output);
|
||||
void day16_part1(std::istream &input, std::ostream &output);
|
||||
void day16_part2(std::istream &input, std::ostream &output);
|
||||
void day17_part1(std::istream &input, std::ostream &output);
|
||||
void day17_part2(std::istream &input, std::ostream &output);
|
||||
void day18_part1(std::istream &input, std::ostream &output);
|
||||
void day18_part2(std::istream &input, std::ostream &output);
|
||||
void day19_part1(std::istream &input, std::ostream &output);
|
||||
void day19_part2(std::istream &input, std::ostream &output);
|
||||
void day20_part1(std::istream &input, std::ostream &output);
|
||||
void day20_part2(std::istream &input, std::ostream &output);
|
||||
void day21_part1(std::istream &input, std::ostream &output);
|
||||
void day21_part2(std::istream &input, std::ostream &output);
|
||||
void day22_part1(std::istream &input, std::ostream &output);
|
||||
void day22_part2(std::istream &input, std::ostream &output);
|
||||
void day23_part1(std::istream &input, std::ostream &output);
|
||||
void day23_part2(std::istream &input, std::ostream &output);
|
||||
void day24_part1(std::istream &input, std::ostream &output);
|
||||
void day24_part2(std::istream &input, std::ostream &output);
|
||||
void day25_part1(std::istream &input, std::ostream &output);
|
||||
void day25_part2(std::istream &input, std::ostream &output);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#include <array>
|
||||
#include "days.hpp"
|
||||
#include "implementations.hpp"
|
||||
|
||||
constexpr const std::array<std::array<aoc2019::solution_t, 2>, 25> SOLUTIONS = {{
|
||||
{aoc2019::day01_part1, aoc2019::day01_part2},
|
||||
{aoc2019::day02_part1, aoc2019::day02_part2},
|
||||
{aoc2019::day03_part1, aoc2019::day03_part2},
|
||||
{aoc2019::day04_part1, aoc2019::day04_part2},
|
||||
{aoc2019::day05_part1, aoc2019::day05_part2},
|
||||
{aoc2019::day06_part1, aoc2019::day06_part2},
|
||||
{aoc2019::day07_part1, aoc2019::day07_part2},
|
||||
{aoc2019::day08_part1, aoc2019::day08_part2},
|
||||
{aoc2019::day09_part1, aoc2019::day09_part2},
|
||||
{aoc2019::day10_part1, aoc2019::day10_part2},
|
||||
{aoc2019::day11_part1, aoc2019::day11_part2},
|
||||
{aoc2019::day12_part1, aoc2019::day12_part2},
|
||||
{aoc2019::day13_part1, aoc2019::day13_part2},
|
||||
{aoc2019::day14_part1, aoc2019::day14_part2},
|
||||
{aoc2019::day15_part1, aoc2019::day15_part2},
|
||||
{aoc2019::day16_part1, aoc2019::day16_part2},
|
||||
{aoc2019::day17_part1, aoc2019::day17_part2},
|
||||
{aoc2019::day18_part1, aoc2019::day18_part2},
|
||||
{aoc2019::day19_part1, aoc2019::day19_part2},
|
||||
{aoc2019::day20_part1, aoc2019::day20_part2},
|
||||
{aoc2019::day21_part1, aoc2019::day21_part2},
|
||||
{aoc2019::day22_part1, aoc2019::day22_part2},
|
||||
{aoc2019::day23_part1, aoc2019::day23_part2},
|
||||
{aoc2019::day24_part1, aoc2019::day24_part2},
|
||||
{aoc2019::day25_part1, aoc2019::day25_part2},
|
||||
}};
|
||||
|
||||
aoc2019::solution_t aoc2019::get_implementation(int day, bool part2) {
|
||||
return SOLUTIONS.at(day - 1).at((int) part2);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
namespace aoc2019 {
|
||||
typedef void (*solution_t)(std::istream &, std::ostream &);
|
||||
|
||||
solution_t get_implementation(int day, bool part2 = false);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace aoc2019 {
|
||||
template<class T, std::size_t L>
|
||||
class Point : public std::array<T, L> {
|
||||
public:
|
||||
constexpr Point& operator +=(Point other) {
|
||||
for (std::size_t i = 0; i < L; ++i) {
|
||||
(*this)[i] += other[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Point operator+(Point other) const {
|
||||
auto result = *this;
|
||||
result += other;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr Point& operator -=(Point other) {
|
||||
for (std::size_t i = 0; i < L; ++i) {
|
||||
(*this)[i] -= other[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Point operator-(Point other) const {
|
||||
auto result = *this;
|
||||
result -= other;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr T l1() const {
|
||||
T result = 0;
|
||||
for (auto e : *this) {
|
||||
result += std::abs(e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ValueType, std::size_t N, typename Ignored>
|
||||
std::pair<Point<ValueType, N>, Point<ValueType, N>> bounding_box(const std::unordered_map<Point<ValueType, N>, Ignored> &data) {
|
||||
Point<ValueType, N> lower, upper;
|
||||
std::fill(lower.begin(), lower.end(), std::numeric_limits<ValueType>::max());
|
||||
std::fill(upper.begin(), upper.end(), std::numeric_limits<ValueType>::min());
|
||||
|
||||
for (auto &entry : data) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
lower[i] = std::min(entry.first[i], lower[i]);
|
||||
upper[i] = std::max(entry.first[i], upper[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return {lower, upper};
|
||||
}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
// Make point usable with unordered collections.
|
||||
template<class T, std::size_t L> struct hash<aoc2019::Point<T, L>> {
|
||||
size_t operator()(const aoc2019::Point<T, L> &o) const {
|
||||
size_t seed = 0;
|
||||
for (auto i : o) {
|
||||
aoc2019::combine_hash(seed, i);
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
#include "implementations.hpp"
|
||||
#include <charconv>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
|
||||
struct AoCOptions {
|
||||
aoc2019::solution_t implementation;
|
||||
bool run_timer;
|
||||
std::optional<std::ifstream> input_file;
|
||||
};
|
||||
|
||||
static AoCOptions parse_options(const int argc, const char* argv[]) {
|
||||
using namespace std::literals;
|
||||
AoCOptions options{};
|
||||
|
||||
auto show_help = [argv] (int exit_status = 0) {
|
||||
std::cerr << "Usage: " << argv[0] << " [--timer|-t] [--part2|-2] [--help|-h] DAY\n"
|
||||
<< "\t--timer|-t: print execution time\n"
|
||||
<< "\t--input ARG|-fARG: use given input file as puzzle input"
|
||||
<< "\t--part2|-2: run part 2\n"
|
||||
<< "\t --help|-h: show this message\n";
|
||||
std::exit(exit_status);
|
||||
};
|
||||
|
||||
int day = -1;
|
||||
bool part2 = false;
|
||||
|
||||
// Here follows a manual implementation of getopt, since getopt doesn't work on windows…
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string_view arg(argv[i]);
|
||||
if (arg[0] == '-') {
|
||||
// Handle flag arguments
|
||||
if (arg[1] != '-') {
|
||||
// Shorthand flags
|
||||
for (int j = 1; j < arg.size(); ++j) {
|
||||
switch (arg[j]) {
|
||||
case '2':
|
||||
part2 = true;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
options.run_timer = true;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
show_help();
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
if (j == arg.size() - 1) {
|
||||
if (i == argc - 1) {
|
||||
std::cerr << "Option -f requires an argument.";
|
||||
show_help(1);
|
||||
} else {
|
||||
options.input_file = std::ifstream(argv[i + 1]);
|
||||
++i;
|
||||
}
|
||||
} else {
|
||||
options.input_file = std::ifstream(std::string(arg.substr(j)));
|
||||
j = arg.size();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cerr << "Unknown flag '" << arg[j] << "'.\n\n";
|
||||
show_help(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle long form versions
|
||||
if (arg == "--timer"sv) {
|
||||
part2 = true;
|
||||
} else if (arg == "--timer"sv) {
|
||||
options.run_timer = true;
|
||||
} else if (arg == "--help"sv) {
|
||||
show_help();
|
||||
} else if (arg == "--input"sv) {
|
||||
if (i == argc - 1) {
|
||||
std::cerr << "Option -f requires an argument.";
|
||||
show_help(1);
|
||||
} else {
|
||||
options.input_file = std::ifstream(argv[i + 1]);
|
||||
++i;
|
||||
}
|
||||
} else {
|
||||
show_help(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (day != -1) {
|
||||
// Double date specification, bail.
|
||||
show_help(1);
|
||||
}
|
||||
|
||||
// Try to parse the date number
|
||||
if (auto res = std::from_chars(arg.data(), arg.data() + arg.size(), day); res.ec != std::errc()) {
|
||||
auto error_code = std::make_error_code(res.ec);
|
||||
std::cerr << error_code.message() << "\n\n";
|
||||
show_help(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (day == -1) {
|
||||
std::cerr << "Argument DAY is required.\n\n";
|
||||
show_help(1);
|
||||
} else if (day < 1 || day > 25) {
|
||||
std::cerr << "Invalid day. Valid range: [1, 25]\n";
|
||||
show_help(1);
|
||||
}
|
||||
|
||||
options.implementation = aoc2019::get_implementation(day, part2);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
auto options = parse_options(argc, argv);
|
||||
|
||||
if (options.implementation != nullptr) {
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
options.implementation(options.input_file ? *options.input_file : std::cin, std::cout);
|
||||
if (options.run_timer) {
|
||||
const std::chrono::duration<double> duration = std::chrono::high_resolution_clock::now() - start;
|
||||
std::cerr << "Time taken: " << duration.count() << "s\n";
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
std::cerr << "Unimplemented.\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
#include <iostream>
|
||||
#include "utils.hpp"
|
||||
|
||||
std::string_view aoc2019::strtok(std::string_view &str, char token) {
|
||||
auto next_delim = str.find(token);
|
||||
auto next = str.substr(0, next_delim);
|
||||
if (next_delim == std::string_view::npos) {
|
||||
str = {};
|
||||
} else {
|
||||
str = str.substr(next_delim + 1);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
std::deque<int64_t> aoc2019::run_intcode(std::vector<int64_t> program, std::deque<int64_t> inputs) {
|
||||
std::deque<std::int64_t> outputs;
|
||||
IntCodeComputer computer(std::move(program), std::move(inputs));
|
||||
computer.connectOutput(outputs);
|
||||
|
||||
computer.run();
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
aoc2019::IntCodeComputer::value_t &aoc2019::IntCodeComputer::interpret_value(int pos) {
|
||||
value_t immediate;
|
||||
switch (pos) {
|
||||
case 1:
|
||||
immediate = program[ip] / 100 % 10;
|
||||
break;
|
||||
case 2:
|
||||
immediate = program[ip] / 1000 % 10;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
immediate = program[ip] / 10000 % 10;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::out_of_range("Invalid position");
|
||||
}
|
||||
|
||||
value_t index;
|
||||
|
||||
switch (immediate) {
|
||||
case 0:
|
||||
index = program[ip + pos];
|
||||
break;
|
||||
|
||||
case 1:
|
||||
index = ip + pos;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
index = program[ip + pos] + relative;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::out_of_range("Invalid mode");
|
||||
}
|
||||
|
||||
if (program.size() <= index) {
|
||||
program.resize(index + 1);
|
||||
}
|
||||
|
||||
return program[index];
|
||||
}
|
||||
|
||||
void aoc2019::IntCodeComputer::connectOutput(aoc2019::IntCodeComputer &computer) {
|
||||
outputSink = &computer.inputs;
|
||||
}
|
||||
|
||||
void aoc2019::IntCodeComputer::connectOutput(std::deque<value_t> &sink) {
|
||||
outputSink = &sink;
|
||||
}
|
||||
|
||||
bool aoc2019::IntCodeComputer::isTerminated() const {
|
||||
return halted;
|
||||
}
|
||||
|
||||
const std::deque<aoc2019::IntCodeComputer::value_t> &aoc2019::IntCodeComputer::currentInputs() const {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
std::vector<aoc2019::IntCodeComputer::value_t> aoc2019::IntCodeComputer::read_intcode(std::istream &input) {
|
||||
std::vector<value_t> program;
|
||||
for (value_t current; input >> current; input.ignore()) {
|
||||
program.push_back(current);
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
void aoc2019::IntCodeComputer::run() {
|
||||
while (ip < program.size()) {
|
||||
switch (program[ip] % 100) {
|
||||
case 1:
|
||||
interpret_value(3) = interpret_value(1) + interpret_value(2);
|
||||
ip += 4;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
interpret_value(3) = interpret_value(1) * interpret_value(2);
|
||||
ip += 4;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (inputs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
interpret_value(1) = inputs.front();
|
||||
inputs.pop_front();
|
||||
ip += 2;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
outputSink->push_back(interpret_value(1));
|
||||
ip += 2;
|
||||
break;
|
||||
|
||||
case 5: // Jump if non-zero
|
||||
if (interpret_value(1)) {
|
||||
ip = interpret_value(2);
|
||||
} else {
|
||||
ip += 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 6: // Jump if zero
|
||||
if (!interpret_value(1)) {
|
||||
ip = interpret_value(2);
|
||||
} else {
|
||||
ip += 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 7: // less than
|
||||
interpret_value(3) = interpret_value(1) < interpret_value(2);
|
||||
ip += 4;
|
||||
break;
|
||||
|
||||
case 8: // equality
|
||||
interpret_value(3) = interpret_value(1) == interpret_value(2) ? 1 : 0;
|
||||
ip += 4;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
relative += interpret_value(1);
|
||||
ip += 2;
|
||||
break;
|
||||
|
||||
case 99:
|
||||
halted = true;
|
||||
return;
|
||||
|
||||
default:
|
||||
char buffer[30];
|
||||
std::snprintf(buffer, sizeof(buffer), "Invalid opcode: %d", program[ip]);
|
||||
|
||||
throw std::domain_error(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aoc2019::IntCodeComputer::IntCodeComputer(std::vector<value_t> program, std::deque<value_t> initial_inputs) :
|
||||
program{std::move(program)}, inputs{std::move(initial_inputs)} {
|
||||
}
|
||||
|
||||
|
||||
aoc2019::IntCodeComputer::IntCodeComputer(std::istream &program_stream, std::deque<value_t> initial_inputs) :
|
||||
program(read_intcode(program_stream)), inputs(std::move(initial_inputs)) {
|
||||
}
|
||||
|
||||
void aoc2019::IntCodeComputer::sendInput(aoc2019::IntCodeComputer::value_t input) {
|
||||
inputs.push_back(input);
|
||||
}
|
||||
|
||||
aoc2019::IntCodeComputer::value_t &aoc2019::IntCodeComputer::operator[](std::size_t index) {
|
||||
return program[index];
|
||||
}
|
||||
|
||||
const aoc2019::IntCodeComputer::value_t &aoc2019::IntCodeComputer::operator[](std::size_t index) const {
|
||||
return program[index];
|
||||
}
|
||||
|
||||
void aoc2019::IntCodeComputer::sendInputs(std::string_view str) {
|
||||
for (char c : str) {
|
||||
sendInput(c);
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <charconv>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace aoc2019 {
|
||||
|
||||
template<typename T>
|
||||
inline std::from_chars_result from_chars(std::string_view str, T &value) {
|
||||
return std::from_chars(str.data(), str.data() + str.size(), value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void combine_hash(std::size_t &seed, const T &o) {
|
||||
// Algorithm taken from boost::combine_hash.
|
||||
std::hash<T> hash{};
|
||||
seed ^= hash(o) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
template<typename ValueType, typename OutputIt>
|
||||
std::istream &read_line_numbers_and_garbage(std::istream &input, OutputIt output) {
|
||||
ValueType v;
|
||||
char c;
|
||||
while (input && (c = input.peek()) != '\n') {
|
||||
if (c == '-' || std::isdigit(c)) {
|
||||
input >> v;
|
||||
*output = v;
|
||||
++output;
|
||||
} else {
|
||||
input.ignore();
|
||||
}
|
||||
}
|
||||
|
||||
input.get();
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
std::string_view strtok(std::string_view &str, char token = ',');
|
||||
|
||||
std::deque<int64_t> run_intcode(std::vector<std::int64_t> program, std::deque<std::int64_t> inputs = {});
|
||||
|
||||
template<class Node>
|
||||
std::vector<Node> topological_sort(const std::unordered_map<Node, std::vector<Node>> &edge_list) {
|
||||
std::unordered_map<Node, int> incoming_edges;
|
||||
|
||||
for (auto &entry : edge_list) {
|
||||
// Ensure entry for parent exist
|
||||
incoming_edges[entry.first] += 0;
|
||||
|
||||
for (auto &node : entry.second) {
|
||||
incoming_edges[node]++;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Node> order;
|
||||
std::deque<Node> childless;
|
||||
|
||||
for (auto &entry : incoming_edges) {
|
||||
if (!entry.second) {
|
||||
childless.push_back(entry.first);
|
||||
}
|
||||
}
|
||||
|
||||
while (!childless.empty()) {
|
||||
auto current = childless.front();
|
||||
childless.pop_front();
|
||||
order.emplace_back(current);
|
||||
|
||||
if (auto it = edge_list.find(current); it != edge_list.end()) {
|
||||
for (const auto &parent : it->second) {
|
||||
if (--incoming_edges[parent] == 0) {
|
||||
childless.push_back(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (order.size() != incoming_edges.size()) {
|
||||
throw std::domain_error("Not a DAG.");
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
class IntCodeComputer {
|
||||
public:
|
||||
typedef std::int64_t value_t;
|
||||
|
||||
explicit IntCodeComputer(std::vector<value_t> program, std::deque<value_t> initial_inputs = {});
|
||||
explicit IntCodeComputer(std::istream &program_stream, std::deque<value_t> initial_inputs = {});
|
||||
|
||||
void run();
|
||||
void connectOutput(IntCodeComputer &computer);
|
||||
void connectOutput(std::deque<value_t> &sink);
|
||||
void sendInput(value_t input);
|
||||
|
||||
void sendInputs(std::string_view str);
|
||||
|
||||
[[nodiscard]] bool isTerminated() const;
|
||||
|
||||
[[nodiscard]] const std::deque<value_t> ¤tInputs() const;
|
||||
|
||||
value_t &operator[](std::size_t index);
|
||||
const value_t &operator[](std::size_t index) const;
|
||||
|
||||
static std::vector<value_t> read_intcode(std::istream &input);
|
||||
|
||||
private:
|
||||
std::vector<value_t> program;
|
||||
std::deque<value_t> inputs = {};
|
||||
std::deque<value_t> *outputSink = nullptr;
|
||||
int ip = 0;
|
||||
int relative = 0;
|
||||
bool halted = false;
|
||||
|
||||
[[nodiscard]] value_t &interpret_value(int pos);
|
||||
};
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
100756
|
||||
@@ -1 +0,0 @@
|
||||
33583
|
||||
@@ -1,2 +0,0 @@
|
||||
R75,D30,R83,U83,L12,D49,R71,U7,L72
|
||||
U62,R66,U55,R34,D71,R55,D58,R83
|
||||
@@ -1 +0,0 @@
|
||||
159
|
||||
@@ -1,2 +0,0 @@
|
||||
R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
|
||||
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7
|
||||
@@ -1 +0,0 @@
|
||||
135
|
||||
@@ -1,2 +0,0 @@
|
||||
R8,U5,L5,D3
|
||||
U7,R6,D4,L4
|
||||
@@ -1 +0,0 @@
|
||||
6
|
||||
@@ -1 +0,0 @@
|
||||
03-1-1.in
|
||||
@@ -1 +0,0 @@
|
||||
610
|
||||
@@ -1 +0,0 @@
|
||||
03-1-2.in
|
||||
@@ -1 +0,0 @@
|
||||
410
|
||||
@@ -1 +0,0 @@
|
||||
03-1-3.in
|
||||
@@ -1 +0,0 @@
|
||||
30
|
||||
@@ -1,11 +0,0 @@
|
||||
COM)B
|
||||
B)C
|
||||
C)D
|
||||
D)E
|
||||
E)F
|
||||
B)G
|
||||
G)H
|
||||
D)I
|
||||
E)J
|
||||
J)K
|
||||
K)L
|
||||
@@ -1 +0,0 @@
|
||||
42
|
||||
@@ -1,13 +0,0 @@
|
||||
COM)B
|
||||
B)C
|
||||
C)D
|
||||
D)E
|
||||
E)F
|
||||
B)G
|
||||
G)H
|
||||
D)I
|
||||
E)J
|
||||
J)K
|
||||
K)L
|
||||
K)YOU
|
||||
I)SAN
|
||||
@@ -1 +0,0 @@
|
||||
4
|
||||
@@ -1 +0,0 @@
|
||||
3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0
|
||||
@@ -1 +0,0 @@
|
||||
43210
|
||||
@@ -1,2 +0,0 @@
|
||||
3,23,3,24,1002,24,10,24,1002,23,-1,23,
|
||||
101,5,23,23,1,24,23,23,4,23,99,0,0
|
||||
@@ -1 +0,0 @@
|
||||
54321
|
||||
@@ -1,2 +0,0 @@
|
||||
3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,
|
||||
1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0
|
||||
@@ -1 +0,0 @@
|
||||
65210
|
||||
@@ -1,2 +0,0 @@
|
||||
3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,
|
||||
27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5
|
||||
@@ -1 +0,0 @@
|
||||
139629729
|
||||
@@ -1,3 +0,0 @@
|
||||
3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,
|
||||
-5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,
|
||||
53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10
|
||||
@@ -1 +0,0 @@
|
||||
18216
|
||||
@@ -1,5 +0,0 @@
|
||||
.#..#
|
||||
.....
|
||||
#####
|
||||
....#
|
||||
...##
|
||||
@@ -1 +0,0 @@
|
||||
8
|
||||
@@ -1,10 +0,0 @@
|
||||
......#.#.
|
||||
#..#.#....
|
||||
..#######.
|
||||
.#.#.###..
|
||||
.#..#.....
|
||||
..#....#.#
|
||||
#..#....#.
|
||||
.##.#..###
|
||||
##...#..#.
|
||||
.#....####
|
||||
@@ -1 +0,0 @@
|
||||
33
|
||||
@@ -1,10 +0,0 @@
|
||||
#.#...#.#.
|
||||
.###....#.
|
||||
.#....#...
|
||||
##.#.#.#.#
|
||||
....#.#.#.
|
||||
.##..###.#
|
||||
..#...##..
|
||||
..##....##
|
||||
......#...
|
||||
.####.###.
|
||||
@@ -1 +0,0 @@
|
||||
35
|
||||
@@ -1,10 +0,0 @@
|
||||
.#..#..###
|
||||
####.###.#
|
||||
....###.#.
|
||||
..###.##.#
|
||||
##.##.#.#.
|
||||
....###..#
|
||||
..#.#..#.#
|
||||
#..#.#.###
|
||||
.##...##.#
|
||||
.....#.#..
|
||||
@@ -1 +0,0 @@
|
||||
41
|
||||
@@ -1,20 +0,0 @@
|
||||
.#..##.###...#######
|
||||
##.############..##.
|
||||
.#.######.########.#
|
||||
.###.#######.####.#.
|
||||
#####.##.#.##.###.##
|
||||
..#####..#.#########
|
||||
####################
|
||||
#.####....###.#.#.##
|
||||
##.#################
|
||||
#####.##.###..####..
|
||||
..######..##.#######
|
||||
####.##.####...##..#
|
||||
.#####..#.######.###
|
||||
##...#.##########...
|
||||
#.##########.#######
|
||||
.####.#.###.###.#.##
|
||||
....##.##.###..#####
|
||||
.#.#.###########.###
|
||||
#.#.#.#####.####.###
|
||||
###.##.####.##.#..##
|
||||
@@ -1 +0,0 @@
|
||||
210
|
||||
@@ -1,4 +0,0 @@
|
||||
<x=-1, y=0, z=2>
|
||||
<x=2, y=-10, z=-7>
|
||||
<x=4, y=-8, z=8>
|
||||
<x=3, y=5, z=-1>
|
||||
@@ -1 +0,0 @@
|
||||
2772
|
||||
@@ -1,4 +0,0 @@
|
||||
<x=-8, y=-10, z=0>
|
||||
<x=5, y=5, z=10>
|
||||
<x=2, y=-7, z=3>
|
||||
<x=9, y=-8, z=-3>
|
||||
@@ -1 +0,0 @@
|
||||
4686774924
|
||||
@@ -1,6 +0,0 @@
|
||||
10 ORE => 10 A
|
||||
1 ORE => 1 B
|
||||
7 A, 1 B => 1 C
|
||||
7 A, 1 C => 1 D
|
||||
7 A, 1 D => 1 E
|
||||
7 A, 1 E => 1 FUEL
|
||||
@@ -1 +0,0 @@
|
||||
31
|
||||
@@ -1,7 +0,0 @@
|
||||
9 ORE => 2 A
|
||||
8 ORE => 3 B
|
||||
7 ORE => 5 C
|
||||
3 A, 4 B => 1 AB
|
||||
5 B, 7 C => 1 BC
|
||||
4 C, 1 A => 1 CA
|
||||
2 AB, 3 BC, 4 CA => 1 FUEL
|
||||
@@ -1 +0,0 @@
|
||||
165
|
||||
@@ -1,9 +0,0 @@
|
||||
157 ORE => 5 NZVS
|
||||
165 ORE => 6 DCFZ
|
||||
44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL
|
||||
12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ
|
||||
179 ORE => 7 PSHF
|
||||
177 ORE => 5 HKGWZ
|
||||
7 DCFZ, 7 PSHF => 2 XJWVT
|
||||
165 ORE => 2 GPVTF
|
||||
3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT
|
||||
@@ -1 +0,0 @@
|
||||
13312
|
||||
@@ -1,12 +0,0 @@
|
||||
2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG
|
||||
17 NVRVD, 3 JNWZP => 8 VPVL
|
||||
53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL
|
||||
22 VJHF, 37 MNCFX => 5 FWMGM
|
||||
139 ORE => 4 NVRVD
|
||||
144 ORE => 7 JNWZP
|
||||
5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC
|
||||
5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV
|
||||
145 ORE => 6 MNCFX
|
||||
1 NVRVD => 8 CXFTF
|
||||
1 VJHF, 6 MNCFX => 4 RFSQX
|
||||
176 ORE => 6 VJHF
|
||||
@@ -1 +0,0 @@
|
||||
180697
|
||||
@@ -1,17 +0,0 @@
|
||||
171 ORE => 8 CNZTR
|
||||
7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL
|
||||
114 ORE => 4 BHXH
|
||||
14 VRPVC => 6 BMBT
|
||||
6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL
|
||||
6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT
|
||||
15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW
|
||||
13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW
|
||||
5 BMBT => 4 WPTQ
|
||||
189 ORE => 9 KTJDG
|
||||
1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP
|
||||
12 VRPVC, 27 CNZTR => 2 XDBXC
|
||||
15 KTJDG, 12 BHXH => 5 XCVML
|
||||
3 BHXH, 2 VRPVC => 7 MZWV
|
||||
121 ORE => 7 VRPVC
|
||||
7 XCVML => 6 RJRHP
|
||||
5 BHXH, 4 VRPVC => 5 LTCX
|
||||
@@ -1 +0,0 @@
|
||||
2210736
|
||||
@@ -1 +0,0 @@
|
||||
14-1-3.in
|
||||
@@ -1 +0,0 @@
|
||||
82892753
|
||||
@@ -1 +0,0 @@
|
||||
14-1-4.in
|
||||
@@ -1 +0,0 @@
|
||||
5586022
|
||||
@@ -1 +0,0 @@
|
||||
14-1-5.in
|
||||
@@ -1 +0,0 @@
|
||||
460664
|
||||
@@ -1 +0,0 @@
|
||||
80871224585914546619083218645595
|
||||
@@ -1 +0,0 @@
|
||||
24176176
|
||||
@@ -1 +0,0 @@
|
||||
19617804207202209144916044189917
|
||||
@@ -1 +0,0 @@
|
||||
73745418
|
||||
@@ -1 +0,0 @@
|
||||
69317163492948606335995924319873
|
||||
@@ -1 +0,0 @@
|
||||
52432133
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user