advent2021

Advent of Code 2021 Solutions
git clone git://bsandro.tech/advent2021
Log | Files | Refs | README | LICENSE

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 }