day24b.cpp (7138B)
1 /* 2 3 I don't know what addition circuits look like, but they should have some 4 kind of regular structure. I manually inspected how the first few digits 5 behave to get an idea. 6 7 With some more manual inspection I noticed that the "temporary result" 8 gates that are results of a XOR operation always appear as terms in 9 exactly one XOR and one AND operation; the same thing happens for 10 OR-results, while AND-results appear as terms only in one OR operation. 11 12 This excludes z-gates, which are the result of a XOR operation (except 13 for z45, which is the result of an OR operation) and do not appear as 14 terms in any other operation. And of course "errors" do not follow this 15 pattern either. 16 17 So what I do is going through all gates and see if their result 18 satisfies the conditions above. This gives 8 candidates for the error, 19 but unfortunately they were not the correct answer (I tried submitting 20 this list 3 times). 21 22 I thought about it a bit longer and I realized that one of the entries 23 in my list is rfg, which appears as the result of x00 & y00. I thought 24 that since this was the first digit, it could be an edge case and thus 25 correctly behaving differently from the others. This leaves me with 26 7 candidates only, so I have to find another one. 27 28 One of the gates I had to swap (z23) was the result of an OR operation, 29 but should have been the result o a XOR. But any XOR-result could be 30 swapped with an OR-result without breaking the conditions I guessed at the 31 beginning. So I tried them all: for every XOR-gate I swapped the results 32 with my other 7 candidates in every way possible according to my rules, 33 and checked which of these re-arrangements gave the correct answer for 34 the example input. 35 36 I found two candidates (kdf and nsr). I could have checked more inputs 37 to see which of the two worked, or I could just try both of them. Of 38 course, I choose the second option, and kdf worked. 39 40 Did I get the gold star? Yes. Do I know how I was supposed to solve this 41 problem? Not a clue. But hey, I solved it without hints, a win is a win. 42 43 */ 44 45 #include <algorithm> 46 #include <cstdint> 47 #include <iostream> 48 #include <map> 49 #include <queue> 50 #include <ranges> 51 #include <set> 52 #include <sstream> 53 #include <string> 54 #include <string_view> 55 #include <vector> 56 using namespace std; 57 58 enum class Op { AND, OR, XOR }; 59 60 class Gate { 61 public: 62 int x, y, z; 63 Op op; 64 65 bool ready(const int8_t *state) const { 66 return state[x] != -1 && state[y] != -1; 67 } 68 69 bool operate(int8_t *state) const { /* Returns true if the operation flips a bit */ 70 auto oldstate = state[z]; 71 switch (op) { 72 case Op::AND: 73 state[z] = state[x] & state[y]; 74 break; 75 case Op::OR: 76 state[z] = state[x] | state[y]; 77 break; 78 case Op::XOR: 79 state[z] = state[x] ^ state[y]; 80 break; 81 } 82 return oldstate != state[z]; 83 } 84 }; 85 86 int8_t state[36*36*36]; 87 vector<Gate> gates; 88 89 int d(char x) { return x >= 'a' && x <= 'z' ? x-'a' : 26+x-'0'; } 90 char h(int i) { return i >= 26 ? i-26+'0' : i+'a'; } 91 int index(char a, char b, char c) { return d(c) + 36 * (d(b) + 36 * d(a)); } 92 string name(int i) { 93 string r = " "; 94 r[0] = h(i / (36*36)); 95 r[1] = h((i/ 36) % 36); 96 r[2] = h(i % 36); 97 return r; 98 } 99 100 int64_t digit(int i, char c) { 101 return index(c, (i/10)+'0', (i%10)+'0'); 102 } 103 104 int64_t getnum(char c, int m) { 105 int64_t result = 0; 106 for (int64_t i = 0, j = 1; i < m; i++, j *= 2) 107 result += (int64_t)state[digit(i, c)] * j; 108 return result; 109 } 110 111 Op make_op(char c) { 112 switch (c) { 113 case '&': return Op::AND; 114 case '|': return Op::OR; 115 default: return Op::XOR; 116 } 117 } 118 119 char opname(Op op) { 120 switch (op) { 121 case Op::AND: return '&'; 122 case Op::OR: return '|'; 123 default: return '^'; 124 } 125 } 126 127 void printop(Gate g, const vector<Op>& a) { 128 cout << name(g.z) << " is " << name(g.x) << " " << opname(g.op) 129 << " " << name(g.y) << " but appears in "; 130 for (auto op : a) 131 cout << opname(op) << " "; 132 cout << endl; 133 } 134 135 int gg(const string& rr) { 136 for (unsigned i = 0; i < gates.size(); i++) 137 if (name(gates[i].z) == rr) 138 return i; 139 return -1; 140 } 141 142 void swapgates(vector<Gate>& local_gates, vector<int> ixsa, 143 vector<int> iasx, int iosx, int gswap) { 144 } 145 146 void run(int8_t *state, const vector<Gate>& gates) { 147 for (bool flag = true; flag; ) { 148 flag = false; 149 for (auto g : gates) 150 if (g.ready(state)) 151 flag = flag || g.operate(state); 152 } 153 int64_t x = getnum('x', 45); 154 int64_t y = getnum('y', 45); 155 int64_t z = getnum('z', 46); 156 if (x+y == z) cout << "Correct result! " << z << endl; 157 } 158 159 int main() { 160 fill(state, state + 36*36*36, -1); 161 string line; 162 163 while (getline(cin, line) && line != "") 164 state[index(line[0], line[1], line[2])] = line[5]-'0'; 165 166 while (getline(cin, line)) 167 gates.push_back(Gate { 168 .x = index(line[0], line[1], line[2]), 169 .y = index(line[6], line[7], line[8]), 170 .z = index(line[13], line[14], line[15]), 171 .op = make_op(line[4]) 172 }); 173 174 #if 0 175 /* Print error candidates */ 176 vector<string> bad; 177 for (auto g : gates) { 178 if (name(g.z)[0] == 'z') { 179 if ((name(g.z) == "z45" && g.op != Op::OR) || 180 (name(g.z) != "z45" && g.op != Op::XOR)) { 181 printop(g, vector<Op>()); 182 bad.push_back(name(g.z)); 183 } 184 continue; 185 } 186 vector<Op> appears_in; 187 for (auto f : gates) 188 if (g.z == f.x || g.z == f.y) 189 appears_in.push_back(f.op); 190 if (g.op == Op::AND) { 191 if (appears_in.size() != 1 || appears_in[0] != Op::OR) { 192 printop(g, appears_in); 193 bad.push_back(name(g.z)); 194 } 195 } else { 196 if (appears_in.size() != 2 || 197 find(appears_in.begin(), appears_in.end(), Op::AND) == appears_in.end() || 198 find(appears_in.begin(), appears_in.end(), Op::XOR) == appears_in.end()) { 199 printop(g, appears_in); 200 bad.push_back(name(g.z)); 201 } 202 } 203 } 204 205 sort(bad.begin(), bad.end()); 206 207 for (auto b : bad) 208 cout << b << ","; 209 cout << endl; 210 #endif 211 212 vector<string> is_xor_shouldbe_and = { "ckj", "rpp", "dbp" }; 213 vector<string> is_and_shouldbe_xor = { "fdv", "z15", "z39" }; 214 string is_or_shouldbe_xor = "z23"; 215 216 vector<int> ixsa = { gg("ckj"), gg("rpp"), gg("dbp") }; 217 vector<int> iasx = { gg("fdv"), gg("z15"), gg("z39") }; 218 int iosx = gg("z23"); 219 220 vector<vector<pair<int, int>>> swaps = { 221 {{ixsa[0],iasx[0]}, {ixsa[1],iasx[1]}, {ixsa[2],iasx[2]}}, 222 {{ixsa[0],iasx[0]}, {ixsa[1],iasx[2]}, {ixsa[2],iasx[1]}}, 223 {{ixsa[0],iasx[1]}, {ixsa[1],iasx[0]}, {ixsa[2],iasx[2]}}, 224 {{ixsa[0],iasx[1]}, {ixsa[1],iasx[2]}, {ixsa[2],iasx[0]}}, 225 {{ixsa[0],iasx[2]}, {ixsa[1],iasx[1]}, {ixsa[2],iasx[0]}}, 226 {{ixsa[0],iasx[2]}, {ixsa[1],iasx[0]}, {ixsa[2],iasx[1]}} 227 }; 228 229 for (unsigned gswap = 0; gswap < gates.size(); gswap++) { 230 if (find(ixsa.begin(), ixsa.end(), gswap) != ixsa.end() || 231 find(iasx.begin(), iasx.end(), gswap) != iasx.end() || 232 (int)gswap == iosx || gates[gswap].op != Op::XOR) 233 continue; 234 cout << "Trying gswap " << name(gates[gswap].z) << endl; 235 for (auto ss : swaps) { 236 auto local_state = state; 237 auto local_gates = gates; 238 swap(local_gates[ss[0].first].z, local_gates[ss[0].second].z); 239 swap(local_gates[ss[1].first].z, local_gates[ss[1].second].z); 240 swap(local_gates[ss[2].first].z, local_gates[ss[2].second].z); 241 swap(local_gates[iosx].z, local_gates[gswap].z); 242 run(local_state, local_gates); 243 } 244 } 245 246 return 0; 247 }