aoc

My solutions for the Advent of Code
git clone https://git.tronto.net/aoc
Download | Log | Files | Refs | README

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 }