aboutsummaryrefslogtreecommitdiff
path: root/2024/src
diff options
context:
space:
mode:
authorThomas Schmucker <ts@its1.de>2025-01-02 14:08:05 +0100
committerThomas Schmucker <ts@its1.de>2025-01-02 14:08:05 +0100
commit9098c9d0d9922f54b3ad9414151604402b8e9c44 (patch)
treed30acf9a4b755ab449c274112a696ddec4334b40 /2024/src
parent870b007c75a3d3c37e89aa4074c2574ac6b5dd81 (diff)
downloadadvent-of-code-9098c9d0d9922f54b3ad9414151604402b8e9c44.tar.gz
advent-of-code-9098c9d0d9922f54b3ad9414151604402b8e9c44.tar.bz2
advent-of-code-9098c9d0d9922f54b3ad9414151604402b8e9c44.zip
aoc 2024, day 24, part 2 and day 25
Diffstat (limited to '2024/src')
-rw-r--r--2024/src/day24-alt.cpp197
-rw-r--r--2024/src/day24.cpp234
-rw-r--r--2024/src/day25.cpp87
3 files changed, 515 insertions, 3 deletions
diff --git a/2024/src/day24-alt.cpp b/2024/src/day24-alt.cpp
new file mode 100644
index 0000000..df6d333
--- /dev/null
+++ b/2024/src/day24-alt.cpp
@@ -0,0 +1,197 @@
1#include <array>
2#include <fstream>
3#include <iostream>
4#include <map>
5#include <set>
6#include <sstream>
7#include <string>
8#include <vector>
9using namespace std;
10
11vector<string>
12split(string_view line, string_view delimiter)
13{
14 size_t pos_start = 0;
15 size_t pos_end = 0;
16
17 vector<string> res;
18
19 while ( (pos_end = line.find(delimiter, pos_start)) != std::string::npos ) {
20 auto token = line.substr(pos_start, pos_end - pos_start);
21 pos_start = pos_end + delimiter.length();
22
23 res.emplace_back(token);
24 }
25
26 res.emplace_back(line.substr(pos_start));
27 return res;
28}
29
30template<typename Container>
31string
32join(const Container& container, const string& delimiter)
33{
34 ostringstream oss;
35 auto it = container.begin();
36 if ( it != container.end() ) {
37 oss << *it;
38 ++it;
39 }
40 while ( it != container.end() ) {
41 oss << delimiter << *it;
42 ++it;
43 }
44 return oss.str();
45}
46
47tuple<map<string, unsigned long>, map<string, tuple<string, string, string>>>
48read_file(string_view filename)
49{
50 fstream input{ filename };
51 const auto parts = split(string{ istreambuf_iterator<char>{ input }, {} }, "\n\n");
52
53 map<string, unsigned long> inputs;
54
55 // register => { reg1 operator reg2 }
56 map<string, tuple<string, string, string>> deps;
57
58 stringstream spart1{ parts[0] };
59 for ( string line; getline(spart1, line); ) {
60 const auto foo = split(line, ": ");
61 inputs[foo[0]] = stoul(foo[1]);
62 }
63
64 stringstream spart2{ parts[1] };
65 for ( string line; getline(spart2, line); ) {
66 const auto foo = split(line, " ");
67 deps[foo[4]] = { foo[0], foo[1], foo[2] };
68 }
69
70 return { inputs, deps };
71}
72
73void
74part1(const tuple<map<string, unsigned long>, map<string, tuple<string, string, string>>>& data)
75{
76 auto inputs = get<0>(data);
77 const auto& deps = get<1>(data);
78
79 function<unsigned long(const string&)> eval = [&](const string& input) -> unsigned long {
80 if ( inputs.contains(input) ) {
81 return inputs.at(input);
82 }
83
84 const auto& [lhs, op, rhs] = deps.at(input);
85
86 unsigned long value = 0;
87
88 if ( op == "XOR" ) {
89 value = eval(lhs) ^ eval(rhs);
90 }
91 else if ( op == "AND" ) {
92 value = eval(lhs) & eval(rhs);
93 }
94 else if ( op == "OR" ) {
95 value = eval(lhs) | eval(rhs);
96 }
97 else {
98 cerr << "Das darf nicht passieren! Input: " << input << endl;
99 }
100
101 inputs[input] = value;
102
103 return value;
104 };
105
106 unsigned long value = 0;
107 for ( const auto& [first, second]: deps ) {
108 if ( first.starts_with("z") ) {
109 if ( eval(first) != 0 ) {
110 auto bit = stoul(first.substr(1));
111 value |= 1UL << bit;
112 }
113 }
114 }
115 cout << value << endl;
116}
117
118void
119debug(const tuple<map<string, unsigned long>, map<string, tuple<string, string, string>>>& data)
120{
121 const auto& deps = get<1>(data);
122
123 function<void(const string&, size_t)> eval = [&](const string& input, size_t depth) {
124 if ( !deps.contains(input) ) {
125 return;
126 }
127
128 if ( depth == 3 ) {
129 return;
130 }
131
132 const auto& [lhs, op, rhs] = deps.at(input);
133
134 const string indent(depth * 2, ' ');
135
136 cout << indent << op << " (" << input << ")\n"
137 << indent << " - " << lhs << "\n"
138 << indent << " - " << rhs << endl;
139
140 eval(lhs, depth + 1);
141 eval(rhs, depth + 1);
142 };
143
144 eval("z02", 0);
145 eval("z16", 0);
146 eval("z17", 0);
147}
148
149void
150part2(const tuple<map<string, unsigned long>, map<string, tuple<string, string, string>>>& data)
151{
152 auto deps = get<1>(data);
153
154 set<string> wrong;
155 for ( const auto& [res, formula]: deps ) {
156 const auto& [op1, op, op2] = formula;
157
158 if ( res.starts_with('z') && op != "XOR" && res != "z45" ) {
159 wrong.insert(res);
160 }
161
162 static const set<char> starts{ 'x', 'y', 'z' };
163
164 if ( op == "XOR" && !starts.contains(res[0]) && !starts.contains(op1[0]) && !starts.contains(op2[0]) ) {
165 wrong.insert(res);
166 }
167
168 if ( op == "AND" && op1 != "x00" && op2 != "x00" ) {
169 for ( const auto& [subres, subformula]: deps ) {
170 const auto& [subop1, subop, subop2] = subformula;
171 if ( (res == subop1 || res == subop2) and subop != "OR" ) {
172 wrong.insert(res);
173 }
174 }
175 }
176
177 if ( op == "XOR" ) {
178 for ( const auto& [subres, subformula]: deps ) {
179 const auto& [subop1, subop, subop2] = subformula;
180 if ( (res == subop1 || res == subop2) and subop == "OR" ) {
181 wrong.insert(res);
182 }
183 }
184 }
185 }
186
187 cout << join(wrong, ",") << endl;
188}
189
190int
191main()
192{
193 const auto data = read_file("data/day24.txt");
194 // debug(data);
195 part1(data);
196 part2(data);
197}
diff --git a/2024/src/day24.cpp b/2024/src/day24.cpp
index bf439c7..3f252a1 100644
--- a/2024/src/day24.cpp
+++ b/2024/src/day24.cpp
@@ -1,3 +1,4 @@
1#include <array>
1#include <fstream> 2#include <fstream>
2#include <iostream> 3#include <iostream>
3#include <map> 4#include <map>
@@ -7,6 +8,8 @@
7#include <vector> 8#include <vector>
8using namespace std; 9using namespace std;
9 10
11static const auto MAX_BITS = 45UL;
12
10vector<string> 13vector<string>
11split(string_view line, string_view delimiter) 14split(string_view line, string_view delimiter)
12{ 15{
@@ -26,6 +29,23 @@ split(string_view line, string_view delimiter)
26 return res; 29 return res;
27} 30}
28 31
32template<typename Container>
33string
34join(const Container& container, const string& delimiter)
35{
36 ostringstream oss;
37 auto iter = container.begin();
38 if ( iter != container.end() ) {
39 oss << *iter;
40 ++iter;
41 }
42 while ( iter != container.end() ) {
43 oss << delimiter << *iter;
44 ++iter;
45 }
46 return oss.str();
47}
48
29tuple<map<string, unsigned long>, map<string, tuple<string, string, string>>> 49tuple<map<string, unsigned long>, map<string, tuple<string, string, string>>>
30read_file(string_view filename) 50read_file(string_view filename)
31{ 51{
@@ -76,9 +96,6 @@ part1(const tuple<map<string, unsigned long>, map<string, tuple<string, string,
76 else if ( op == "OR" ) { 96 else if ( op == "OR" ) {
77 value = eval(lhs) | eval(rhs); 97 value = eval(lhs) | eval(rhs);
78 } 98 }
79 else {
80 cerr << "Das darf nicht passieren! Input: " << input << endl;
81 }
82 99
83 inputs[input] = value; 100 inputs[input] = value;
84 101
@@ -97,9 +114,220 @@ part1(const tuple<map<string, unsigned long>, map<string, tuple<string, string,
97 cout << value << endl; 114 cout << value << endl;
98} 115}
99 116
117string
118make_wire(char chr, unsigned long num)
119{
120 static const size_t MAX_LEN = 10;
121 array<char, MAX_LEN> tmp{};
122 snprintf(tmp.data(), tmp.size(), "%c%02lu", chr, num); // NOLINT
123 return tmp.data();
124}
125
126class RippleCarryAdder {
127public:
128 explicit RippleCarryAdder(const tuple<map<string, unsigned long>, map<string, tuple<string, string, string>>>& data)
129 : inputs_(get<0>(data))
130 , deps_(get<1>(data))
131 {
132 }
133
134 RippleCarryAdder(const map<string, tuple<string, string, string>>& deps, unsigned long x_value, unsigned long y_value)
135 : deps_(deps)
136 {
137 build_inputs(x_value, y_value);
138 }
139
140 void swap_wire(const string& lhs, const string& rhs)
141 {
142 swap(deps_.at(lhs), deps_.at(rhs));
143 }
144
145 bool eval_z(unsigned long x_value, unsigned long y_value)
146 {
147 build_inputs(x_value, y_value);
148
149 bool overflow = false;
150 for ( auto bit = 0UL; bit != MAX_BITS + 1; ++bit ) {
151 static const int MAX_RECURSION_DEPTH = 99;
152 eval(make_wire('z', bit), MAX_RECURSION_DEPTH, overflow);
153 }
154 return !overflow;
155 }
156
157 [[nodiscard]] vector<string> test(unsigned long x_value, unsigned long y_value) const
158 {
159 const auto z_value = x_value + y_value;
160
161 vector<string> failed_bits;
162 auto bit = 0UL;
163 for ( ; bit != MAX_BITS + 1; ++bit ) {
164 const auto z_bit = (z_value & (1UL << bit)) != 0 ? 1UL : 0UL;
165 const auto wire = make_wire('z', bit);
166 if ( inputs_.at(wire) != z_bit ) {
167 failed_bits.push_back(wire);
168 }
169 }
170 return failed_bits;
171 };
172
173 void print(unsigned long x_value, unsigned long y_value, ostream& out) const
174 {
175 const auto z_value = x_value + y_value;
176
177 out << " x: " << bitset<MAX_BITS>{ x_value } << '\n'
178 << "+ y: " << bitset<MAX_BITS>{ y_value } << '\n'
179 << "= z: " << bitset<MAX_BITS + 1>{ z_value } << '\n';
180
181 out << "===: ";
182 for ( auto bit = MAX_BITS + 1; bit-- > 0; ) {
183 const auto wire = make_wire('z', bit);
184 if ( inputs_.contains(wire) ) {
185 out << inputs_.at(wire);
186 }
187 else {
188 out << '_';
189 }
190 }
191 out << endl;
192 };
193
194 void collect(const string& input, set<string>& possible_fails) const
195 {
196 collect(input, 2, possible_fails);
197 }
198
199private:
200 void build_inputs(unsigned long x_value, unsigned long y_value)
201 {
202 inputs_.clear();
203
204 for ( auto bit = 0UL; bit != MAX_BITS; ++bit ) {
205 inputs_.emplace(make_wire('x', bit), (x_value & (1UL << bit)) != 0 ? 1 : 0);
206 inputs_.emplace(make_wire('y', bit), (y_value & (1UL << bit)) != 0 ? 1 : 0);
207 }
208 }
209
210 unsigned long eval(const string& input, size_t depth, bool& overflow)
211 {
212 if ( depth == 0 ) {
213 overflow = true;
214 return 0;
215 }
216
217 if ( inputs_.contains(input) ) {
218 return inputs_.at(input);
219 }
220
221 const auto& [lhs, op, rhs] = deps_.at(input);
222
223 unsigned long value = 0;
224
225 if ( op == "XOR" ) {
226 value = eval(lhs, depth - 1, overflow) ^ eval(rhs, depth - 1, overflow);
227 }
228 else if ( op == "AND" ) {
229 value = eval(lhs, depth - 1, overflow) & eval(rhs, depth - 1, overflow);
230 }
231 else if ( op == "OR" ) {
232 value = eval(lhs, depth - 1, overflow) | eval(rhs, depth - 1, overflow);
233 }
234
235 inputs_[input] = value;
236
237 return value;
238 }
239
240 void collect(const string& input, size_t depth, set<string>& possible_fails) const
241 {
242 if ( !deps_.contains(input) ) {
243 return;
244 }
245
246 possible_fails.insert(input);
247
248 if ( depth == 0 ) {
249 return;
250 }
251
252 const auto& [lhs, op, rhs] = deps_.at(input);
253
254 collect(lhs, depth - 1, possible_fails);
255 collect(rhs, depth - 1, possible_fails);
256 };
257
258 map<string, unsigned long> inputs_;
259
260 // register => { reg1 operator reg2 }
261 map<string, tuple<string, string, string>> deps_;
262};
263
264void
265part2(const tuple<map<string, unsigned long>, map<string, tuple<string, string, string>>>& data)
266{
267 RippleCarryAdder rca(data);
268
269 set<string> solution;
270
271 for ( auto bit = 0UL; bit != MAX_BITS; ++bit ) {
272 const auto x_value = 1UL << bit; // (1UL << (bit+1)) - 1;
273 const auto y_value = 0UL << bit;
274
275 if ( !rca.eval_z(x_value, y_value) ) {
276 return;
277 }
278
279 const auto failed_bits = rca.test(x_value, y_value);
280 if ( !failed_bits.empty() ) {
281 set<string> candidates;
282 for ( const auto& wire: failed_bits ) {
283 rca.collect(wire, candidates);
284 }
285
286 const vector<string> foo{ candidates.begin(), candidates.end() };
287
288 for ( auto i = foo.begin(); i != foo.end(); ++i ) {
289 for ( auto j = i + 1; j != foo.end(); ++j ) {
290 auto do_test = [&](unsigned long lhs, unsigned long rhs) -> bool {
291 RippleCarryAdder rca_tester{ data };
292
293 rca_tester.swap_wire(*i, *j);
294
295 if ( !rca_tester.eval_z(lhs, rhs) ) {
296 return false;
297 }
298 return rca_tester.test(lhs, rhs).empty();
299 };
300
301 if ( do_test(x_value, y_value) ) {
302 const auto x_value2 = 7UL << (bit - 1);
303 const auto y_value2 = 3UL << (bit - 1);
304
305 if ( do_test(x_value2, y_value2) ) {
306 const auto x_value3 = 1UL << bit;
307 const auto y_value3 = 1UL << bit;
308
309 if ( do_test(x_value3, y_value3) ) {
310 const auto x_value4 = 5UL << (bit - 1);
311 const auto y_value4 = 3UL << (bit - 1);
312
313 if ( do_test(x_value4, y_value4) ) {
314 solution.insert(*i);
315 solution.insert(*j);
316 }
317 }
318 }
319 }
320 }
321 }
322 }
323 }
324 cout << join(solution, ",") << endl;
325}
326
100int 327int
101main() 328main()
102{ 329{
103 const auto data = read_file("data/day24.txt"); 330 const auto data = read_file("data/day24.txt");
104 part1(data); 331 part1(data);
332 part2(data);
105} 333}
diff --git a/2024/src/day25.cpp b/2024/src/day25.cpp
new file mode 100644
index 0000000..6d43ddf
--- /dev/null
+++ b/2024/src/day25.cpp
@@ -0,0 +1,87 @@
1#include <fstream>
2#include <iostream>
3#include <sstream>
4#include <string>
5#include <tuple>
6#include <vector>
7using namespace std;
8
9using pattern_type = vector<string>;
10
11vector<string>
12split(string_view line, string_view delimiter)
13{
14 size_t pos_start = 0;
15 size_t pos_end = 0;
16
17 vector<string> res;
18
19 while ( (pos_end = line.find(delimiter, pos_start)) != std::string::npos ) {
20 auto token = line.substr(pos_start, pos_end - pos_start);
21 pos_start = pos_end + delimiter.length();
22
23 res.emplace_back(token);
24 }
25
26 res.emplace_back(line.substr(pos_start));
27 return res;
28}
29
30tuple<vector<pattern_type>, vector<pattern_type>>
31read_file(string_view filename)
32{
33 fstream input{ filename };
34 const auto patterns{ split(string{ istreambuf_iterator<char>{ input }, {} }, "\n\n") };
35
36 vector<pattern_type> keys;
37 vector<pattern_type> locks;
38
39 for ( const auto& pattern: patterns ) {
40 const pattern_type& lines = split(pattern, "\n");
41
42 if ( lines[0] == "#####" && lines[6] == "....." ) {
43 locks.push_back(lines);
44 }
45 else if ( lines[0] == "....." && lines[6] == "#####" ) {
46 keys.push_back(lines);
47 }
48 }
49
50 return { keys, locks };
51}
52
53bool
54fits(const pattern_type& key, const pattern_type& lock)
55{
56 for ( size_t row = 0; row != key.size(); ++row ) {
57 for ( size_t col = 0; col != key[row].size(); ++col ) {
58 if ( key[row][col] == '#' && lock[row][col] == '#' ) {
59 return false;
60 }
61 }
62 }
63 return true;
64}
65
66void
67part1(const tuple<vector<pattern_type>, vector<pattern_type>>& data)
68{
69 const auto [keys, locks] = data;
70
71 long count = 0;
72 for ( const auto& key: keys ) {
73 for ( const auto& lock: locks ) {
74 if ( fits(key, lock) ) {
75 ++count;
76 }
77 }
78 }
79 cout << count << endl;
80}
81
82int
83main()
84{
85 const auto data = read_file("data/day25.txt");
86 part1(data);
87}