diff --git a/2019/17.cpp b/2019/17.cpp new file mode 100644 index 0000000..aedf05f --- /dev/null +++ b/2019/17.cpp @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace aocpp; +using namespace intcode; + +namespace { + +auto GatherOutput(Machine m) -> Grid +{ + Grid grid; + bool eol = true; // next output starts a new line + + Run(m, []() -> ValueType { + throw std::runtime_error{"input not supported"}; + }, + [&](ValueType c) -> void { + if (eol) { + grid.rows.emplace_back(); + eol = false; + } + if ('\n' == c) { + eol = true; + } else { + grid.rows.back().push_back(static_cast(c));} + } + ); + return grid; +} + +/// 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{}; + 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> 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(int argc, char** argv) -> int { + auto machine = Machine{ParseStream(aocpp::Startup(argc, argv))}; + 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; +} diff --git a/2019/CMakeLists.txt b/2019/CMakeLists.txt index f67ede9..23489e9 100644 --- a/2019/CMakeLists.txt +++ b/2019/CMakeLists.txt @@ -43,6 +43,9 @@ target_link_libraries(14 aocpp) add_executable(15 15.cpp) target_link_libraries(15 aocpp intcode) +add_executable(17 17.cpp) +target_link_libraries(17 aocpp intcode) + add_executable(18 18.cpp) target_link_libraries(18 aocpp) diff --git a/lib/include/aocpp/Grid.hpp b/lib/include/aocpp/Grid.hpp index ba4cf4c..990ad77 100644 --- a/lib/include/aocpp/Grid.hpp +++ b/lib/include/aocpp/Grid.hpp @@ -13,6 +13,14 @@ namespace aocpp { struct Grid { std::vector rows; + auto contains(Coord c) const -> bool { + return + 0 <= c.x + && 0 <= c.y + && c.y < rows.size() + && c.x < rows[c.y].size(); + } + auto operator[](Coord c) -> char& { return rows[c.y][c.x]; } auto operator[](Coord c) const -> char { return rows[c.y][c.x]; } @@ -40,4 +48,4 @@ struct Grid { } -#endif \ No newline at end of file +#endif