17
This commit is contained in:
parent
5b21e37772
commit
40c6998e05
226
2019/17.cpp
Normal file
226
2019/17.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
#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
|
||||
{
|
||||
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<char>(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<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))};
|
||||
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;
|
||||
}
|
@ -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)
|
||||
|
||||
|
@ -13,6 +13,14 @@ namespace aocpp {
|
||||
struct Grid {
|
||||
std::vector<std::string> 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
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user