aocpp/2019/17.cpp

227 lines
5.6 KiB
C++
Raw Normal View History

2022-11-12 17:16:28 -08:00
#include <algorithm>
#include <iostream>
#include <map>
#include <tuple>
#include <utility>
#include <vector>
#include <aocpp/Coord.hpp>
#include <aocpp/Grid.hpp>
#include <aocpp/Startup.hpp>
#include <intcode/intcode.hpp>
using namespace aocpp;
using namespace intcode;
namespace {
auto GatherOutput(Machine m) -> Grid
{
2022-11-28 09:20:50 -08:00
std::vector<std::string> rows;
2022-11-12 17:16:28 -08:00
bool eol = true; // next output starts a new line
Run(m, []() -> ValueType {
throw std::runtime_error{"input not supported"};
},
[&](ValueType c) -> void {
if (eol) {
2022-11-28 09:20:50 -08:00
rows.emplace_back();
2022-11-12 17:16:28 -08:00
eol = false;
}
if ('\n' == c) {
eol = true;
} else {
2022-11-28 09:20:50 -08:00
rows.back().push_back(static_cast<char>(c));}
2022-11-12 17:16:28 -08:00
}
);
2022-11-28 09:20:50 -08:00
return Grid{std::move(rows)};
2022-11-12 17:16:28 -08:00
}
/// Find all the 4-way intersections.
/// @return Sum of product of coordinates
auto ScaffoldAlignments(Grid const& grid) -> std::size_t
{
std::int64_t answer = 0;
std::int64_t h = grid.rows.size();
for (std::int64_t y = 1; y+1 < h; y++) {
auto const& row = grid.rows[y];
std::int64_t w = row.size();
for (std::int64_t x = 1; x+1 < w; x++) {
Coord c = {x,y};
if (grid[c] == '#' &&
grid[Up(c)] == '#' &&
grid[Down(c)] == '#' &&
grid[Left(c)] == '#' &&
grid[Right(c)] == '#')
{
answer += x*y;
}
}
}
return answer;
}
auto ComputePath(Grid const& grid)
-> std::vector<std::pair<char, std::int64_t>>
{
// Determine starting location and direction
Coord start{}, step{};
grid.each([&](Coord c, char v) {
switch (v) {
case '^': start = c; step = { 0, -1}; break;
case '>': start = c; step = { 0, 1}; break;
case '<': start = c; step = {-1, 0}; break;
case 'v': start = c; step = { 0, 1}; break;
}
});
auto is_path = [&](Coord c) {
return grid.contains(c) && grid[c] == '#';
};
std::vector<std::pair<char, std::int64_t>> result;
for(;;) {
char dir;
if (is_path(start + CW(step))) {
dir = 'R';
step = CW(step);
} else if (is_path(start + CCW(step))) {
dir = 'L';
step = CCW(step);
} else {
return result;
}
std::int64_t n = 0;
while (is_path(start + step)) {
start += step;
n++;
}
result.emplace_back(dir, n);
}
}
template <typename It>
auto RenderCommands(It begin, It end) -> std::string
{
std::string result;
while (begin != end) {
auto const& [m,n] = *begin++;
result += m;
result += ',';
result += std::to_string(n);
result += ',';
}
result.pop_back(); // trailing comma
return result;
}
template<class InputIter1, class InputIter2>
auto constexpr prefix(
InputIter1 first1, InputIter1 last1,
InputIter2 first2, InputIter2 last2
) -> bool
{
for(;;) {
if (first1 == last1) return true;
if (first2 == last2) return false;
if (*first1++ != *first2++) return false;
}
}
using Path = std::vector<std::pair<char, std::int64_t>>;
auto ComputeProgramLoop(
Path::const_iterator begin,
Path::const_iterator end,
std::vector<char> & solution,
std::map<char, std::pair<Path::const_iterator, Path::const_iterator>> & subroutines
) -> bool
{
// no path left to encode, we're done
if (begin == end) return true;
// Try using an existing subroutine
for (auto const& [k,v] : subroutines) {
if (prefix(v.first, v.second, begin, end)) {
solution.push_back(k);
auto begin_ = begin + (v.second - v.first);
if (ComputeProgramLoop(begin_, end, solution, subroutines)) return true;
solution.pop_back(); // rollback k
}
}
// Try allocating a new subroutine
if (subroutines.size() == 3) return false;
auto next_routine = 'A' + subroutines.size();
auto sub = subroutines.insert({next_routine, {}}).first;
solution.push_back(next_routine);
// Divide begin-end into begin-cursor and cursor-end
// Assign begin-end to a new subroutine and recursively solve
for (
auto cursor = begin+1;
cursor != end && RenderCommands(begin, cursor).size() <= 20;
++cursor
) {
sub->second = {begin, cursor};
if (ComputeProgramLoop(cursor, end, solution, subroutines)) return true;
}
// Deallocate the new subroutine; there was no solution
subroutines.erase(sub);
solution.pop_back();
return false;
}
auto ComputeProgram(Path const& path) -> std::string {
std::vector<char> solution;
std::map<char, std::pair<Path::const_iterator, Path::const_iterator>> subroutines;
if (!ComputeProgramLoop(path.begin(), path.end(), solution, subroutines)) {
throw std::runtime_error{"no solution found"};
}
std::string result;
for (char c : solution) {
result += c;
result += ',';
}
result.back() = '\n';
for (auto const& [_,v] : subroutines) {
result += RenderCommands(v.first, v.second);
result += '\n';
}
result += "n\n"; // no video feed
return result;
}
auto GatherScore(Machine m, std::string const& program) -> ValueType {
ValueType score {};
auto it = program.begin();
m.At(0) = 2; // put into fancy mode
Run(m,
[&]() -> ValueType {
if (it < program.end()) { return *it++; }
throw std::runtime_error{"insufficient input"};
},
[&](ValueType o) { score = o; });
return score;
}
} // namespace
auto main(int argc, char** argv) -> int {
auto machine = Machine{ParseStream(*aocpp::Startup(argc, argv))};
2022-11-12 17:16:28 -08:00
auto grid = GatherOutput(machine);
auto part1 = ScaffoldAlignments(grid);
auto path = ComputePath(grid);
auto program = ComputeProgram(path);
std::cout << "Part 1: " << part1 << std::endl;
auto part2 = GatherScore(std::move(machine), program);
std::cout << "Part 2: " << part2 << std::endl;
}