Skip to content

Commit 4f48ea9

Browse files
Challenge 21
1 parent 5d781eb commit 4f48ea9

File tree

4 files changed

+196
-0
lines changed

4 files changed

+196
-0
lines changed

advent-of-code-2023.qbs

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ Project {
3535
"challenge2.hpp",
3636
"challenge20.cpp",
3737
"challenge20.hpp",
38+
"challenge21.cpp",
39+
"challenge21.hpp",
3840
"challenge3.cpp",
3941
"challenge3.hpp",
4042
"challenge4.cpp",

challenge21.cpp

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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+
}

challenge21.hpp

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef CHALLENGE21_HPP
2+
#define CHALLENGE21_HPP
3+
4+
#include <string_view>
5+
#include <vector>
6+
7+
bool challenge21(const std::vector<std::string_view>& input);
8+
9+
#endif //CHALLENGE21_HPP

main.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "challenge19.hpp"
1212
#include "challenge2.hpp"
1313
#include "challenge20.hpp"
14+
#include "challenge21.hpp"
1415
#include "challenge3.hpp"
1516
#include "challenge4.hpp"
1617
#include "challenge5.hpp"
@@ -140,6 +141,7 @@ int main(int argc, char* argv[]) {
140141
case 18 : runAndAdd(challenge18); break;
141142
case 19 : runAndAdd(challenge19); break;
142143
case 20 : runAndAdd(challenge20); break;
144+
case 21 : runAndAdd(challenge21); break;
143145

144146
default : {
145147
--challengesRun;

0 commit comments

Comments
 (0)