diff --git a/2022/15.cpp b/2022/15.cpp index d2595de..dd4c288 100644 --- a/2022/15.cpp +++ b/2022/15.cpp @@ -1,13 +1,12 @@ #include #include #include -#include +#include #include #include #include #include -#include #include #include @@ -70,12 +69,15 @@ auto Parse(std::istream & in) -> Input struct Range { std::int64_t lo, hi; auto operator<=>(Range const& rhs) const = default; + auto size() const -> std::size_t { + return hi - lo; + } }; template -auto subtract( +auto subtract_to( std::array x, - std::array y, + std::array const& y, std::vector> & out ) -> void { @@ -98,6 +100,17 @@ auto subtract( } } +template +auto subtract( + std::array const& x, + std::array const& y +) -> std::vector> +{ + std::vector> result; + subtract_to(x, y, result); + return result; +} + auto Segment(std::int64_t center, std::int64_t radius) -> Range { return { center - radius, center + radius + 1}; } @@ -111,16 +124,59 @@ auto Corner(std::array const& s) -> aocpp::Coord return { (s[0].lo + s[1].lo) / 2, (s[0].lo - s[1].lo) / 2}; } -auto Part2(Input const& input) -> void { +auto RowSlice(aocpp::Coord sensor, std::int64_t radius, std::int64_t row) +-> std::optional> +{ + auto dy = std::abs(row - sensor.y); + auto dx = radius - dy; + if (dx >= 0) { + return {{Segment(sensor.x, dx)}}; + } else { + return {}; + } +} - auto region = std::vector>{Square({2'000'000, 2'000'000}, 2'000'000)}; +auto Part1(Input const& input, std::int64_t search) -> std::size_t { + auto region = std::vector>{}; + auto region_next = std::vector>{}; + + for (auto const& entry : input) { + auto radius = aocpp::Norm1(entry.beacon - entry.sensor); + if (auto s = RowSlice(entry.sensor, radius, search)) { + region_next.push_back(*s); + for (auto const& x : region) { + subtract_to(x, *s, region_next); + } + std::swap(region, region_next); + region_next.clear(); + } + } + + std::size_t result {}; + for (auto const& x : region) { + result += x[0].hi - x[0].lo; + } + + std::set seen; + for (auto const& entry : input) { + if (entry.beacon.y == search && seen.insert(entry.beacon.x).second) { + result--; + } + } + + return result; +} + +auto Part2(Input const& input, std::int64_t search) -> std::int64_t { + + auto region = std::vector>{Square({search/2, search/2}, search)}; auto region_next = std::vector>{}; for (auto const& entry : input) { auto radius = aocpp::Norm1(entry.beacon - entry.sensor); auto s = Square(entry.sensor, radius); for (auto const& x : region) { - subtract(x, s, region_next); + subtract_to(x, s, region_next); } std::swap(region, region_next); region_next.clear(); @@ -128,19 +184,20 @@ auto Part2(Input const& input) -> void { for (auto const& entry : region) { auto corner = Corner(entry); - //if (0 <= corner.x && corner.x <= 4000000 && - // 0 <= corner.y && corner.y <= 4000000) - //{ - std::cout << corner << " " << 4'000'000 * corner.x + corner.y << std::endl; - //} + if (0 <= corner.x && corner.x <= search && + 0 <= corner.y && corner.y <= search) + { + return 4'000'000 * corner.x + corner.y; + } } + + throw std::runtime_error{"no solution to part 2"}; } } // namespace TEST_SUITE("2022-15 examples") { - /* - TEST_CASE("1D subtraction") { + TEST_CASE("subtraction") { CHECK(subtract<1>({1,3}, {4,5}) == std::vector>{{1,3}}); CHECK(subtract<1>({4,5}, {1,3}) == std::vector>{{4,5}}); CHECK(subtract<1>({1,3}, {2,4}) == std::vector>{{1,2}}); @@ -149,8 +206,17 @@ TEST_SUITE("2022-15 examples") { CHECK(subtract<1>({1,4}, {2,3}) == std::vector>{{1,2}, {3,4}}); CHECK(subtract<1>({1,4}, {2,4}) == std::vector>{{1,2}}); CHECK(subtract<1>({1,4}, {1,3}) == std::vector>{{3,4}}); + + CHECK(subtract<2>( + std::array{Range{0,3}, Range{0,4}}, std::array{Range{1,2},Range{2,3}}) + == + std::vector>{ + std::array{Range{0,1}, Range{0,4}}, + std::array{Range{2,3}, Range{0,4}}, + std::array{Range{1,2}, Range{0,2}}, + std::array{Range{1,2}, Range{3,4}} + }); } - */ TEST_CASE("documented example") { auto in = std::istringstream{ @@ -170,12 +236,14 @@ TEST_SUITE("2022-15 examples") { "Sensor at x=20, y=1: closest beacon is at x=15, y=3\n" }; auto const input = Parse(in); + CHECK(26 == Part1(input, 10)); + CHECK(56000011 == Part2(input, 20)); } } auto Main(std::istream & in, std::ostream & out) -> void { auto const input = Parse(in); - //out << "Part 1: " << Part1(bottom, world) << std::endl; - Part2(input); + out << "Part 1: " << Part1(input, 2'000'000) << std::endl; + out << "Part 2: " << Part2(input, 4'000'000) << std::endl; }