Advent of Code 2025, done in C++
1#include "common/getinputpath.h"
2#include <algorithm>
3#include <filesystem>
4#include <fstream>
5#include <print>
6#include <ranges>
7#include <sstream>
8#include <string>
9#include <vector>
10
11class Range {
12public:
13 Range(long long from, long long to, bool inclusive)
14 : from(from), to(to), inclusive(inclusive) {}
15
16 long long from;
17 long long to;
18 bool inclusive;
19
20 bool contains(long long value) {
21 auto real_from = std::min(from, to);
22 auto real_to = std::max(from, to);
23
24 if (inclusive) {
25 return value >= real_from && value <= real_to;
26 } else {
27 return value >= real_from && value < real_to;
28 }
29 }
30
31 long long size() {
32 auto real_from = std::min(from, to);
33 auto real_to = std::max(from, to);
34 if (inclusive) {
35 real_to += 1;
36 }
37
38 // TODO(fix): may be buggy with from is negative and to is positive
39 return real_to - real_from;
40 }
41};
42
43// runtime on Ryzen 5 5600G: 0.003s
44int main() {
45 auto path = get_input_path(DATA_FOLDER);
46 auto data_size = std::filesystem::file_size(path);
47 std::string data = "";
48 data.resize(data_size);
49 std::ifstream data_ifstream(path);
50 if (!data_ifstream.is_open()) {
51 std::println("Unable to read from the solution file at {}", path.string());
52 return 1;
53 }
54 data_ifstream.read(data.data(), data_size);
55
56 auto split_idx = data.find("\n\n");
57 auto data_ranges = data.substr(0, split_idx);
58 auto data_ids = data.substr(split_idx + 2);
59
60 std::vector<Range> ranges{};
61 {
62 auto stream = std::stringstream(data_ranges);
63 for (std::string t; std::getline(stream, t);) {
64 // parsing
65 auto split_idx = t.find("-");
66 auto left = std::stoll(t.substr(0, split_idx));
67 auto right = std::stoll(t.substr(split_idx + 1));
68
69 // RAM is expensive these days
70 // burning the CPU in attempts to occupy as less RAM at peak as possible
71 // fuck OpenAI
72 bool skip = false;
73 for (auto [i, range] : std::ranges::views::enumerate(ranges)) {
74 bool left_in_range = left >= range.from && left <= range.to;
75 bool right_in_range = right >= range.from && right <= range.to;
76 if (left_in_range && right_in_range) {
77 // my hand just itches to use goto
78 skip = true;
79 break;
80 }
81 if (left_in_range) {
82 ranges[i] = Range(range.from, right, true);
83 skip = true;
84 break;
85 }
86 if (right_in_range) {
87 ranges[i] = Range(left, range.to, true);
88 skip = true;
89 break;
90 }
91 if (left <= range.from && right >= range.to) {
92 ranges[i] = Range(left, right, true);
93 skip = true;
94 break;
95 }
96 }
97 if (skip) {
98 continue;
99 }
100
101 auto new_range = Range(left, right, true);
102 ranges.push_back(new_range);
103 }
104 }
105
106 std::sort(ranges.begin(), ranges.end(),
107 [](Range one, Range other) { return one.from < other.from; });
108
109 std::vector<Range> merged_ranges = std::ranges::fold_left(
110 ranges, std::vector<Range>(), [](std::vector<Range> acc, Range range) {
111 if (acc.empty() || acc.back().to < range.from) {
112 acc.push_back(range);
113 } else {
114 acc.back().to = std::max(range.to, acc.back().to);
115 }
116
117 return acc;
118 });
119
120 std::vector<long long> ids{};
121 {
122 auto stream = std::stringstream(data_ids);
123 for (std::string t; std::getline(stream, t);) {
124 auto id = std::stoll(t);
125 ids.push_back(id);
126 }
127 }
128
129 long long password_part_1 = 0;
130
131 std::ranges::for_each(ids, [&merged_ranges, &password_part_1](long long id) {
132 bool is_fresh = false; // guilty until proven innocent!
133 for (auto range : merged_ranges) {
134 is_fresh = range.contains(id);
135 if (is_fresh) {
136 break;
137 }
138 }
139 if (is_fresh) {
140 password_part_1 += 1;
141 }
142 });
143
144 long long password_part_2 = 0;
145
146 for (auto range : merged_ranges) {
147 password_part_2 += range.size();
148
149 std::println("{} - {}", range.from, range.to);
150 }
151
152 std::println("Eureka! {} / {}", password_part_1, password_part_2);
153
154 return 0;
155}