puzzle.c (4940B)
1 #define _DEFAULT_SOURCE 2 3 #include <limits.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <errno.h> 7 #include <string.h> 8 #include <strings.h> 9 #include <assert.h> 10 #include <stdbool.h> 11 #include <inttypes.h> 12 13 #define STR_LEN 2048 14 15 typedef struct Node { 16 char name[4]; 17 char left[4]; 18 char right[4]; 19 struct Node *next; 20 } Node; 21 22 Node * new_node(Node *orig) { 23 Node *node = malloc(sizeof(Node)); 24 memset(node, 0, sizeof(Node)); 25 if (orig!=NULL) { 26 strncpy(node->name, orig->name, 4); 27 strncpy(node->left, orig->left, 4); 28 strncpy(node->right, orig->right, 4); 29 } 30 return node; 31 } 32 33 Node * find_node(Node *first, char name[4]) { 34 Node *node = first; 35 while (node!=NULL) { 36 if (strncmp(name, node->name, 3)==0) { 37 return node; 38 } 39 node = node->next; 40 } 41 return NULL; 42 } 43 44 typedef struct Anode { 45 Node *start; 46 uint64_t znode; 47 } Anode; 48 49 typedef struct Item { 50 Node *node; 51 uint64_t steps; 52 struct Item *next; 53 } Item; 54 Item * new_item() { 55 Item *item = malloc(sizeof(Item)); 56 memset(item, 0, sizeof(Item)); 57 return item; 58 } 59 Item * find_item(Item *items, Node *node) { 60 Item *item = items; 61 while (item!=NULL) { 62 if (item->node == node) return item; 63 item = item->next; 64 } 65 return NULL; 66 } 67 void print_items(Item *items) { 68 Item *item = items; 69 while (item!=NULL) { 70 printf("%s[%" PRIu64 "] -> ", item->node->name, item->steps); 71 item = item->next; 72 } 73 printf("\n"); 74 } 75 void free_items(Item *items) { 76 Item *item = items; 77 while (item!=NULL) { 78 Item *n = item->next; 79 free(item); 80 item = n; 81 } 82 } 83 84 bool check_z_nodes(Anode *anodes, size_t len) { 85 for (size_t i=0; i<len; ++i) { 86 if (anodes[i].start->name[2]!='Z') { 87 return false; 88 } 89 } 90 return true; 91 } 92 93 void print_nodes(Node *first) { 94 Node *node = first; 95 while (node!=NULL) { 96 printf("n:%s, l:%s, r:%s\n", node->name, node->left, node->right); 97 node=node->next; 98 } 99 } 100 101 static uint64_t gcd(uint64_t a, uint64_t b) { 102 return b==0 ? a : gcd(b, a%b); 103 } 104 105 static uint64_t lcm(uint64_t a, uint64_t b) { 106 return (a*b)/gcd(a,b); 107 } 108 109 void puzzle(const char *filename, long long *result1, long long *result2) { 110 FILE *infile = fopen(filename, "r"); 111 if (infile == NULL) { 112 fprintf(stderr, "fopen() error: %s\n", strerror(errno)); 113 return; 114 } 115 116 char buf[STR_LEN] = {0}; 117 unsigned int line_num = 0; 118 119 Node *nodes = NULL; 120 Node *nodes_last = NULL; 121 Anode *anodes = NULL; 122 size_t anodes_len = 0; 123 const char *instructions = NULL; 124 125 while (fgets(buf, STR_LEN, infile) != NULL) { 126 // first line is always instructions 127 size_t len = strlen(buf); 128 if (line_num==0) { 129 instructions = strndup(buf, len-1); // removing \n 130 } else if (len>1) { 131 Node *node = new_node(NULL); 132 int cnt = sscanf(buf, "%3s = (%3s, %3s)\n", node->name, node->left, node->right); 133 if (cnt!=3) { 134 free(node); 135 printf("error %d parsing %s\n", cnt, buf); 136 return; 137 } 138 if (nodes==NULL) nodes = node; 139 if (nodes_last!=NULL) nodes_last->next = node; 140 nodes_last = node; 141 142 // starting nodes for p.2 143 if (node->name[2]=='A') { 144 ++anodes_len; 145 anodes = realloc(anodes, anodes_len*sizeof(Anode)); 146 anodes[anodes_len-1].start = node; 147 anodes[anodes_len-1].znode = 0; 148 } 149 } 150 ++line_num; 151 bzero(buf, STR_LEN); 152 } 153 154 uint64_t steps = 0; 155 int il = strlen(instructions); 156 // p.1 157 Node *n = find_node(nodes, "AAA"); 158 while (1) { 159 char instr = instructions[steps % il]; 160 if (instr!='L'&&instr!='R') { 161 printf("illegal instruction %c\n", instr); 162 break; 163 } 164 Node *nn = find_node(nodes, instr=='L'?n->left:n->right); 165 if (nn==NULL) { 166 printf("node %s was not found\n", instr=='L'?n->left:n->right); 167 break; 168 } 169 n=nn; 170 ++steps; 171 if (strcmp(n->name, "ZZZ")==0) { 172 *result1 = steps; 173 break; 174 } 175 if (steps==UINT64_MAX) { 176 printf("steps overflow\n"); 177 break; 178 } 179 } 180 181 // p.2 182 // fill anodes with znode 183 for (size_t i=0; i<anodes_len; ++i) { 184 steps = 0; 185 Node *n = anodes[i].start; 186 //printf("filling anode %s\n", n->name); 187 Item *hist = NULL; 188 Item *hist_last = NULL; 189 Node *z = NULL; 190 Node *w = NULL; 191 while (1) { 192 char instr = instructions[steps%il]; 193 n = find_node(nodes, instr=='L'?n->left:n->right); 194 //printf("checking %s\n", n->name); 195 if (n->name[2]=='Z') { 196 if (z!=NULL&&n!=z) { 197 printf("z nodes are different\n"); 198 } 199 anodes[i].znode = steps+1; 200 z = n; 201 } 202 if (find_item(hist, n)==NULL) { 203 Item *it = new_item(); 204 it->node = n; 205 it->steps = steps; 206 if (hist==NULL) hist=it; 207 if (hist_last!=NULL) hist_last->next=it; 208 hist_last=it; 209 } else if (w==NULL) { 210 //printf("anode %s wrapped up on %s (%" PRIu64 ")\n", anodes[i].start->name, n->name, steps+1); 211 w = n; 212 //free_items(hist); 213 //break; 214 } 215 ++steps; 216 if (z!=NULL) { 217 free_items(hist); 218 break; 219 } 220 if (steps==UINT64_MAX) { 221 break; 222 } 223 } 224 } 225 226 *result2 = anodes[0].znode; 227 for (size_t i=1; i<anodes_len; ++i) { 228 *result2 = lcm(*result2, anodes[i].znode); 229 } 230 231 // mutiny! ignoring feof/ferror. 232 fclose(infile); 233 (void)result2; 234 }