Advent of Code 2025, done in C++
1#include "common/getinputpath.h"
2#include <algorithm>
3#include <filesystem>
4#include <format>
5#include <fstream>
6#include <functional>
7#include <iterator>
8#include <print>
9#include <ranges>
10#include <regex>
11#include <string>
12#include <vector>
13
14#ifndef DATA_FOLDER
15#error \
16 "Meson must have defined the DATA_FOLDER somewhere in build scripts. Go check it."
17#endif // !DATA_FOLDER
18
19const std::filesystem::path input_path = get_input_path(DATA_FOLDER);
20constexpr const char delimiter = ',';
21
22struct NumberProfile {
23 long number;
24 bool part1_valid;
25 bool part2_valid;
26};
27
28[[nodiscard]] NumberProfile is_valid(long id) {
29 std::string id_string = std::to_string(id);
30
31 bool part1_valid = true;
32 bool part2_valid = true;
33
34 // repeats 2 times
35 if (id_string.length() % 2 == 0 && part1_valid) {
36 bool invalid = id_string.substr(0, id_string.length() / 2) ==
37 id_string.substr(id_string.length() / 2);
38 part1_valid = !invalid;
39 }
40
41 // repeats more than 2 times
42 if (id_string.length() >= 2 && part2_valid) {
43 auto t = id_string + id_string;
44 part2_valid =
45 t.substr(1, t.length() - 2).find(id_string) == std::string::npos;
46 }
47
48 return NumberProfile{
49 .number = id,
50 .part1_valid = part1_valid,
51 .part2_valid = part2_valid,
52 };
53}
54
55// Ryzen 5 5600G runtime: 1s494ms
56int main() {
57 std::ifstream input(input_path);
58 if (!input.is_open()) {
59 std::println("Something wrong happened. Can't open file {}",
60 input_path.string());
61 return 1;
62 }
63
64 unsigned long long password_part_1 = 0;
65 unsigned long long password_part_2 = 0;
66
67 // loop hell incoming
68 for (std::string t = ""; std::getline(input, t, delimiter);) {
69 std::println("Checking range {}", t);
70 auto left = t.substr(0, t.find('-'));
71 auto right = t.substr(t.find('-') + 1);
72
73 auto ids = std::vector<NumberProfile>(std::stol(right) - std::stol(left));
74
75 {
76 auto ids_num = std::vector<long>(std::stol(right) - std::stol(left));
77 std::ranges::iota(ids_num.begin(), ids_num.end(), std::stol(left));
78 std::ranges::transform(ids_num, std::back_inserter(ids),
79 [](auto i) { return is_valid(i); });
80 }
81
82 auto invalid_ids_part1 =
83 ids | std::ranges::views::filter([](NumberProfile i) {
84 return !i.part1_valid;
85 }) |
86 std::ranges::views::transform([](NumberProfile i) { return i.number; });
87
88 auto invalid_ids_part2 =
89 ids | std::ranges::views::filter([](NumberProfile i) {
90 return !i.part2_valid;
91 }) |
92 std::ranges::views::transform([](NumberProfile i) { return i.number; });
93
94 auto sum_part1 =
95 std::ranges::fold_left(invalid_ids_part1, 0ULL, std::plus<>{});
96 password_part_1 += sum_part1;
97
98 auto sum_part2 =
99 std::ranges::fold_left(invalid_ids_part2, 0ULL, std::plus<>{});
100 password_part_2 += sum_part2;
101 }
102
103 std::println("Eureka! {} / {}", password_part_1, password_part_2);
104
105 return 0;
106}