#include #include #include #include #include #define CHARS_MAX 64 #define MAX_GATES_PER_WIRE 16 #define MAX_WIRES 16*1024 #define MAX_GATES 8*1024 #define c2i(a) ((a) >= 'a' ? ((a) - 'a') : ((a) - '0' + 26)) int cmp(const void* a, const void* b) { const char **pa = (const char**)a; const char **pb = (const char**)b; if ((*pa)[0] != (*pb)[0]) { return (*pa)[0] - (*pb)[0]; } if ((*pa)[1] != (*pb)[1]) { return (*pa)[1] - (*pb)[1]; } return (*pa)[2] - (*pb)[2]; } typedef struct node { struct node *children[36]; struct wire *value; char key; uint8_t is_leaf; } node_t; node_t* create_node() { node_t *node = calloc(1, sizeof(node[0])); return node; } node_t* search(node_t* root, char* word) { node_t* node = root; for (int i = 0; i < 3; i++) { if (node->children[c2i(word[i])] == NULL) { return NULL; } node = node->children[c2i(word[i])]; } return node; } void insert(node_t* root, char* word, struct wire *value) { node_t* node = root; for (int i = 0; i < 3; i++) { if (node->children[c2i(word[i])] == NULL) { node->children[c2i(word[i])] = create_node(); } node = node->children[c2i(word[i])]; } node->value = value; node->is_leaf = 1; } void free_node(node_t* node) { for (int i = 0; i < 36; i++) { if (node->children[i] != NULL) { free_node(node->children[i]); } } free(node->value); free(node); } typedef struct wire { char name[3]; int signal; struct gate *gates[MAX_GATES_PER_WIRE]; int output_gate_count; struct gate *generator; } wire_t; typedef enum { AND, OR, XOR } gate_type_t; typedef struct gate { wire_t *inputs[2]; wire_t *output; gate_type_t type; } gate_t; void process_wire(wire_t *wire); uint64_t get_output(node_t *wires, char c); void reset(node_t *wires); void set_input1(node_t *wires, uint64_t value); void set_input2(node_t *wires, uint64_t value); void backtrack(node_t *wires, wire_t **list, int *list_count, wire_t* wire); int is_correct(node_t *wires, int i, wire_t **initial_wires); void get_activated_gates(node_t *wires, gate_t **list, int *list_count); int main() { char *p, *buf, c; buf = calloc(CHARS_MAX, sizeof(char)); p = buf; wire_t **initial_wires = calloc(MAX_WIRES, sizeof(wire_t*)); int initial_wire_count = 0; int reading_initial_wires = 1; char (*preparsed_gates)[10] = calloc(MAX_GATES, sizeof(preparsed_gates[0])); int gate_count = 0; while ((c = getchar()) != EOF) { *p++ = c; if (c != '\n') { continue; } if (reading_initial_wires) { if (buf[0] == '\n') { reading_initial_wires = 0; memset(buf, 0, CHARS_MAX); p = buf; continue; } initial_wires[initial_wire_count] = calloc(1, sizeof(wire_t)); initial_wires[initial_wire_count]->name[0] = buf[0]; initial_wires[initial_wire_count]->name[1] = buf[1]; initial_wires[initial_wire_count]->name[2] = buf[2]; initial_wires[initial_wire_count++]->signal = buf[5] == '1' ? 1 : 0; } else { preparsed_gates[gate_count][0] = buf[0]; preparsed_gates[gate_count][1] = buf[1]; preparsed_gates[gate_count][2] = buf[2]; preparsed_gates[gate_count][3] = buf[4] == 'A' ? AND : (buf[4] == 'O' ? OR : XOR); p = buf; while (*p++ != ' '); while (*p++ != ' '); preparsed_gates[gate_count][4] = p[0]; preparsed_gates[gate_count][5] = p[1]; preparsed_gates[gate_count][6] = p[2]; while (*p++ != ' '); while (*p++ != ' '); preparsed_gates[gate_count][7] = p[0]; preparsed_gates[gate_count][8] = p[1]; preparsed_gates[gate_count][9] = p[2]; gate_count++; } memset(buf, 0, CHARS_MAX); p = buf; } free(buf); // Create trie of wires node_t *wires = create_node(); // Insert initial wires for (int i = 0; i < initial_wire_count; i++) { insert(wires, initial_wires[i]->name, initial_wires[i]); } // Go through gates and extract previously unknown wires for (int i = 0; i < gate_count; i++) { char* wire_name = &preparsed_gates[i][0]; node_t *wire = search(wires, wire_name); if (wire == NULL || wire->is_leaf == 0) { wire_t *value = calloc(1, sizeof(wire_t)); memcpy(value->name, wire_name, 3); value->signal = -1; insert(wires, value->name, value); } wire_name = &preparsed_gates[i][4]; wire = search(wires, wire_name); if (wire == NULL || wire->is_leaf == 0) { wire_t *value = calloc(1, sizeof(wire_t)); memcpy(value->name, wire_name, 3); value->signal = -1; insert(wires, value->name, value); } wire_name = &preparsed_gates[i][7]; wire = search(wires, wire_name); if (wire == NULL || wire->is_leaf == 0) { wire_t *value = calloc(1, sizeof(wire_t)); memcpy(value->name, wire_name, 3); value->signal = -1; insert(wires, value->name, value); } } // Now link everything together gate_t *gates = calloc(MAX_GATES, sizeof(gates[0])); for (int i = 0; i < gate_count; i++) { // Link gate inputs/outputs gates[i].type = preparsed_gates[i][3]; wire_t *wire1 = search(wires, &preparsed_gates[i][0])->value; gates[i].inputs[0] = wire1; wire_t *wire2 = search(wires, &preparsed_gates[i][4])->value; gates[i].inputs[1] = wire2; wire_t *wire3 = search(wires, &preparsed_gates[i][7])->value; gates[i].output = wire3; // Link wires to gates wire1->gates[wire1->output_gate_count++] = &gates[i]; wire2->gates[wire2->output_gate_count++] = &gates[i]; wire3->generator = &gates[i]; } free(preparsed_gates); for (int i = 0; i < initial_wire_count; i++) { process_wire(initial_wires[i]); } uint64_t output = get_output(wires, 'z'); printf("%lu\n", output); char **part2 = calloc(8, sizeof(part2[0])); int part2_c = 0; wire_t **relevant_gates = calloc(MAX_GATES, sizeof(wire_t*)); relevant_gates[0] = search(wires, "z00")->value; int relevant_gate_count = 1; int pc = 1; for (int i = 1; i < 64; i++) { char buf[4]; sprintf(buf, "z%02d", i); node_t *generator_node = search(wires, buf); if (generator_node) { backtrack(wires, relevant_gates, &relevant_gate_count, generator_node->value); if (!is_correct(wires, i, initial_wires)) { gate_t **activated = calloc(MAX_GATES, sizeof(gate_t*)); gate_t **switchable = calloc(MAX_GATES, sizeof(gate_t*)); int activated_count = 0; int switchable_count = 0; get_activated_gates(wires, activated, &activated_count); for (int j = 0; j < activated_count; j++) { int found = 0; for (int jj = 0; jj < pc; jj++) { if (relevant_gates[jj] == activated[j]->output) { found = 1; break; } } if (!found) { switchable[switchable_count++] = activated[j]; } } // Try to switch each switchable int br = 0; for (int s1 = 0; s1 < switchable_count; s1++) { for (int s2 = s1 + 1; s2 < switchable_count; s2++) { wire_t *tmp = switchable[s2]->output; switchable[s2]->output = switchable[s1]->output; switchable[s1]->output = tmp; // Check switched version if (is_correct(wires, i, initial_wires)) { part2[part2_c++] = switchable[s1]->output->name; part2[part2_c++] = switchable[s2]->output->name; br = 1; break; } switchable[s1]->output = switchable[s2]->output; switchable[s2]->output = tmp; } if (br) { break; } } free(activated); free(switchable); } pc = relevant_gate_count; } } qsort(part2, part2_c, sizeof(char*), cmp); for (int i = 0; i < part2_c; i++) { printf("%c%c%c", part2[i][0], part2[i][1], part2[i][2]); if (i != part2_c - 1) { printf(","); } } printf("\n"); free(initial_wires); free_node(wires); free(gates); free(relevant_gates); free(part2); } int eval_gate(gate_t gate) { switch (gate.type) { case AND: return gate.inputs[0]->signal & gate.inputs[1]->signal; case OR: return gate.inputs[0]->signal | gate.inputs[1]->signal; case XOR: return gate.inputs[0]->signal ^ gate.inputs[1]->signal; } return -1; } void process_wire(wire_t *wire) { // Try each of its outputs for (int i = 0; i < wire->output_gate_count; i++) { // Check if gate has both of it's outputs set if (wire->gates[i]->inputs[0]->signal != -1 && wire->gates[i]->inputs[1]->signal != -1) { wire->gates[i]->output->signal = eval_gate(*wire->gates[i]); process_wire(wire->gates[i]->output); } } } void set_input1(node_t *wires, uint64_t value) { for (int i = 0; i < INT_MAX; i++) { char buf[4]; sprintf(buf, "x%02d", i); node_t *node = search(wires, buf); if (node == NULL) { break; } node->value->signal = (value >> i) & 1; } } void set_input2(node_t *wires, uint64_t value) { for (int i = 0; i < INT_MAX; i++) { char buf[4]; sprintf(buf, "y%02d", i); node_t *node = search(wires, buf); if (node == NULL) { break; } node->value->signal = (value >> i) & 1; } } uint64_t get_output(node_t *wires, char c) { uint64_t result = 0; for (int i = 0; i < INT_MAX; i++) { char buf[4]; sprintf(buf, "%c%02d", c, i); node_t *node = search(wires, buf); if (node == NULL) { break; } result += (uint64_t)node->value->signal << i; } return result; } void reset(node_t *wires) { if (wires == NULL) { return; } for (int i = 0; i < 36; i++) { reset(wires->children[i]); } if (wires->value != NULL) { wires->value->signal = -1; } } void backtrack(node_t *wires, wire_t **list, int *list_count, wire_t* wire) { if (wire == NULL) { return; } int found = 0; for (int i = 0; i < *list_count; i++) { if (wire == list[i]) { found = 1; break; } } if (found) { return; } list[*list_count] = wire; (*list_count)++; if (wire->generator) { backtrack(wires, list, list_count, wire->generator->inputs[0]); backtrack(wires, list, list_count, wire->generator->inputs[1]); } } void get_activated_gates(node_t *wires, gate_t **list, int *list_count) { if (wires == NULL) { return; } if (wires->value != NULL && wires->value->generator != NULL && wires->value->signal != -1) { int found = 0; for (int i = 0; i < *list_count; i++) { if (wires->value->generator == list[i]) { found = 1; break; } } if (!found) { list[*list_count] = wires->value->generator; (*list_count)++; } } // Iterate each wire for (int i = 0; i < 36; i++) { get_activated_gates(wires->children[i], list, list_count); } } int is_correct(node_t *wires, int i, wire_t **initial_wires) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { for (int jj = 0; jj < 2; jj++) { for (int kk = 0; kk < 2; kk++) { reset(wires); set_input1(wires, ((uint64_t)j << i) + ((uint64_t)k << (i-1))); set_input2(wires, ((uint64_t)jj << i) + ((uint64_t)kk << (i-1))); uint64_t e = ((uint64_t)j << i) + ((uint64_t)k << (i-1)) + ((uint64_t)jj << i) + ((uint64_t)kk << (i-1)); char buf[4]; for (int ii = 0; ii <= i; ii++) { if (i == 45) { continue; } sprintf(buf, "x%02d", ii); wire_t *x = search(wires, buf)->value; process_wire(x); sprintf(buf, "y%02d", ii); wire_t *y = search(wires, buf)->value; process_wire(y); } for (int ii = 0; ii < i-1; ii++) { sprintf(buf, "z%02d", ii); wire_t *z = search(wires, buf)->value; if (z->signal != 0) { return 0; } } sprintf(buf, "z%02d", i-1); wire_t *z = search(wires, buf)->value; if (z->signal != (k ^ kk)) { return 0; } sprintf(buf, "z%02d", i); z = search(wires, buf)->value; if (z->signal != ((j ^ jj) ^ (k & kk))) { return 0; } } } } } return 1; }