advent2021

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

puzzle.c (7631B)


      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 
     17 struct rule_t {
     18 	char name[3];
     19 	char child;
     20 	struct rule_t *children[2];
     21 	long long score;
     22 };
     23 
     24 struct count_t {
     25 	struct rule_t *section;
     26 	char symbol;
     27 	unsigned long long count;
     28 };
     29 
     30 long long parse_rule(struct array_t *rules, const char *str);
     31 void process_rules(struct array_t *rules);
     32 struct rule_t * get_rule(struct array_t *rules, const char name[3]);
     33 struct array_t make_sections_count(struct array_t *sections);
     34 void add_section_count(struct array_t *counts, struct rule_t *section, unsigned long long count);
     35 void add_symbol_count(struct array_t *counts, char symbol, unsigned long long multiplier);
     36 unsigned long long get_min_count(const struct array_t *counts);
     37 unsigned long long get_max_count(const struct array_t *counts);
     38 struct array_t make_sections(struct array_t *orig_rules, const char *str);
     39 struct array_t iterate_sections_count(struct array_t *rules, struct array_t counts);
     40 unsigned long long get_char_counts(struct array_t counts);
     41 
     42 void puzzle(const char *filename, unsigned long long *result1, unsigned long long *result2) {
     43 	FILE *infile = fopen(filename, "r");
     44 	if (infile == NULL) {
     45 		fprintf(stderr, "fopen() error: %s\n", strerror(errno));
     46 		return;
     47 	}
     48 
     49 	char buf[STR_LEN] = {0};
     50 	unsigned int line_num = 0;
     51 
     52 	bool rules_started = false;
     53 	char *str = NULL;
     54 	struct array_t rules = { .data = NULL };
     55 	array_init(&rules, sizeof(struct rule_t), 10);
     56 	while (fgets(buf, STR_LEN, infile) != NULL) {
     57 		if (strlen(buf) == 1) {
     58 			rules_started = true;
     59 			continue;
     60 		}
     61 		if (rules_started) {
     62 			parse_rule(&rules, buf);
     63 		} else {
     64 			assert(str == NULL);
     65 			size_t len = strlen(buf);
     66 			str = malloc(len);
     67 			bzero(str, len);
     68 			strncpy(str, buf, len-1); // losing last symbol \n
     69 		}
     70 		++line_num;
     71 		bzero(buf, STR_LEN);
     72 	}
     73 
     74 	process_rules(&rules);
     75 	struct array_t sections = make_sections(&rules, str);
     76 	struct array_t sections_count = make_sections_count(&sections);
     77 
     78 	for (int i = 0; i < 40; ++i) {
     79 		struct array_t sections_count1 = iterate_sections_count(&rules, sections_count);
     80 		free(sections_count.data);
     81 		sections_count = sections_count1;
     82 
     83 		if (i == 9) {
     84 			*result1 = get_char_counts(sections_count);
     85 		}
     86 	}
     87 
     88 	*result2 = get_char_counts(sections_count);
     89 
     90 	free(str);
     91 	free(sections.data);
     92 	free(sections_count.data);
     93 	free(rules.data);
     94 	// mutiny! ignoring feof/ferror.
     95 	fclose(infile);
     96 }
     97 
     98 struct array_t iterate_sections_count(struct array_t *rules, struct array_t counts) {
     99 	assert(rules != NULL);
    100 	struct array_t new_counts = { .data = NULL };
    101 	array_init(&new_counts, sizeof(struct count_t), counts.cap);
    102 
    103 	struct count_t *counts_data = counts.data;
    104 	for (size_t i = 0; i < counts.count; ++i) {
    105 		struct rule_t *rule = counts_data[i].section;
    106 		if (counts_data[i].count > 0) {
    107 			add_section_count(&new_counts, rule->children[0], counts_data[i].count);
    108 			add_section_count(&new_counts, rule->children[1], counts_data[i].count);
    109 		}
    110 	}
    111 
    112 	return new_counts;
    113 }
    114 
    115 struct array_t make_sections_count(struct array_t *sections) {
    116 	struct array_t counts = { .data = NULL };
    117 	array_init(&counts, sizeof(struct count_t), 10);
    118 	struct rule_t **data = sections->data;
    119 	for (size_t i = 0; i < sections->count; ++i) {
    120 		add_section_count(&counts, data[i], 1);
    121 	}
    122 
    123 	return counts;
    124 }
    125 
    126 unsigned long long get_char_counts(struct array_t counts) {
    127 	struct array_t char_counts = { .data = NULL };
    128 	array_init(&char_counts, sizeof(struct count_t), 10);
    129 
    130 	struct count_t *counts_data = counts.data;
    131 	for (size_t i = 0; i < counts.count; ++i) {
    132 		if (i == 0) {
    133 			add_symbol_count(&char_counts, counts_data[i].section->name[0], 1);
    134 		}
    135 		add_symbol_count(&char_counts, counts_data[i].section->name[1], counts_data[i].count);
    136 	}
    137 
    138 	unsigned long long count_min = get_min_count(&char_counts);
    139 	unsigned long long count_max = get_max_count(&char_counts);
    140 
    141 	return count_max - count_min;
    142 }
    143 
    144 void add_section_count(struct array_t *counts, struct rule_t *section, unsigned long long count) {
    145 	assert(counts != NULL);
    146 	assert(section != NULL);
    147 	struct count_t *data = counts->data;
    148 	for (size_t i = 0; i < counts->count; ++i) {
    149 		if (data[i].section == section) {
    150 			data[i].count += count;
    151 			return;
    152 		}
    153 	}
    154 
    155 	if (counts->count >= counts->cap) {
    156 		array_expand(counts);
    157 	}
    158 	data = counts->data;
    159 	data[counts->count++] = (struct count_t){ .section = section, .count = count };
    160 }
    161 
    162 void add_symbol_count(struct array_t *counts, char symbol, unsigned long long multiplier) {
    163 	assert(counts != NULL);
    164 	struct count_t *data = counts->data;
    165 	for (size_t i = 0; i < counts->count; ++i) {
    166 		if (data[i].symbol == symbol) {
    167 			data[i].count += multiplier;
    168 			return;
    169 		}
    170 	}
    171 
    172 	if (counts->count >= counts->cap) {
    173 		array_expand(counts);
    174 	}
    175 	data = counts->data;
    176 	data[counts->count++] = (struct count_t){ .symbol = symbol, .count = multiplier };
    177 }
    178 
    179 
    180 unsigned long long get_max_count(const struct array_t *counts) {
    181 	assert(counts != NULL);
    182 	struct count_t *data = counts->data;
    183 	unsigned long long max = 0;
    184 	for (size_t i = 0; i < counts->count; ++i) {
    185 		max = data[i].count > max ? data[i].count : max;
    186 	}
    187 	return max;
    188 }
    189 
    190 unsigned long long get_min_count(const struct array_t *counts) {
    191 	assert(counts != NULL);
    192 	struct count_t *data = counts->data;
    193 	unsigned long long min = ULLONG_MAX;
    194 	for (size_t i = 0; i < counts->count; ++i) {
    195 		min = data[i].count < min ? data[i].count : min;
    196 	}
    197 	return min;
    198 }
    199 
    200 long long parse_rule(struct array_t *rules, const char *str) {
    201 	assert(rules != NULL);
    202 	assert(str != NULL);
    203 	char *tmp = strndup(str, STR_LEN);
    204 	assert(tmp != NULL);
    205 	char *token = NULL;
    206 
    207 	int n = 0;
    208 	struct rule_t rule = {0};
    209 	while ((token = strsep(&tmp, " ->\n")) != NULL) {
    210 		if (*token != '\0') {
    211 			assert(n < 2);
    212 			if (n == 0) {
    213 				assert(strlen(token) == 2);
    214 				strncpy(rule.name, token, 3);
    215 				rule.score = rule.name[0] * 1000 + rule.name[1];
    216 			} else {
    217 				assert(strlen(token) == 1);
    218 				rule.child = token[0];
    219 			}
    220 			n++;
    221 		}
    222 	}
    223 
    224 	if (rules->count >= rules->cap) {
    225 		array_expand(rules);
    226 	}
    227 
    228 	struct rule_t *data = rules->data;
    229 	data[rules->count++] = rule;
    230 	free(tmp);
    231 	return rule.score;
    232 }
    233 
    234 void process_rules(struct array_t *rules) {
    235 	assert(rules != NULL);
    236 	struct rule_t *data = rules->data;
    237 	for (size_t i = 0; i < rules->count; ++i) {
    238 		char name1[3] = {0};
    239 		char name2[3] = {0};
    240 		name1[0] = data[i].name[0];
    241 		name1[1] = data[i].child;
    242 		name2[0] = data[i].child;
    243 		name2[1] = data[i].name[1];
    244 		struct rule_t *child1 = get_rule(rules, name1);
    245 		struct rule_t *child2 = get_rule(rules, name2);
    246 		assert(child1 != NULL);
    247 		assert(child2 != NULL);
    248 		data[i].children[0] = child1;
    249 		data[i].children[1] = child2;
    250 	}
    251 }
    252 
    253 struct rule_t * get_rule(struct array_t *rules, const char name[3]) {
    254 	assert(rules != NULL);
    255 	struct rule_t *data = rules->data;
    256 	for (size_t i = 0; i < rules->count; ++i) {
    257 		if (data[i].name[0] == name[0] && data[i].name[1] == name[1]) {
    258 			return &data[i];
    259 		}
    260 	}
    261 	return NULL;
    262 }
    263 
    264 struct array_t make_sections(struct array_t *rules, const char *str) {
    265 	assert(str != NULL);
    266 	assert(rules != NULL);
    267 	size_t len = strlen(str);
    268 	struct array_t sections = { .data = NULL };
    269 	array_init(&sections, sizeof(void *), 10);
    270 	for (size_t i = 0; i < len - 1; ++i) {
    271 		char name[3] = { str[i], str[i+1], 0};
    272 		if (sections.count >= sections.cap) {
    273 			array_expand(&sections);
    274 		}
    275 		struct rule_t **data = sections.data;
    276 		struct rule_t *rule = get_rule(rules, name);
    277 		assert(rule != NULL);
    278 		data[sections.count++] = rule;
    279 	}
    280 
    281 	return sections;
    282 }