Advent of Code 2025, done in C++
1#include <cstddef>
2#include <format>
3#include <fstream>
4#include <map>
5#include <print>
6#include <string>
7#include <vector>
8
9#include "common/getinputpath.h"
10
11void render(std::vector<std::string>& data) {
12 if (data.size() < 2) {
13 return;
14 }
15
16 for (size_t i = 1; i < data.size(); ++i) {
17 auto& previous = data[i - 1];
18 auto& current = data[i];
19
20 for (size_t ch = 0; ch < current.size(); ++ch) {
21 char previous_ch = previous[ch];
22 char current_ch = current[ch];
23 if (previous_ch != 'S' && previous_ch != '|') {
24 continue;
25 }
26
27 // render
28 if (current_ch == '^') {
29 // split
30 if (ch > 0) {
31 current[ch - 1] = '|';
32 }
33 if (ch < current.size()) {
34 current[ch + 1] = '|';
35 }
36 } else {
37 current[ch] = '|';
38 }
39 }
40 }
41}
42
43long long count_beams_split(std::vector<std::string>& data) {
44 if (data.size() < 2) {
45 return 0;
46 }
47 auto out = 0;
48
49 for (size_t i = 1; i < data.size(); ++i) {
50 auto& previous = data[i - 1];
51 auto& current = data[i];
52
53 for (size_t ch = 0; ch < current.size(); ++ch) {
54 if (previous[ch] == '|' && current[ch] == '^') {
55 out += 1;
56 }
57 }
58 }
59
60 return out;
61}
62
63struct Position {
64 size_t x = 0;
65 size_t y = 0;
66
67 bool operator==(const Position& other) const {
68 return x == other.x && y == other.y;
69 }
70
71 bool operator<(const Position& other) const {
72 return y < other.y || (y == other.y && x < other.x);
73 }
74};
75
76long long count_possible_paths(std::vector<std::string>& data,
77 Position position,
78 std::map<Position, long long>& cache) {
79 // TODO
80 if (position.y == data.size()) { // if we reached bottom
81 return 1;
82 }
83 long long result = 0;
84 if (data[position.y][position.x] == '|' ||
85 data[position.y][position.x] == 'S') {
86 Position new_pos{.x = position.x, .y = position.y + 1};
87 result = count_possible_paths(data, new_pos, cache);
88
89 } else if (data[position.y][position.x] == '^') {
90 Position left = Position{.x = position.x - 1, .y = position.y};
91 Position right = Position{.x = position.x + 1, .y = position.y};
92
93 // a weird workaround to satisfy some static checks. maps must
94 // be sortable.
95 if (cache.contains(position)) {
96 result = cache[position];
97 } else {
98 result = count_possible_paths(data, left, cache) +
99 count_possible_paths(data, right, cache);
100
101 cache[position] = result;
102 }
103 }
104
105 return result;
106}
107
108long long count_possible_paths(std::vector<std::string>& data,
109 std::map<Position, long long>& cache) {
110 Position start{
111 .x = data[0].find_first_of('S'),
112 .y = 0,
113 };
114 return count_possible_paths(data, start, cache);
115}
116
117// runtime on Ryzen 5 5600G: 0.003s
118int main() {
119 auto data_path = get_input_path(DATA_FOLDER);
120 std::ifstream data_ifstream(data_path);
121 if (!data_ifstream.is_open()) {
122 std::println("Cannot open the file at {}", data_path.string());
123 return 1;
124 }
125
126 std::vector<std::string> data{};
127 for (std::string t; std::getline(data_ifstream, t);) {
128 data.push_back(t);
129 }
130
131 render(data); // this has some great potential for TUI visualization
132
133 long long password_part_1 = count_beams_split(data);
134
135 std::map<Position, long long> cache{};
136 long long password_part_2 =
137 count_possible_paths(data, cache); // this one too
138
139 std::println("Eureka! {} / {}", password_part_1, password_part_2);
140
141 return 0;
142}