summaryrefslogtreecommitdiffstats
path: root/day04/solution.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'day04/solution.cpp')
-rw-r--r--day04/solution.cpp119
1 files changed, 119 insertions, 0 deletions
diff --git a/day04/solution.cpp b/day04/solution.cpp
new file mode 100644
index 0000000..6fa5ccd
--- /dev/null
+++ b/day04/solution.cpp
@@ -0,0 +1,119 @@
1#include <array>
2#include <iterator>
3#include <print>
4#include <iostream>
5#include <ranges>
6#include <string_view>
7#include <algorithm>
8#include <functional>
9#include <utility>
10#include <vector>
11#include <tuple>
12#include <map>
13#include <unordered_map>
14#include <set>
15#include <unordered_set>
16#include <numeric>
17
18using namespace std::literals;
19namespace views = std::views;
20namespace ranges = std::ranges;
21
22const auto parse_input() {
23 std::vector<std::string> grid;
24
25 for (std::string line; std::getline(std::cin, line);) {
26 grid.push_back(std::move(line));
27 }
28
29 return grid;
30}
31
32using IndexPair = std::pair<int, int>;
33
34void part1(auto input) {
35 uint64_t answer{0};
36 const int width(input[0].size());
37 const int height(input.size());
38
39 constexpr auto zeros = views::repeat(0, 4);
40 constexpr auto incr = views::iota(0, 4);
41 constexpr auto decr = incr | views::transform(std::negate<>{});
42 // Pack directions into a tuple because they have different types
43 constexpr auto directions = std::tuple{
44 views::zip(decr, zeros), // top
45 views::zip(decr, incr), // top-right
46 views::zip(zeros, incr), // right
47 views::zip(incr, incr), // bottom-right
48 views::zip(incr, zeros), // bottom
49 views::zip(incr, decr), // bottom-left
50 views::zip(zeros, decr), // left
51 views::zip(decr, decr), // top-left
52 };
53
54 for (const auto [i, j] :
55 views::cartesian_product(views::iota(0, width),
56 views::iota(0, height))) {
57
58 // std::apply is needed to unfold the ‘directions’ tuple.
59 std::apply([=, &answer](auto &&... dirs) {
60 auto add_offset = [i, j](IndexPair e) {
61 const auto [di, dj] = e;
62 return std::tuple{i + di, j + dj};
63 };
64
65 auto in_bounds = [width, height](IndexPair e) -> bool {
66 const auto [i, j] = e;
67 return 0 <= i and i < height and 0 <= j and j < width;
68 };
69
70 auto get_input_element = [input](IndexPair e) -> char {
71 const auto [i, j] = e;
72 return input[i][j];
73 };
74
75 (((answer += ranges::equal(dirs
76 | views::transform(add_offset)
77 | views::filter(in_bounds)
78 | views::transform(get_input_element), "XMAS"sv))), ...);
79 }, directions);
80 }
81
82 std::println("{}", answer);
83}
84
85void part2(auto input) {
86 uint64_t answer{0};
87 int width(input[0].size());
88 int height(input.size());
89
90 constexpr size_t window_size = 3;
91 for (auto &&v_window : input | views::slide(window_size)) {
92 for (const auto &[r1, r2, r3] : views::zip(v_window[0] | views::slide(window_size),
93 v_window[1] | views::slide(window_size),
94 v_window[2] | views::slide(window_size))) {
95 const std::array<char, 3> diagonals[] = {
96 {r1[0], r2[1], r3[2]},
97 {r3[0], r2[1], r1[2]},
98 };
99
100 answer += ranges::all_of(diagonals, [](const auto &diag) {
101 return ranges::equal(diag, "MAS"sv) or ranges::equal(diag, "SAM"sv);
102 });
103 }
104 }
105 std::println("{}", answer);
106}
107
108int main() {
109 const auto input = parse_input();
110
111#ifndef NO_PART1
112 part1(input);
113#endif
114
115#ifndef NO_PART2
116 part2(input);
117#endif
118 return 0;
119}