1+ #include " common.h"
2+
3+ namespace {
4+ #define NOT_FOUND -1
5+ using Pos = std::complex <size_t >;
6+ using Heading = std::complex <long >;
7+
8+ long solve (const auto &input, Pos st, Pos end) {
9+ size_t m = input.size (), n = input[0 ].size ();
10+
11+ auto hasher = [](const Pos &k) -> std::size_t {
12+ return std::hash<long >()(k.real ()) ^ std::hash<long >()(k.imag ());
13+ };
14+ auto within_bounds = [&m, &n](const Heading &v) -> bool {
15+ return v.imag () >= 0 && v.real () >= 0 && v.imag () < static_cast <long >(n) &&
16+ v.real () < static_cast <long >(m);
17+ };
18+ auto within_height = [&input](const Pos &u, const Heading &v) -> bool {
19+ return (input[v.real ()][v.imag ()] - input[u.real ()][u.imag ()] <= 1 );
20+ };
21+ std::array<Heading, 4 > dirs = {Heading{-1 , 0 }, Heading{1 , 0 }, Heading{0 , -1 },
22+ Heading{0 , 1 }};
23+ std::deque<std::tuple<Pos, long >> frontier{{st, 0 }};
24+ std::unordered_set<Pos, decltype (hasher)> visited{st};
25+ long res{NOT_FOUND };
26+ while (frontier.size () > 0 && res == NOT_FOUND ) {
27+ const auto &[u, d] = frontier.front ();
28+ if (u == end) {
29+ res = d;
30+ continue ;
31+ }
32+ for (const auto &dir : dirs) {
33+ Heading v = dir + static_cast <Heading>(u);
34+ if (!visited.contains (static_cast <Pos>(v)) && within_bounds (v) &&
35+ within_height (u, v)) {
36+ frontier.push_back ({v, d + 1 });
37+ visited.emplace (v);
38+ }
39+ }
40+ frontier.pop_front ();
41+ }
42+ return res;
43+ }
44+
45+ long p1 (auto input) {
46+ Pos st{}, end{};
47+ for (size_t i{0 }; const auto &line : input) {
48+ if (size_t j{0 }; (j = line.find (" S" )) != std::string::npos)
49+ st = Pos{i, j};
50+ if (size_t j{0 }; (j = line.find (" E" )) != std::string::npos)
51+ end = Pos{i, j};
52+ ++i;
53+ }
54+ input[st.real ()][st.imag ()] = ' a' ;
55+ input[end.real ()][end.imag ()] = ' z' ;
56+
57+ const auto res = solve (input, st, end);
58+ ASSERT_EXPR (res != NOT_FOUND , " Failed to find solution" );
59+ return res;
60+ }
61+
62+ long p2 (auto input) {
63+ size_t m = input.size (), n = input[0 ].size ();
64+
65+ std::vector<Pos> strts{};
66+ Pos end{};
67+ for (size_t i{0 }; i < m; ++i) {
68+ for (size_t j{0 }; j < n; ++j) {
69+ if (input[i][j] == ' a' || input[i][j] == ' S' ) {
70+ strts.emplace_back (Pos{i, j});
71+ input[i][j] = ' a' ;
72+ }
73+ if (input[i][j] == ' E' ) {
74+ end = Pos{i, j};
75+ input[i][j] = ' z' ;
76+ }
77+ }
78+ }
79+
80+ std::vector<long > res{};
81+ for (const auto &st : strts) {
82+ const auto t = solve (input, st, end);
83+ if (t != NOT_FOUND )
84+ res.emplace_back (t);
85+ }
86+ ASSERT_EXPR (res.size () != 0 , " Failed to find solution" );
87+ return std::ranges::min (res);
88+ }
89+ } // namespace
90+
91+ int main () {
92+ const auto &input = gb::advent2021::readIn ();
93+ gb::advent2021::writeOut (std::to_string (p1 (input)));
94+ gb::advent2021::writeOut (std::to_string (p2 (input)));
95+ }
0 commit comments