#include #include #include #include #include #include #include #include #include #include using namespace aocpp; using namespace intcode; namespace { auto GatherOutput(Machine m) -> Grid { std::vector rows; bool eol = true; // next output starts a new line Run(m, []() -> ValueType { throw std::runtime_error{"input not supported"}; }, [&](ValueType c) -> void { if (eol) { rows.emplace_back(); eol = false; } if ('\n' == c) { eol = true; } else { rows.back().push_back(static_cast(c));} } ); return Grid{std::move(rows)}; } /// 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> { // Determine starting location and direction Coord start{}, step{}; for (auto [c, v] : grid) { 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> 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 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 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>; auto ComputeProgramLoop( Path::const_iterator begin, Path::const_iterator end, std::vector & solution, std::map> & 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 solution; std::map> 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(std::istream & in, std::ostream & out) -> void { auto machine = Machine{ParseStream(in)}; auto const grid = GatherOutput(machine); auto const part1 = ScaffoldAlignments(grid); auto const path = ComputePath(grid); auto const program = ComputeProgram(path); out << "Part 1: " << part1 << std::endl; auto const part2 = GatherScore(std::move(machine), program); out << "Part 2: " << part2 << std::endl; }