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 }