aoc2023

advent of code 2023
Log | Files | Refs | Feed | README

day3.c (5827B)


      1 /* AOC 2023: Day 3 - Gear Ratios
      2  *
      3  * Suppose you have the following visual representation
      4  * of some schematic:
      5  *
      6  * 467..114.3
      7  * 35*.......
      8  * ......633.
      9  * ......#...
     10  * 617*......
     11  * .....+.58.
     12  * ..592.....
     13  * ......755.
     14  * .....*....
     15  * $664.598..
     16  *
     17  * Task 1: Find the sum of all numbers adjacent to a symbol (everything
     18  * that is not a '.', '\n', or digit) including diagonally.
     19  * Test Output: 4361
     20  *
     21  * Task 2: If exactly two numbers are next to a '*' they are a gear.
     22  * Its ratio is the product of the two numbers. Find the sum of all
     23  * gear ratios.
     24  * Test Output: 467835
     25  */
     26 #include <assert.h>
     27 #include <fcntl.h>
     28 #include <stdarg.h>
     29 #include <stdint.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <sys/mman.h>
     33 #include <unistd.h>
     34 
     35 typedef uint64_t u64;
     36 typedef uint32_t u32;
     37 
     38 typedef struct { void *data; u32 idx, cap; } Vector;
     39 typedef struct { u32 r, c; } Index;
     40 typedef struct { Index s, e; } Pair;
     41 
     42 typedef struct {
     43 	int chr;
     44 	char *data;
     45 	u32 dlen;
     46 	u32 pos;
     47 	Index idx;
     48 } Scanner;
     49 
     50 static u32 line_length;
     51 static Vector numpairs;
     52 static Vector syms;
     53 
     54 static void
     55 die(char *fmt, ...)
     56 {
     57 	va_list ap;
     58 	va_start(ap, fmt);
     59 	vfprintf(stderr, fmt, ap);
     60 	va_end(ap);
     61 	exit(1);
     62 }
     63 
     64 static void
     65 resize_vector(Vector *v, size_t elemsize)
     66 {
     67 	if (v->idx + 1 < v->cap)
     68 		return;
     69 
     70 	v->cap += 10000;
     71 	v->data = realloc(v->data, v->cap * elemsize);
     72 
     73 	if (!v->data)
     74 		die("realloc\n");
     75 }
     76 
     77 static void
     78 inc(Scanner *s)
     79 {
     80 	if (s->chr == '\n') {
     81 		assert(line_length == 0 || line_length == s->idx.c + 1);
     82 		line_length = s->idx.c + 1;
     83 		s->idx.r++;
     84 		s->idx.c = 0;
     85 	} else {
     86 		s->idx.c++;
     87 	}
     88 	s->chr = s->data[++s->pos];
     89 }
     90 
     91 static void
     92 dec(Scanner *s)
     93 {
     94 	if (s->pos > 0 && s->data[s->pos - 1] == '\n') {
     95 		s->idx.r--;
     96 		s->idx.c = line_length - 1;
     97 	} else {
     98 		s->idx.c--;
     99 	}
    100 	s->chr = s->data[--s->pos];
    101 }
    102 
    103 static void
    104 number(Scanner *s, Vector *v)
    105 {
    106 	resize_vector(v, sizeof(Pair));
    107 	Pair *p = &((Pair *)v->data)[v->idx++];
    108 	p->s = s->idx;
    109 	for (inc(s); s->pos < s->dlen; inc(s))
    110 		if (s->chr < '0' || s->chr > '9')
    111 			break;
    112 	dec(s);
    113 	p->e = s->idx;
    114 }
    115 
    116 static void
    117 sym(Scanner *s, Vector *v)
    118 {
    119 	resize_vector(v, sizeof(Index));
    120 	((Index *)v->data)[v->idx++] = s->idx;
    121 }
    122 
    123 static void
    124 scan(Scanner *s)
    125 {
    126 	for (; s->pos < s->dlen; inc(s)) {
    127 		if (s->chr >= '0' && s->chr <= '9')
    128 			number(s, &numpairs);
    129 		else if (s->chr != '.' && s->chr != '\n')
    130 			sym(s, &syms);
    131 	}
    132 }
    133 
    134 static u64
    135 pair_to_num(Pair *n, char *data)
    136 {
    137 	assert(n->s.r == n->e.r);
    138 	u64 ret = 0;
    139 	u32 len = n->e.c - n->s.c;
    140 	u32 roff = n->s.r * line_length;
    141 	for (u32 i = 0; i <= len; i++) {
    142 		u32 idx = roff + n->s.c + i;
    143 		int t = data[idx] & 0xFF;
    144 		assert(t <= '9' && t >= '0');
    145 		t -= '0';
    146 		for (u32 j = len - i; j > 0; j--)
    147 			t *= 10;
    148 		ret += t;
    149 	}
    150 	return ret;
    151 }
    152 
    153 static void
    154 dump_syms(Vector *v)
    155 {
    156 	for (u64 i = 0; i < v->idx; i++) {
    157 		Index s = ((Index *)v->data)[i];
    158 		printf("(%u, %u)\n", s.r, s.c);
    159 	}
    160 }
    161 
    162 static void
    163 dump_pairs(Vector *v, char *data)
    164 {
    165 	for (u64 i = 0; i < v->idx; i++) {
    166 		Pair *p = &((Pair *)v->data)[i];
    167 		printf("(%u, %u), (%u, %u) -> %zu\n",
    168 		       p->s.r, p->s.c, p->e.r, p->e.c, pair_to_num(p, data));
    169 	}
    170 }
    171 
    172 #define MAX(A, B)               ((A) > (B) ? (A) : (B))
    173 #define MIN(A, B)               ((A) < (B) ? (A) : (B))
    174 static int
    175 intersects(Pair *p, Index *i)
    176 {
    177 	int min = MAX(0, (p->s.c - 1));
    178 	int max = MIN(line_length, p->e.c + 1);
    179 	if (min < 0) min = 0;
    180 	if (min <= i->c && i->c <= max)
    181 		return 1;
    182 	return 0;
    183 }
    184 
    185 static int
    186 validate_pair(Pair *p, Vector *v)
    187 {
    188 	Index *si;
    189 	int r1 = p->s.r - 1, r2 = r1 + 1, r3 = r2 + 1;
    190 	int sr = r1 >= 0? r1 : r2;
    191 
    192 	u32 idx;
    193 	for (idx = 0; idx < v->idx; idx++) {
    194 		si = &((Index *)v->data)[idx];
    195 		if (si->r >= sr)
    196 			break;
    197 	}
    198 	for (; si->r <= r3 && idx < v->idx; idx++) {
    199 		si = &((Index *)v->data)[idx];
    200 		if (si->r <= r3 && intersects(p, si))
    201 			return 1;
    202 	}
    203 	return 0;
    204 }
    205 
    206 static u64
    207 sum_pairs(char *data, Vector *np, Vector *s)
    208 {
    209 	u64 ret = 0;
    210 	for (u32 i = 0; i < np->idx; i++) {
    211 		Pair *p = &((Pair *)np->data)[i];
    212 		if (validate_pair(p, s))
    213 			ret += pair_to_num(p, data);
    214 	}
    215 	return ret;
    216 }
    217 
    218 static void
    219 get_stars(Vector *st, Vector *sy, char *data)
    220 {
    221 	for (u32 i = 0; i < sy->idx; i++) {
    222 		Index si = ((Index *)sy->data)[i];
    223 		u32 roff = si.r * line_length;
    224 		int t = data[roff + si.c] & 0xFF;
    225 		if (t != '*')
    226 			continue;
    227 		resize_vector(st, sizeof(Index));
    228 		((Index *)st->data)[st->idx++] = si;
    229 	}
    230 }
    231 
    232 static u64
    233 validate_gear(Index *s, Vector *np, char *data)
    234 {
    235 	u64 ret = 0;
    236 	u32 count = 0, idx;
    237 	Pair *p;
    238 
    239 	int r1 = s->r - 1, r2 = r1 + 1, r3 = r2 + 1;
    240 	int sr = r1 >= 0? r1 : r2;
    241 
    242 	/* skip invalid rows */
    243 	for (idx = 0; idx < np->idx; idx++) {
    244 		p = &((Pair *)np->data)[idx];
    245 		if (p->s.r >= sr)
    246 			break;
    247 	}
    248 
    249 	for (; idx < np->idx; idx++) {
    250 		p = &((Pair *)np->data)[idx];
    251 		if (p->s.r > r3)
    252 			break;
    253 		if (!intersects(p, s))
    254 			continue;
    255 		if (ret == 0)
    256 			ret += pair_to_num(p, data);
    257 		else
    258 			ret *= pair_to_num(p, data);
    259 		count++;
    260 		if (count > 2)
    261 			return 0;
    262 	}
    263 	if (count != 2)
    264 		return 0;
    265 	return ret;
    266 }
    267 
    268 static u64
    269 gear_ratio(char *data, Vector *np, Vector *s)
    270 {
    271 	Vector stars = {0};
    272 	u64 ret = 0;
    273 	get_stars(&stars, s, data);
    274 	for (u32 i = 0; i < stars.idx; i++) {
    275 		Index *si = &((Index *)stars.data)[i];
    276 		ret += validate_gear(si, np, data);
    277 	}
    278 	free(stars.data);
    279 	return ret;
    280 }
    281 
    282 int
    283 main(int argc, char *argv[])
    284 {
    285 	int fd;
    286 	Scanner s = {0};
    287 
    288 	if (argc < 2)
    289 		die("must specify a file\n");
    290 
    291 	if ((fd = open(argv[1], O_RDONLY)) < 0)
    292 		die("can't open file: %s\n", argv[1]);
    293 	s.dlen = lseek(fd, 0, SEEK_END);
    294 	s.data = mmap(NULL, s.dlen, PROT_READ, MAP_PRIVATE, fd, 0);
    295 	s.chr = s.data[0];
    296 	close(fd);
    297 
    298 	scan(&s);
    299 	u64 sum = sum_pairs(s.data, &numpairs, &syms);
    300 	u64 gr = gear_ratio(s.data, &numpairs, &syms);
    301 	printf("Task 1:\t%8zu\n", sum);
    302 	printf("Task 2:\t%8zu\n", gr);
    303 
    304 	/* be a good boy and unmap */
    305 	munmap(s.data, s.dlen);
    306 
    307 	return 0;
    308 }