advent2023

Advent of Code 2023 solutions
git clone git://bsandro.tech/advent2023
Log | Files | Refs | LICENSE

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 }