diff --git a/2019/10.cpp b/2019/10.cpp new file mode 100644 index 0000000..ff3bcfc --- /dev/null +++ b/2019/10.cpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace aocpp; + +namespace { + +template +struct Rational { + T numerator, denominator; + + Rational() : numerator{0}, denominator{1} {} + Rational(T x) : numerator{x}, denominator{1} {} + Rational(T n, T d) { + if (d == 0) { numerator = 1; denominator = 0; return; } + if (d < 0) { d = -d; n = -n; } + T m = std::gcd(n,d); + numerator = n / m; + denominator = d / m; + } + auto constexpr operator<=>(Rational const& rhs) const -> std::strong_ordering { + return numerator * rhs.denominator <=> rhs.numerator * denominator; + } +}; + +template +auto operator<<(std::ostream & out, Rational const& r) -> std::ostream & { + return out << r.numerator << "/" << r.denominator; +} + +/// Map a vector to a rational number such that the numbers are +/// ordered starting with up and proceeding clockwise. +/// Each quadrant is mapped to a whole number. Slopes in that +/// quadrant are mapped to a fraction between 0 and 1. +auto angle(Coord c) -> Rational { + auto mk = [](std::int64_t q, std::int64_t a, std::int64_t b) { + return Rational{q*(a+b)-b, a+b}; + }; + return + c.x == 0 && c.y == 0 ? -1 : + c.x >= 0 && c.y < 0 ? mk(1, c.x, -c.y) : + c.x > 0 && c.y >= 0 ? mk(2, c.y, c.x) : + c.x <= 0 && c.y > 0 ? mk(3,-c.x, c.y) : + mk(4, c.y, c.x); +} + +auto Part1(Grid const& grid) { + std::size_t best = 0; + Coord base {}; + grid.each([&](Coord src, char s) { + if ('#' == s) { + std::set> angles; + grid.each([&](Coord dst, char d){ + if ('#' == d && src != dst) { + auto delta = dst - src; + angles.insert(angle(dst - src)); + } + }); + auto n = angles.size(); + if (n > best) { + best = n; + base = src; + } + } + }); + return std::make_pair(best, base); +} + +auto Part2(Grid const& grid, Coord base) { + std::map, std::vector> targets; + + // arrange all the other asteroids by their angle relative to the base + grid.each([&](Coord c, char v){ + if (c != base && v == '#') { + targets[angle(c-base)].push_back(c); + } + }); + + // Sort to vectors per angle such that the nearest asteroids are at + // the end of the list + for (auto & [_, vec] : targets) { + std::sort(vec.begin(), vec.end(), [](auto const& x, auto const& y) { + return Norm1(x) > Norm1(y); + }); + } + + // Remove 199 asteroids from the asteroid list + auto cursor = targets.begin(); + for (std::size_t n = 199; n > 0; n--) { + if (targets.empty()) { throw std::runtime_error{"no solution to part 2"}; } + + cursor->second.pop_back(); + if (cursor->second.empty()) { + cursor = targets.erase(cursor); + } else { + cursor++; + } + + if (cursor == targets.end()) { + cursor = targets.begin(); + } + } + + // Report the location of the 200th asteroid + auto const& asteroid = cursor->second.back(); + return 100 * asteroid.x + asteroid.y; +} + +} // namespace + +auto main(int argc, char** argv) -> int { + auto grid = Grid::Parse(Startup(argc, argv)); + auto [part1, base] = Part1(grid); + std::cout << "Part 1: " << part1 << std::endl; + std::cout << "Part 2: " << Part2(grid, base) << std::endl; +} diff --git a/2019/18.cpp b/2019/18.cpp index 3df8e82..b0ce910 100644 --- a/2019/18.cpp +++ b/2019/18.cpp @@ -17,6 +17,7 @@ #include #include +#include using namespace aocpp; @@ -38,37 +39,18 @@ auto SetKey(Doors& doors, char key) { return doors.set(std::toupper(key) - 'A'); } -struct Map { - std::vector rows; - - auto operator[](Coord c) -> char& { return rows[c.y][c.x]; } - auto operator[](Coord c) const -> char { return rows[c.y][c.x]; } - - static auto Parse(std::istream & in) -> Map { - Map result; - std::string line; - while (std::getline(in, line)) { - result.rows.emplace_back(std::move(line)); - } - return {result}; - } -}; - -auto FindFeatures(Map const& map) -> Features { +auto FindFeatures(Grid const& grid) -> Features { Features features; - for (std::size_t y = 0; y < map.rows.size(); y++) { - for (std::size_t x = 0; x < map.rows[y].size(); x++) { - auto const c = map[{std::int64_t(x),std::int64_t(y)}]; - if ('#' != c && '.' != c) { - features[c] = {std::int64_t(x),std::int64_t(y)}; - } + grid.each([&](Coord c, char v) { + if ('#' != v && '.' != v) { + features[v] = c; } - } + }); return features; } auto FindDistancesFrom( - Map const& map, + Grid const& grid, char const start_letter, Coord const start ) { @@ -84,7 +66,7 @@ auto FindDistancesFrom( // Don't visit the same coordinate twice if (!seen.insert(here).second) continue; - auto const c = map[here]; + auto const c = grid[here]; if (c == '#') continue; // avoid walls // success, we've found a key, record the path @@ -107,11 +89,11 @@ auto FindDistancesFrom( return result; } -auto FindDistances(Map const& map, Features const& features) { +auto FindDistances(Grid const& grid, Features const& features) { Distances distances; for (auto const [start_letter, start_coord] : features) { if (!std::isupper(start_letter)) { - distances[start_letter] = FindDistancesFrom(map, start_letter, start_coord); + distances[start_letter] = FindDistancesFrom(grid, start_letter, start_coord); } } return distances; @@ -163,10 +145,10 @@ auto SolveMaze( } // Part 2 instructs us to update the map splitting it into 4 quadrants -auto Part2(Map& map, Features & features) { +auto Part2(Grid & grid, Features & features) { auto const start = features['@']; for (auto const fn : directions) { - map[fn(start)] = '#'; + grid[fn(start)] = '#'; } features.erase('@'); features['^'] = Up(Left (start)); @@ -178,12 +160,12 @@ auto Part2(Map& map, Features & features) { } // namespace auto main(int argc, char** argv) -> int { - auto map = Map::Parse(Startup(argc, argv)); - auto features = FindFeatures(map); - auto distances = FindDistances(map, features); + auto grid = Grid::Parse(Startup(argc, argv)); + auto features = FindFeatures(grid); + auto distances = FindDistances(grid, features); std::cout << "Part 1: " << SolveMaze(distances, "@") << std::endl; - Part2(map, features); - distances = FindDistances(map, features); + Part2(grid, features); + distances = FindDistances(grid, features); std::cout << "Part 2: " << SolveMaze(distances, "^&*$") << std::endl; } diff --git a/2019/20.cpp b/2019/20.cpp index 811fb55..773d62e 100644 --- a/2019/20.cpp +++ b/2019/20.cpp @@ -17,6 +17,7 @@ #include #include +#include using namespace aocpp; @@ -28,43 +29,27 @@ using Name = std::string; using Portals = std::map; using Distances = std::map>>; -struct Map { - std::vector rows; - - auto operator[](Coord c) -> char& { return rows[c.y][c.x]; } - auto operator[](Coord c) const -> char { return rows[c.y][c.x]; } - - static auto Parse(std::istream & in) -> Map { - Map result; - std::string line; - while (std::getline(in, line)) { - result.rows.emplace_back(std::move(line)); - } - return {result}; - } -}; - -auto FindPortals(Map const& map) -> Portals { +auto FindPortals(Grid const& grid) -> Portals { Portals portals; - std::int64_t w = map.rows[0].size(); - std::int64_t h = map.rows.size(); + std::int64_t w = grid.rows[0].size(); + std::int64_t h = grid.rows.size(); for (std::int64_t x = 1; x < w-1; x++) { for (std::int64_t y = 1; y < h-1; y++) { Coord const c = {x,y}; - auto const v = map[c]; + auto const v = grid[c]; if (std::isupper(v)) { char polarity = x==1 || x==w-2 || y==1 || y==h-2 ? '-' : '+'; - if (map[Up(c)] == '.') { - portals[{polarity, v, map[Down(c)]}] = Up(c); - } else if (map[Down(c)] == '.') { - portals[{polarity, map[Up(c)], v}] = Down(c); - } else if (map[Left(c)] == '.') { - portals[{polarity, v, map[Right(c)]}] = Left(c); - } else if (map[Right(c)] == '.') { - portals[{polarity, map[Left(c)], v}] = Right(c); + if (grid[Up(c)] == '.') { + portals[{polarity, v, grid[Down(c)]}] = Up(c); + } else if (grid[Down(c)] == '.') { + portals[{polarity, grid[Up(c)], v}] = Down(c); + } else if (grid[Left(c)] == '.') { + portals[{polarity, v, grid[Right(c)]}] = Left(c); + } else if (grid[Right(c)] == '.') { + portals[{polarity, grid[Left(c)], v}] = Right(c); } } } @@ -73,7 +58,7 @@ auto FindPortals(Map const& map) -> Portals { } auto FindDistancesFrom( - Map const& map, + Grid const& grid, std::map const& names, std::string const start_name, Coord const start @@ -90,7 +75,7 @@ auto FindDistancesFrom( // Don't visit the same coordinate twice if (!seen.insert(here).second) continue; - auto const c = map[here]; + auto const c = grid[here]; if (c != '.') continue; // avoid walls // success, we've found a key, record the path @@ -113,7 +98,7 @@ auto OtherName(Name name) { return name; } -auto FindDistances(Map const& map, Portals const& portals) { +auto FindDistances(Grid const& grid, Portals const& portals) { Distances distances; std::map names; @@ -122,7 +107,7 @@ auto FindDistances(Map const& map, Portals const& portals) { } for (auto const& [start_name, start_coord] : portals) { - distances[start_name] = FindDistancesFrom(map, names, start_name, start_coord); + distances[start_name] = FindDistancesFrom(grid, names, start_name, start_coord); } return distances; @@ -165,7 +150,7 @@ auto SolveMaze(Distances const& distances, bool const recursive) -> std::int64_t } // namespace auto main(int argc, char** argv) -> int { - auto map = Map::Parse(Startup(argc, argv)); + auto map = Grid::Parse(Startup(argc, argv)); auto portals = FindPortals(map); auto distances = FindDistances(map, portals); diff --git a/2019/24.cpp b/2019/24.cpp index ab8c3c7..21494cb 100644 --- a/2019/24.cpp +++ b/2019/24.cpp @@ -12,39 +12,21 @@ #include #include +#include using namespace aocpp; namespace { -struct Map { - std::vector rows; - - auto operator[](Coord c) -> char& { return rows[c.y][c.x]; } - auto operator[](Coord c) const -> char { return rows[c.y][c.x]; } - - static auto Parse(std::istream & in) -> Map { - Map result; - std::string line; - while (std::getline(in, line)) { - result.rows.emplace_back(std::move(line)); - } - return {result}; - } -}; - -auto FindBugs(Map const& map) -> std::vector { +auto FindBugs(Grid const& grid) -> std::vector { std::vector result; - std::int64_t w = map.rows[0].size(); - std::int64_t h = map.rows.size(); + std::int64_t w = grid.rows[0].size(); + std::int64_t h = grid.rows.size(); - for (std::int64_t x = 0; x < w; x++) { - for (std::int64_t y = 0; y < h; y++) { - Coord const c = {x,y}; - if (map[c] == '#') result.push_back(c); - } - } + grid.each([&](Coord c, char v) { + if (v == '#') result.push_back(c); + }); return result; } @@ -60,9 +42,9 @@ struct Neighbor1 { struct Neighbor2 { using C3 = std::pair; auto operator()(C3 cd, auto f) const -> void { - auto const [c,d] = cd; - auto left_neighbors = [&](auto k_, auto k) { + auto left_neighbors = [cd,f](auto k_, auto k) { + auto const [c,d] = cd; auto c_ = k_(c); if (c_.x == 1 && c_.y == 0) { for (std::int64_t yi = -2; yi <= 2; yi++) { @@ -139,7 +121,7 @@ auto Part2(std::vector const& bugs) { } // namespace auto main(int argc, char** argv) -> int { - auto const bugs = FindBugs(Map::Parse(Startup(argc, argv))); + auto const bugs = FindBugs(Grid::Parse(Startup(argc, argv))); std::cout << "Part 1: " << Part1(bugs) << std::endl; std::cout << "Part 2: " << Part2(std::move(bugs)) << std::endl; } diff --git a/2019/CMakeLists.txt b/2019/CMakeLists.txt index 5ebc73b..7a24aef 100644 --- a/2019/CMakeLists.txt +++ b/2019/CMakeLists.txt @@ -25,6 +25,9 @@ target_link_libraries(08 aocpp) add_executable(09 09.cpp) target_link_libraries(09 aocpp intcode) +add_executable(10 10.cpp) +target_link_libraries(10 aocpp) + add_executable(11 11.cpp) target_link_libraries(11 aocpp intcode)