part2.c (7570B)
1#include "allocator.h" 2#include "aoc.h" 3#include "hmap.h" 4#include "hmap_s.h" 5#include "vec_s.h" 6#include "util.h" 7#include "list.h" 8 9#include <assert.h> 10#include <ctype.h> 11#include <stdbool.h> 12#include <stdint.h> 13#include <stdlib.h> 14 15#define PORTAL_ID(a, b, i) \ 16 ((size_t) (((uint8_t) (i) << 16) + ((uint8_t) (a) << 8) + (uint8_t) (b))) 17#define PORTAL_CC_MASK 0xffff 18#define PORTAL_IN_MASK 0x10000 19 20struct djk_state { 21 size_t dist; 22 struct vec3i pos; 23 24 struct list_link link; 25}; 26 27void 28vec3i_add_2d(struct vec3i *dst, const struct vec3i *src, const struct vec2i *off) 29{ 30 dst->x = src->x + off->x; 31 dst->y = src->y + off->y; 32 dst->z = src->z; 33} 34 35static bool 36djk_dist_ordering(const void *p1, const void *p2, void *u) 37{ 38 const struct djk_state *s1 = p1, *s2 = p2; 39 40 return s1->dist < s2->dist; 41} 42 43static void 44load_map(struct hmap *map, const char *input, size_t len, 45 struct vec2i *min, struct vec2i *max) 46{ 47 char line[256]; 48 const char *lpos, *lend; 49 struct vec2i pos, *key; 50 51 lpos = aoc.input; 52 lend = lpos + aoc.input_size; 53 max->x = max->y = 0; 54 vec2i_setv(&pos, 0, 0); 55 while (readtok(line, sizeof(line), '\n', &lpos, lend)) { 56 for (pos.x = 0; line[pos.x]; pos.x++) { 57 if (line[pos.x] == ' ') 58 continue; 59 if (line[pos.x] == '.') { 60 min->x = min->x ? MIN(min->x, pos.x) : pos.x; 61 min->y = min->y ? MIN(min->y, pos.y) : pos.y; 62 max->x = max->x ? MAX(max->x, pos.x) : pos.x; 63 max->y = max->y ? MAX(max->y, pos.y) : pos.y; 64 } 65 key = malloc(sizeof(struct vec3i)); 66 vec2i_set(key, &pos); 67 hmap_add(map, (struct hmap_key) {.p = key}, 68 (struct hmap_val) {.c = line[pos.x]}); 69 } 70 pos.y += 1; 71 } 72} 73 74static void 75load_portals(struct hmap *portals, struct hmap *map, 76 struct vec2i *min, struct vec2i *max) 77{ 78 struct hmap_link *link1; 79 struct hmap_link *link2; 80 struct hmap_iter iter; 81 struct vec2i pos; 82 struct vec2i *key; 83 size_t i, id; 84 bool inner; 85 86 for (HMAP_ITER(map, iter)) { 87 if (!isupper(iter.link->value.c)) 88 continue; 89 90 for (i = 0; i < 4; i++) { 91 vec2i_add(&pos, iter.link->key.p, &adj[i]); 92 link1 = hmap_get(map, (struct hmap_key) {.p = &pos}); 93 if (link1 && isupper(link1->value.c)) 94 break; 95 } 96 if (i != ADJ_SOUTH && i != ADJ_EAST) 97 continue; 98 99 for (i = 0; i < 4; i++) { 100 vec2i_add(&pos, iter.link->key.p, &adj[i]); 101 link2 = hmap_get(map, (struct hmap_key) {.p = &pos}); 102 if (link2 && link2->value.c == '.') 103 break; 104 } 105 if (i == 4) { 106 for (i = 0; i < 4; i++) { 107 vec2i_add(&pos, link1->key.p, &adj[i]); 108 link2 = hmap_get(map, (struct hmap_key) {.p = &pos}); 109 if (link2 && link2->value.c == '.') 110 break; 111 } 112 } 113 assert(i != 4); 114 vec2i_set(&pos, link2->key.p); 115 116 inner = pos.x != min->x && pos.y != min->y 117 && pos.x != max->x && pos.y != max->y; 118 aoc_debug("PORTAL %c %c %li %li %i\n", 119 iter.link->value.c, link1->value.c, 120 pos.x, pos.y, inner); 121 122 key = memdup(&pos, sizeof(struct vec2i)); 123 id = PORTAL_ID(iter.link->value.c, link1->value.c, inner); 124 hmap_add(portals, (struct hmap_key) {.p = key}, 125 (struct hmap_val) {.s = id}); 126 } 127} 128 129static bool 130get_portal(struct hmap *portals, struct vec3i *pos, size_t id) 131{ 132 struct hmap_iter iter; 133 const struct vec3i *tmp; 134 135 for (HMAP_ITER(portals, iter)) { 136 tmp = iter.link->key.p; 137 if (iter.link->value.s == id) { 138 pos->x = tmp->x; 139 pos->y = tmp->y; 140 pos->z = 0; 141 return true; 142 } 143 } 144 145 return false; 146} 147 148static void 149add_djk_state(struct list *active, struct hmap *distmap, 150 struct hmap *portals, struct vec3i *pos, size_t dist) 151{ 152 struct hmap_link **hmap_linkp; 153 struct djk_state *nstate; 154 struct vec3i *key; 155 bool new; 156 157 hmap_linkp = hmap_link_pos(distmap, (struct hmap_key) {.p = pos}); 158 if (!*hmap_linkp) { 159 key = memdup(pos, sizeof(struct vec3i)); 160 *hmap_linkp = hmap_link_alloc(distmap, 161 (struct hmap_key) {.p = key}, 162 (struct hmap_val) {.s = dist}, NULL); 163 new = true; 164 } else { 165 if (dist < (*hmap_linkp)->value.s) { 166 (*hmap_linkp)->value.s = dist; 167 new = true; 168 } else { 169 new = false; 170 } 171 } 172 173 if (new) { 174 aoc_debug("ADDING NEW\n"); 175 nstate = malloc(sizeof(struct djk_state)); 176 nstate->dist = dist; 177 vec3i_set(&nstate->pos, pos); 178 nstate->link = LIST_LINK_INIT; 179 list_insert_sorted(active, &nstate->link, 180 false, djk_dist_ordering, 181 LIST_OFFSET(struct djk_state, link), NULL); 182 } 183} 184 185static size_t 186min_dist(struct hmap *map, struct hmap *portals, 187 struct vec3i *start, struct vec3i *end) 188{ 189 struct list active; 190 struct hmap distmap; 191 struct list_link *list_link; 192 struct hmap_link *hmap_link; 193 struct hmap_iter hmap_iter; 194 struct djk_state *state; 195 struct vec3i *key, npos; 196 size_t mdist, i, id; 197 198 list_init(&active); 199 hmap_init(&distmap, 256, vec3i_hmap_hash, vec3i_hmap_keycmp, 200 &stdlib_strict_heap_allocator); 201 202 state = malloc(sizeof(struct djk_state)); 203 state->dist = 0; 204 vec3i_set(&state->pos, start); 205 list_insert_front(&active, &state->link); 206 207 key = memdup(start, sizeof(struct vec3i)); 208 hmap_add(&distmap, (struct hmap_key) {.p = key}, 209 (struct hmap_val) {.s = 0}); 210 211 mdist = 0; 212 while (!list_empty(&active)) { 213 list_link = list_front(&active); 214 state = LIST_UPCAST(list_link, struct djk_state, link); 215 if (vec3i_eql(&state->pos, end)) { 216 mdist = state->dist; 217 goto exit; 218 } 219 220 list_link_pop(list_link); 221 222 aoc_debug("ACTIVE (%lu) %li %li %li\n", 223 state->dist, state->pos.x, state->pos.y, state->pos.z); 224 225 hmap_link = hmap_get(&distmap, (struct hmap_key) {.p = &state->pos}); 226 assert(hmap_link); 227 if (state->dist != hmap_link->value.s) { 228 free(state); 229 continue; 230 } 231 232 hmap_link = hmap_get(portals, (struct hmap_key) {.p = &state->pos}); 233 if (hmap_link) { 234 id = hmap_link->value.s; 235 if (get_portal(portals, &npos, id ^ PORTAL_IN_MASK)) { 236 if (id) { 237 aoc_debug("PORTAL %c %c %i -> %li %li\n", 238 (id >> 8) & 0xff, id & 0xff, 239 !!(id & PORTAL_IN_MASK), npos.x, npos.y); 240 } else { 241 aoc_debug("NO PORTAL %c %c\n", 242 (hmap_link->value.s >> 8) & 0xff, 243 (hmap_link->value.s >> 0) & 0xff); 244 } 245 246 if (id & PORTAL_IN_MASK) { 247 npos.z = state->pos.z + 1; 248 add_djk_state(&active, &distmap, portals, 249 &npos, state->dist + 1); 250 } else if (id) { 251 npos.z = state->pos.z - 1; 252 if (npos.z >= 0) { 253 add_djk_state(&active, &distmap, portals, 254 &npos, state->dist + 1); 255 } 256 } 257 } 258 } 259 260 for (i = 0; i < 4; i++) { 261 vec3i_add_2d(&npos, &state->pos, &adj[i]); 262 hmap_link = hmap_get(map, (struct hmap_key) {.p = &npos}); 263 if (!hmap_link || hmap_link->value.c != '.') continue; 264 add_djk_state(&active, &distmap, portals, 265 &npos, state->dist + 1); 266 } 267 268 free(state); 269 } 270 271exit: 272 for (HMAP_ITER(&distmap, hmap_iter)) 273 free(hmap_iter.link->key._p); 274 hmap_deinit(&distmap); 275 list_free_items(&active, free, LIST_OFFSET(struct djk_state, link)); 276 277 return mdist; 278} 279 280void 281part2(void) 282{ 283 struct hmap map; 284 struct hmap portals; 285 struct hmap_iter iter; 286 struct vec3i start, end; 287 struct vec2i min, max; 288 size_t answer; 289 290 hmap_init(&map, 256, vec2i_hmap_hash, vec2i_hmap_keycmp, 291 &stdlib_strict_heap_allocator); 292 hmap_init(&portals, 256, vec2i_hmap_hash, vec2i_hmap_keycmp, 293 &stdlib_strict_heap_allocator); 294 295 load_map(&map, aoc.input, aoc.input_size, &min, &max); 296 load_portals(&portals, &map, &min, &max); 297 298 assert(get_portal(&portals, &start, PORTAL_ID('A', 'A', 0))); 299 assert(get_portal(&portals, &end, PORTAL_ID('Z', 'Z', 0))); 300 answer = min_dist(&map, &portals, &start, &end); 301 302 aoc.answer = aprintf("%lu", answer); 303 aoc.solution = "7114"; 304 305 for (HMAP_ITER(&portals, iter)) 306 free(iter.link->key._p); 307 hmap_deinit(&portals); 308 for (HMAP_ITER(&map, iter)) 309 free(iter.link->key._p); 310 hmap_deinit(&map); 311}