aoc-2019-c

Advent of Code 2019 Solutions in C
git clone https://git.sinitax.com/sinitax/aoc-2019-c
Log | Files | Refs | README | sfeed.txt

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}