Remove C++ solutions

This commit is contained in:
2021-01-23 15:00:04 +01:00
parent e88ef7b410
commit 69de955158
122 changed files with 0 additions and 3647 deletions

1
2019/.gitignore vendored
View File

@@ -1 +0,0 @@
cmake-build-*

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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.");
}

View File

@@ -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;
}

View File

@@ -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));

View File

@@ -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;
}

View File

@@ -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.");
}

View File

@@ -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;
}

View File

@@ -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';
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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';
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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()

View File

@@ -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";
}

View File

@@ -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();
}
}
}

View File

@@ -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.");
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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";
}

View File

@@ -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;
}

View File

@@ -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";
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
};
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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> &currentInputs() 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);
};
}

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@
100756

View File

@@ -1 +0,0 @@
33583

View File

@@ -1,2 +0,0 @@
R75,D30,R83,U83,L12,D49,R71,U7,L72
U62,R66,U55,R34,D71,R55,D58,R83

View File

@@ -1 +0,0 @@
159

View File

@@ -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

View File

@@ -1 +0,0 @@
135

View File

@@ -1,2 +0,0 @@
R8,U5,L5,D3
U7,R6,D4,L4

View File

@@ -1 +0,0 @@
6

View File

@@ -1 +0,0 @@
03-1-1.in

View File

@@ -1 +0,0 @@
610

View File

@@ -1 +0,0 @@
03-1-2.in

View File

@@ -1 +0,0 @@
410

View File

@@ -1 +0,0 @@
03-1-3.in

View File

@@ -1 +0,0 @@
30

View File

@@ -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

View File

@@ -1 +0,0 @@
42

View File

@@ -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

View File

@@ -1 +0,0 @@
4

View File

@@ -1 +0,0 @@
3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0

View File

@@ -1 +0,0 @@
43210

View File

@@ -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

View File

@@ -1 +0,0 @@
54321

View File

@@ -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

View File

@@ -1 +0,0 @@
65210

View File

@@ -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

View File

@@ -1 +0,0 @@
139629729

View File

@@ -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

View File

@@ -1 +0,0 @@
18216

View File

@@ -1,5 +0,0 @@
.#..#
.....
#####
....#
...##

View File

@@ -1 +0,0 @@
8

View File

@@ -1,10 +0,0 @@
......#.#.
#..#.#....
..#######.
.#.#.###..
.#..#.....
..#....#.#
#..#....#.
.##.#..###
##...#..#.
.#....####

View File

@@ -1 +0,0 @@
33

View File

@@ -1,10 +0,0 @@
#.#...#.#.
.###....#.
.#....#...
##.#.#.#.#
....#.#.#.
.##..###.#
..#...##..
..##....##
......#...
.####.###.

View File

@@ -1 +0,0 @@
35

View File

@@ -1,10 +0,0 @@
.#..#..###
####.###.#
....###.#.
..###.##.#
##.##.#.#.
....###..#
..#.#..#.#
#..#.#.###
.##...##.#
.....#.#..

View File

@@ -1 +0,0 @@
41

View File

@@ -1,20 +0,0 @@
.#..##.###...#######
##.############..##.
.#.######.########.#
.###.#######.####.#.
#####.##.#.##.###.##
..#####..#.#########
####################
#.####....###.#.#.##
##.#################
#####.##.###..####..
..######..##.#######
####.##.####...##..#
.#####..#.######.###
##...#.##########...
#.##########.#######
.####.#.###.###.#.##
....##.##.###..#####
.#.#.###########.###
#.#.#.#####.####.###
###.##.####.##.#..##

View File

@@ -1 +0,0 @@
210

View File

@@ -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>

View File

@@ -1 +0,0 @@
2772

View File

@@ -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>

View File

@@ -1 +0,0 @@
4686774924

View File

@@ -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

View File

@@ -1 +0,0 @@
31

View File

@@ -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

View File

@@ -1 +0,0 @@
165

View File

@@ -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

View File

@@ -1 +0,0 @@
13312

View File

@@ -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

View File

@@ -1 +0,0 @@
180697

View File

@@ -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

View File

@@ -1 +0,0 @@
2210736

View File

@@ -1 +0,0 @@
14-1-3.in

View File

@@ -1 +0,0 @@
82892753

View File

@@ -1 +0,0 @@
14-1-4.in

View File

@@ -1 +0,0 @@
5586022

View File

@@ -1 +0,0 @@
14-1-5.in

View File

@@ -1 +0,0 @@
460664

View File

@@ -1 +0,0 @@
80871224585914546619083218645595

View File

@@ -1 +0,0 @@
24176176

View File

@@ -1 +0,0 @@
19617804207202209144916044189917

View File

@@ -1 +0,0 @@
73745418

View File

@@ -1 +0,0 @@
69317163492948606335995924319873

View File

@@ -1 +0,0 @@
52432133

Some files were not shown because too many files have changed in this diff Show More