aboutsummaryrefslogtreecommitdiff
path: root/2024/src/day24.cpp
diff options
context:
space:
mode:
Diffstat (limited to '2024/src/day24.cpp')
-rw-r--r--2024/src/day24.cpp234
1 files changed, 231 insertions, 3 deletions
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}