puzzle.c (6555B)
1 #define _DEFAULT_SOURCE 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <errno.h> 6 #include <string.h> 7 #include <strings.h> 8 #include <assert.h> 9 #include <ctype.h> 10 #include <limits.h> 11 #include <stdbool.h> 12 13 #include "util.h" 14 15 #define STR_LEN 32 16 #define NAME_LEN 8 17 #define NAME_START "start" 18 #define NAME_END "end" 19 20 struct node_t { 21 int id; 22 char name[NAME_LEN]; 23 struct array_t neighbors; 24 bool is_start; 25 bool is_end; 26 bool is_small; 27 }; 28 29 struct visit_t { 30 int node_id; 31 int count; 32 }; 33 34 bool is_str_lowercase(const char *str); 35 struct node_t * node_get(struct array_t *nodes, int id); 36 int node_find(struct array_t *nodes, const char *name); 37 int node_add(struct array_t *nodes, const char *name); 38 void add_path(struct array_t *nodes, const char *str); 39 void neighbor_add(struct node_t *a, int id); 40 int traverse(struct array_t *nodes, struct node_t *node, struct array_t *visited, bool p2); 41 struct visit_t * visited_get(struct array_t *visited, int id); 42 void visited_clean(struct array_t *visited); 43 44 void puzzle(const char *filename, long long *result1, long long *result2) { 45 FILE *infile = fopen(filename, "r"); 46 if (infile == NULL) { 47 fprintf(stderr, "fopen() error: %s\n", strerror(errno)); 48 return; 49 } 50 51 char buf[STR_LEN] = {0}; 52 unsigned int line_num = 0; 53 54 *result1 = 0; 55 *result2 = 0; 56 57 struct array_t nodes = { .data = NULL }; 58 // @todo handle reallocates 59 array_init(&nodes, sizeof(struct node_t), 10); 60 61 while (fgets(buf, STR_LEN, infile) != NULL) { 62 add_path(&nodes, buf); 63 ++line_num; 64 bzero(buf, STR_LEN); 65 } 66 67 int start_id = node_find(&nodes, "start"); 68 int end_id = node_find(&nodes, "end"); 69 assert(start_id != -1 && end_id != -1); 70 struct node_t *start = node_get(&nodes, start_id); 71 struct node_t *end = node_get(&nodes, end_id); 72 assert(start != NULL && end != NULL); 73 74 for (size_t i = 0; i < start->neighbors.count; ++i) { 75 int *data = start->neighbors.data; 76 struct node_t *node = node_get(&nodes, data[i]); 77 assert(node != NULL); 78 struct array_t visited = { .data = NULL }; 79 array_init(&visited, sizeof(void *), 10); 80 81 *result1 += traverse(&nodes, node, &visited, false); 82 visited_clean(&visited); 83 *result2 += traverse(&nodes, node, &visited, true); 84 visited_clean(&visited); 85 86 free(visited.data); 87 } 88 89 free(nodes.data); 90 // mutiny! ignoring feof/ferror. 91 fclose(infile); 92 } 93 94 int traverse(struct array_t *nodes, struct node_t *node, struct array_t *visited, bool part2) { 95 assert(node != NULL); 96 97 if (node->is_end) { 98 return 1; 99 } 100 101 if (node->is_start) { 102 return 0; 103 } 104 105 struct visit_t *visit = visited_get(visited, node->id); 106 assert(visit != NULL); 107 if (node->is_small) { 108 if (part2 && visit->count > 0) { 109 struct visit_t **data = visited->data; 110 for (size_t i = 0; i < visited->count; ++i) { 111 struct visit_t *vvisit = data[i]; 112 if (vvisit->count > 1 && vvisit->node_id != node->id) { 113 struct node_t *vnode = node_get(nodes, vvisit->node_id); 114 assert(vnode != NULL); 115 if (vnode->is_small) { 116 return 0; 117 } 118 } 119 } 120 } else if (!part2 && visit->count > 0) { 121 return 0; 122 } 123 124 if (visit->count > 1) { 125 return 0; 126 } 127 128 visit->count++; 129 } 130 131 int count = 0; 132 for (size_t i = 0; i < node->neighbors.count; ++i) { 133 int *data = node->neighbors.data; 134 struct node_t *neighbor = node_get(nodes, data[i]); 135 assert(neighbor != NULL); 136 int ncount = traverse(nodes, neighbor, visited, part2); 137 count += ncount; 138 } 139 140 if (node->is_small) { 141 visit->count--; 142 } 143 144 return count; 145 } 146 147 struct visit_t * visited_get(struct array_t *visited, int id) { 148 assert(visited != NULL); 149 struct visit_t **data = visited->data; 150 assert(data != NULL); 151 for (size_t i = 0; i < visited->count; ++i) { 152 if (data[i]->node_id == id) { 153 return data[i]; 154 } 155 } 156 157 if (visited->count >= visited->cap) { 158 array_expand(visited); 159 } 160 data = visited->data; // could be reallocated 161 struct visit_t *visit = malloc(sizeof(struct visit_t)); 162 assert(visit != NULL); 163 visit->node_id = id; 164 visit->count = 0; 165 data[visited->count++] = visit; 166 return visit; 167 } 168 169 void visited_clean(struct array_t *visited) { 170 assert(visited != NULL); 171 struct visit_t **data = visited->data; 172 assert(data != NULL); 173 for (size_t i = 0; i < visited->count; ++i) { 174 free(data[i]); 175 } 176 visited->count = 0; 177 bzero(visited->data, sizeof(void *) * visited->cap); 178 } 179 180 void add_path(struct array_t *nodes, const char *str) { 181 char names[2][NAME_LEN]; 182 int i = 0; 183 char *tmp = strndup(str, STR_LEN); 184 assert(tmp != NULL); 185 char *token = NULL; 186 while ((token = strsep(&tmp, "-\n")) != NULL) { 187 if (*token != '\0') { 188 assert(i < 2); 189 assert(strlen(token) < NAME_LEN); 190 strncpy(names[i], token, NAME_LEN-1); 191 ++i; 192 } 193 } 194 assert(i == 2); 195 int id_a = node_add(nodes, names[0]); 196 int id_b = node_add(nodes, names[1]); 197 struct node_t *a = node_get(nodes, id_a); 198 struct node_t *b = node_get(nodes, id_b); 199 assert(a != NULL && b != NULL); 200 if (!b->is_start) { 201 neighbor_add(a, b->id); 202 } 203 if (!a->is_start) { 204 neighbor_add(b, a->id); 205 } 206 } 207 208 void neighbor_add(struct node_t *a, int id) { 209 if (a->neighbors.count >= a->neighbors.cap) { 210 array_expand(&a->neighbors); 211 } 212 int *data = a->neighbors.data; 213 data[a->neighbors.count++] = id; 214 } 215 216 int node_add(struct array_t *nodes, const char *name) { 217 int node_id = node_find(nodes, name); 218 if (node_id != -1) { 219 return node_id; 220 } 221 222 if (nodes->count >= nodes->cap) { 223 array_expand(nodes); 224 } 225 static int last_id = 1; 226 struct node_t *data = (struct node_t *)nodes->data; 227 struct node_t *node = &data[nodes->count++]; 228 node->id = last_id++; 229 strncpy(node->name, name, NAME_LEN-1); 230 if (strncmp(node->name, NAME_START, NAME_LEN) == 0) { 231 node->is_start = true; 232 } else if (strncmp(node->name, NAME_END, NAME_LEN) == 0) { 233 node->is_end = true; 234 } else if (is_str_lowercase(node->name)) { 235 node->is_small = true; 236 } 237 array_init(&node->neighbors, sizeof(int), 5); 238 return node->id; 239 } 240 241 int node_find(struct array_t *nodes, const char *name) { 242 struct node_t *data = (struct node_t *)nodes->data; 243 for (size_t i = 0; i < nodes->count; ++i) { 244 struct node_t *node = &data[i]; 245 if (strncmp(node->name, name, NAME_LEN) == 0) { 246 return node->id; 247 } 248 } 249 return -1; 250 } 251 252 struct node_t * node_get(struct array_t *nodes, int id) { 253 struct node_t *data = (struct node_t *)nodes->data; 254 for (size_t i = 0; i < nodes->count; ++i) { 255 struct node_t *node = &data[i]; 256 if (node->id == id) { 257 return node; 258 } 259 } 260 return NULL; 261 } 262 263 bool is_str_lowercase(const char *str) { 264 assert(str != NULL); 265 size_t len = strlen(str); 266 for (size_t i = 0; i < len; ++i) { 267 if (!islower(str[i])) { 268 return false; 269 } 270 } 271 return true; 272 }