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