|
| 1 | +#include "challenge21.hpp" |
| 2 | + |
| 3 | +#include "helper.hpp" |
| 4 | +#include "print.hpp" |
| 5 | + |
| 6 | +#include <algorithm> |
| 7 | +#include <cmath> |
| 8 | +#include <unordered_set> |
| 9 | + |
| 10 | +using namespace std::string_view_literals; |
| 11 | + |
| 12 | +namespace { |
| 13 | +template<typename T> |
| 14 | +struct Coordinate { |
| 15 | + T Row; |
| 16 | + T Column; |
| 17 | + |
| 18 | + constexpr bool operator==(const Coordinate&) const noexcept = default; |
| 19 | + constexpr auto operator<=>(const Coordinate&) const noexcept = default; |
| 20 | + |
| 21 | + Coordinate left(void) const noexcept { |
| 22 | + return {Row, Column - 1}; |
| 23 | + } |
| 24 | + |
| 25 | + Coordinate right(void) const noexcept { |
| 26 | + return {Row, Column + 1}; |
| 27 | + } |
| 28 | + |
| 29 | + Coordinate up(void) const noexcept { |
| 30 | + return {Row - 1, Column}; |
| 31 | + } |
| 32 | + |
| 33 | + Coordinate down(void) const noexcept { |
| 34 | + return {Row + 1, Column}; |
| 35 | + } |
| 36 | +}; |
| 37 | +} //namespace |
| 38 | + |
| 39 | +namespace std { |
| 40 | +template<typename T> |
| 41 | +struct hash<Coordinate<T>> { |
| 42 | + size_t operator()(const Coordinate<T>& c) const noexcept { |
| 43 | + constexpr auto bits = std::numeric_limits<T>::digits / 2; |
| 44 | + constexpr auto mask = ((T{1} << bits) - 1); |
| 45 | + return std::hash<T>{}((c.Row << bits) | (c.Column & mask)); |
| 46 | + } |
| 47 | +}; |
| 48 | + |
| 49 | +template<typename T, typename U> |
| 50 | +struct hash<std::pair<Coordinate<T>, U>> { |
| 51 | + size_t operator()(const std::pair<Coordinate<T>, U>& p) const noexcept { |
| 52 | + return std::hash<Coordinate<T>>{}(p.first) ^ std::hash<U>{}(p.second); |
| 53 | + } |
| 54 | +}; |
| 55 | +} //namespace std |
| 56 | + |
| 57 | +namespace { |
| 58 | +using MyCoordinate = Coordinate<std::int64_t>; |
| 59 | +using Map = const std::vector<std::string_view>&; |
| 60 | + |
| 61 | +std::int64_t MaxColumn = 0; |
| 62 | +std::int64_t MaxRow = 0; |
| 63 | + |
| 64 | +bool isValid(const MyCoordinate& coordinate) noexcept { |
| 65 | + return coordinate.Column >= 0 && coordinate.Column < MaxColumn && coordinate.Row >= 0 && coordinate.Row < MaxRow; |
| 66 | +} |
| 67 | + |
| 68 | +MyCoordinate findStart(Map map) noexcept { |
| 69 | + MyCoordinate ret; |
| 70 | + for ( ret.Row = 0; ret.Row < MaxRow; ++ret.Row ) { |
| 71 | + ret.Column = static_cast<std::int64_t>(map[static_cast<std::size_t>(ret.Row)].find('S')); |
| 72 | + |
| 73 | + if ( static_cast<std::size_t>(ret.Column) != std::string::npos ) { |
| 74 | + return ret; |
| 75 | + } //if ( static_cast<std::size_t>(ret.Column) != std::string::npos ) |
| 76 | + } //for ( ret.Row = 0; ret.Row < MaxRow; ++ret.Row ) |
| 77 | + return {}; |
| 78 | +} |
| 79 | + |
| 80 | +template<bool Infinite> |
| 81 | +struct ReachableCalculator { |
| 82 | + ::Map Map; |
| 83 | + |
| 84 | + ReachableCalculator(::Map map) noexcept : Map{map} { |
| 85 | + return; |
| 86 | + } |
| 87 | + |
| 88 | + std::int64_t calcReachable(MyCoordinate start, std::int64_t totalSteps) noexcept { |
| 89 | + std::unordered_set<MyCoordinate> alreadySeen; |
| 90 | + std::array<std::int64_t, 2> counter{}; |
| 91 | + |
| 92 | + std::vector<MyCoordinate> current; |
| 93 | + std::vector<MyCoordinate> next{start}; |
| 94 | + |
| 95 | + //Step 0 ist die Start Position. |
| 96 | + for ( std::size_t step = 0; step <= static_cast<std::size_t>(totalSteps); ++step ) { |
| 97 | + std::swap(current, next); |
| 98 | + next.clear(); |
| 99 | + |
| 100 | + for ( auto coordinate : current ) { |
| 101 | + const auto normalizedCoordinate = [](MyCoordinate c) noexcept { |
| 102 | + if constexpr ( !Infinite ) { |
| 103 | + return c; |
| 104 | + } //if constexpr ( !Infinite ) |
| 105 | + else { |
| 106 | + while ( c.Row < 0 ) { |
| 107 | + c.Row += MaxRow; |
| 108 | + } //while ( c.Row < 0 ) |
| 109 | + c.Row %= MaxRow; |
| 110 | + |
| 111 | + while ( c.Column < 0 ) { |
| 112 | + c.Column += MaxColumn; |
| 113 | + } //while ( c.Column < 0 ) |
| 114 | + c.Column %= MaxColumn; |
| 115 | + |
| 116 | + return c; |
| 117 | + } //else -> if constexpr( !Infinite ) |
| 118 | + }(coordinate); |
| 119 | + |
| 120 | + if constexpr ( !Infinite ) { |
| 121 | + if ( !isValid(coordinate) ) { |
| 122 | + continue; |
| 123 | + } //if ( !isValid(coordinate) ) |
| 124 | + } //if constexpr ( !Infinite ) |
| 125 | + |
| 126 | + const auto plot = Map[static_cast<std::size_t>(normalizedCoordinate.Row)] |
| 127 | + [static_cast<std::size_t>(normalizedCoordinate.Column)]; |
| 128 | + |
| 129 | + if ( plot == '#' ) { |
| 130 | + continue; |
| 131 | + } //if ( plot == '#' ) |
| 132 | + |
| 133 | + if ( !alreadySeen.insert(coordinate).second ) { |
| 134 | + continue; |
| 135 | + } //if ( !alreadySeen.insert(coordinate).second ) |
| 136 | + alreadySeen.insert(coordinate); |
| 137 | + ++counter[step % 2]; |
| 138 | + |
| 139 | + next.push_back(coordinate.down()); |
| 140 | + next.push_back(coordinate.up()); |
| 141 | + next.push_back(coordinate.left()); |
| 142 | + next.push_back(coordinate.right()); |
| 143 | + } //for ( auto coordinate : current ) |
| 144 | + } //for ( int step = 0; step <= totalSteps; ++step ) |
| 145 | + |
| 146 | + return counter[static_cast<std::size_t>(totalSteps) % 2]; |
| 147 | + } |
| 148 | +}; |
| 149 | +} //namespace |
| 150 | + |
| 151 | +bool challenge21(Map map) { |
| 152 | + throwIfInvalid(!map.empty()); |
| 153 | + |
| 154 | + MaxRow = static_cast<std::int64_t>(map.size()); |
| 155 | + MaxColumn = static_cast<std::int64_t>(map.front().size()); |
| 156 | + const auto start = findStart(map); |
| 157 | + throwIfInvalid(MaxColumn == MaxRow); |
| 158 | + throwIfInvalid(start.Row == MaxRow / 2); |
| 159 | + throwIfInvalid(start.Column == MaxColumn / 2); |
| 160 | + |
| 161 | + ReachableCalculator<false> partOneCalculator{map}; |
| 162 | + auto reachable = partOneCalculator.calcReachable(start, 64); |
| 163 | + myPrint(" == Result of Part 1: {:d} ==\n", reachable); |
| 164 | + |
| 165 | + ReachableCalculator<true> partTwoCalculator{map}; |
| 166 | + auto calcPart2 = [&partTwoCalculator, &start](int steps) noexcept { |
| 167 | + //Extrapolation über ein quadratisches Polynom. Nicht das ich selbst auf die Idee gekommen wäre... |
| 168 | + auto y1 = partTwoCalculator.calcReachable(start, start.Row); |
| 169 | + auto y2 = partTwoCalculator.calcReachable(start, MaxRow + start.Row); |
| 170 | + auto y3 = partTwoCalculator.calcReachable(start, MaxRow * 2 + start.Row); |
| 171 | + |
| 172 | + auto a = (y3 + y1 - 2 * y2) / 2; |
| 173 | + auto b = (4 * y2 - 3 * y1 - y3) / 2; |
| 174 | + auto c = y1; |
| 175 | + auto evalAt = (steps - start.Row) / MaxRow; |
| 176 | + auto ret = (a * evalAt * evalAt) + (b * evalAt) + c; |
| 177 | + return ret; |
| 178 | + }; |
| 179 | + auto reachable2 = calcPart2(26501365); |
| 180 | + myPrint(" == Result of Part 2: {:d} ==\n", reachable2); |
| 181 | + |
| 182 | + return reachable == 3858 && reachable2 == 636'350'496'972'143; |
| 183 | +} |
0 commit comments