aoc-2019-c

git clone https://git.sinitax.com/sinitax/aoc-2019-c
Log | Files | Refs | README | sfeed.txt

commit 744cb4c6f5e12f84d5a77d8eee69a60c81d53fee
parent 019fd07126c76913fdf62ee2bc1813e666ddf25c
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sun, 26 Mar 2023 15:56:15 +0200

Add day 18

Diffstat:
M.gitignore | 1+
M.gitmodules | 6++++++
M06/main.c | 8+++-----
M07/main.c | 2+-
M10/main.c | 2+-
M11/main.c | 2+-
M12/main.c | 7+++----
M13/main.c | 2+-
M14/main.c | 12++++++------
M15/main.c | 11++++++-----
M16/main.c | 10+++++-----
M17/main.c | 25+++++++++++++------------
A18/info.mk | 4++++
A18/input | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A18/main.c | 898+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A18/part1 | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A18/part2 | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A18/test1 | 6++++++
A18/test2 | 5+++++
A18/test3 | 7+++++++
MMakefile | 17+++++++++++++++--
Acommon/dvec_s.h | 29+++++++++++++++++++++++++++++
Mcommon/icc.c | 6++----
Mcommon/main.c | 4+++-
Mcommon/util.c | 2+-
Mcommon/util.h | 2+-
Alib/liblist | 1+
Alib/libpq | 1+
28 files changed, 1403 insertions(+), 50 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -5,3 +5,4 @@ main build vgcore* .gdb_history +perf.data diff --git a/.gitmodules b/.gitmodules @@ -10,3 +10,9 @@ [submodule "lib/libhmap"] path = lib/libhmap url = git@sinitax.com:snx/libhmap +[submodule "lib/liblist"] + path = lib/liblist + url = git@sinitax.com:snx/liblist +[submodule "lib/libpq"] + path = lib/libpq + url = git@sinitax.com:snx/libpq diff --git a/06/main.c b/06/main.c @@ -1,7 +1,7 @@ #include "aoc.h" -#include "dvec.h" -#include "hmap.h" #include "allocator.h" +#include "hmap.h" +#include "dvec_s.h" #include "util.h" #include <assert.h> @@ -57,7 +57,6 @@ planets_init() struct planet *parent, *child, **slot; const char *pos, *end; char buf[256], *sep; - int rc; hmap_init(&planets, 100, hmap_str_hash, hmap_str_keycmp, &stdlib_heap_allocator); @@ -75,8 +74,7 @@ planets_init() child = planets_get(sep + 1); if (!child) child = planets_add(sep + 1); - slot = dvec_add_slot(&parent->children, &rc); - if (rc) die("main: dvec_add_slot"); + slot = dvec_add_slot(&parent->children); *slot = child; if (child->parent) die("main unexpected parent"); diff --git a/07/main.c b/07/main.c @@ -1,7 +1,7 @@ -#include "allocator.h" #include "aoc.h" #include "icc.h" #include "util.h" +#include "allocator.h" #include <assert.h> #include <string.h> diff --git a/10/main.c b/10/main.c @@ -1,5 +1,5 @@ -#include "allocator.h" #include "aoc.h" +#include "allocator.h" #include "util.h" #include "hmap.h" diff --git a/11/main.c b/11/main.c @@ -1,5 +1,5 @@ -#include "allocator.h" #include "aoc.h" +#include "allocator.h" #include "iccmp.h" #include "hmap.h" #include "util.h" diff --git a/12/main.c b/12/main.c @@ -1,6 +1,6 @@ #include "aoc.h" #include "allocator.h" -#include "dvec.h" +#include "dvec_s.h" #include "util.h" #include "maxint.h" @@ -26,7 +26,7 @@ moons_init(struct dvec *vec) { char *p, *tok, *val; struct moon m, *slot; - int i, rc; + int i; m.vel = (struct vec3) { 0, 0, 0 }; dvec_init(vec, 100, sizeof(struct moon), &stdlib_strict_heap_allocator); @@ -36,8 +36,7 @@ moons_init(struct dvec *vec) val = tok; for (i = 0; i < 3 && (val = strchr(val + 1, '=')); i++) m.pos.axis[i] = (int) strtol(val + 1, NULL, 10); - slot = dvec_add_slot(vec, &rc); - assert(!rc); + slot = dvec_add_slot(vec); memcpy(slot, &m, sizeof(struct moon)); tok = p + 1; } diff --git a/13/main.c b/13/main.c @@ -1,5 +1,5 @@ -#include "allocator.h" #include "aoc.h" +#include "allocator.h" #include "iccmp.h" #include "hmap.h" #include "maxint.h" diff --git a/14/main.c b/14/main.c @@ -1,8 +1,8 @@ -#include "allocator.h" #include "aoc.h" +#include "allocator.h" #include "util.h" #include "hmap.h" -#include "dvec.h" +#include "dvec_s.h" #include <assert.h> #include <stdbool.h> @@ -53,7 +53,7 @@ parse_reactions(struct hmap *reactions) assert(rc == 2); if (sep) { - slot = dvec_add_slot(&reaction->ingredients, NULL); + slot = dvec_add_slot(&reaction->ingredients); memcpy(slot, &ingredient, sizeof(struct ingredient)); } else { @@ -119,7 +119,7 @@ calculate_ore(struct hmap *reactions, struct hmap *excess, dvec_init(&reactants, sizeof(struct ingredient), 10, &stdlib_strict_heap_allocator); - slot = dvec_add_slot(&reactants, NULL); + slot = dvec_add_slot(&reactants); ingredient.amount = amount; snprintf(ingredient.name, 8, "%s", name); memcpy(slot, &ingredient, sizeof(struct ingredient)); @@ -137,7 +137,7 @@ calculate_ore(struct hmap *reactions, struct hmap *excess, n = add_excess(excess, &result, reaction); if (!n) continue; - for (DVEC_ITER(&reaction->ingredients, &slot)) { + for (DVEC_ITER(&reaction->ingredients, slot)) { aoc_debug("%lu %s needs %lu %s\n", result.amount, result.name, slot->amount * n, slot->name); @@ -146,7 +146,7 @@ calculate_ore(struct hmap *reactions, struct hmap *excess, } else { snprintf(ingredient.name, 8, "%s", slot->name); ingredient.amount = slot->amount * n; - slot2 = dvec_add_slot(&reactants, NULL); + slot2 = dvec_add_slot(&reactants); memcpy(slot2, &ingredient, sizeof(struct ingredient)); } } diff --git a/15/main.c b/15/main.c @@ -1,6 +1,7 @@ +#include "aoc.h" +#include "dvec_s.h" #include "allocator.h" #include "util.h" -#include "aoc.h" #include "iccmp.h" #include "hmap.h" #include "vec.h" @@ -148,7 +149,7 @@ dijkstra_dist(struct hmap *map, struct vec2i start, const struct vec2i *end) dvec_init(&outer, sizeof(struct vec2i), 10, &stdlib_strict_heap_allocator); - vec = dvec_add_slot(&outer, NULL); + vec = dvec_add_slot(&outer); vec2i_set(vec, &start); key = memdup(vec, sizeof(struct vec2i)); @@ -180,7 +181,7 @@ dijkstra_dist(struct hmap *map, struct vec2i start, const struct vec2i *end) if (!maxdist || dist > maxdist) maxdist = dist; - vec = dvec_add_slot(&outer, NULL); + vec = dvec_add_slot(&outer); vec2i_set(vec, &npos); key = memdup(&npos, sizeof(struct vec2i)); hmap_link_alloc(&seen, linkp, @@ -214,7 +215,7 @@ explore_map(struct icc *icc, struct hmap *map, dvec_init(&states, sizeof(struct state), 10, &stdlib_strict_heap_allocator); - state = dvec_add_slot(&states, NULL); + state = dvec_add_slot(&states); state->back = -1; state->next = 1; @@ -270,7 +271,7 @@ explore_map(struct icc *icc, struct hmap *map, vec2i_set(tank, &npos); case BLOCK_OPEN: vec2i_set(&pos, &npos); - state = dvec_add_slot(&states, NULL); + state = dvec_add_slot(&states); state->back = rev[move_dir]; state->next = 1; break; diff --git a/16/main.c b/16/main.c @@ -1,7 +1,7 @@ +#include "aoc.h" #include "allocator.h" #include "util.h" -#include "aoc.h" -#include "dvec.h" +#include "dvec_s.h" #include <assert.h> #include <string.h> @@ -19,7 +19,7 @@ parse_input(struct dvec *phases) assert(sep != NULL); for (c = aoc.input; c < sep; c++) { - val = dvec_add_slot(phases, NULL); + val = dvec_add_slot(phases); *val = *c - '0'; } } @@ -105,7 +105,7 @@ fft_dup(struct dvec *phases, size_t offset, size_t rounds, size_t dup) dvec_clear(phases); for (i = 0; i < 8; i++) { - slot = dvec_add_slot(phases, NULL); + slot = dvec_add_slot(phases); *slot = lut[rounds * lutlen + i]; } @@ -171,7 +171,7 @@ part2(void) answer[i] = 0; aoc.answer = answer; - aoc.solution = ""; + aoc.solution = "73556504"; dvec_deinit(&phases); } diff --git a/17/main.c b/17/main.c @@ -1,8 +1,9 @@ +#include "aoc.h" #include "allocator.h" #include "util.h" -#include "aoc.h" #include "iccmp.h" #include "hmap.h" +#include "dvec_s.h" #include "vec.h" #include <assert.h> @@ -191,12 +192,12 @@ add_path(struct hmap *paths, struct path *path) link = hmap_link_pos(paths, (struct hmap_key) {.p = &path->start}); if (*link) { list = (*link)->value._p; - memcpy(dvec_add_slot(list, NULL), path, sizeof(struct path)); + memcpy(dvec_add_slot(list), path, sizeof(struct path)); } else { key = memdup(&path->start, sizeof(struct vec2i)); dvec_alloc(&list, sizeof(struct path), 1, &stdlib_strict_heap_allocator); - memcpy(dvec_add_slot(list, NULL), path, sizeof(struct path)); + memcpy(dvec_add_slot(list), path, sizeof(struct path)); hmap_add(paths, (struct hmap_key) {.p = key}, (struct hmap_val) {.p = list}); @@ -266,7 +267,7 @@ gen_insts(struct dvec *insts, struct hmap *paths, found = false; list = link->value._p; - for (DVEC_ITER(list, &path)) { + for (DVEC_ITER(list, path)) { if (vec2i_eql(&ppos, &path->end)) continue; found = true; @@ -290,14 +291,14 @@ gen_insts(struct dvec *insts, struct hmap *paths, type = type == 'L' ? 'R' : 'L'; } for (; cnt > 0; cnt--) { - inst = dvec_add_slot(insts, NULL); + inst = dvec_add_slot(insts); *inst = type; } dir = ndir; cnt = (int) ABS(path->end.x - path->start.x) + (int) ABS(path->end.y - path->start.y); - inst = dvec_add_slot(insts, NULL); + inst = dvec_add_slot(insts); *inst = (char) -cnt; } } @@ -309,7 +310,7 @@ insts_line(struct dvec *insts) char *c; str = NULL; - for (DVEC_ITER(insts, &c)) { + for (DVEC_ITER(insts, c)) { if (str) str = apprintf(str, ","); if (*c < 0) { str = apprintf(str, "%i", -*c); @@ -331,28 +332,28 @@ gen_func(struct dvec *insts, struct dvec *main, size_t i; for (i = start; i < MIN(insts->len, start + len); i++) { - inst = dvec_add_slot(cur, NULL); + inst = dvec_add_slot(cur); *inst = *(char *)dvec_at(insts, i); } - inst = dvec_add_slot(main, NULL); + inst = dvec_add_slot(main); *inst = cc; while (1) { if (f1 && i + f1->len <= insts->len && !memcmp(dvec_at(insts, i), f1->data, f1->len)) { i += f1->len; - inst = dvec_add_slot(main, NULL); + inst = dvec_add_slot(main); *inst = c1; } else if (f2 && i + f2->len <= insts->len && !memcmp(dvec_at(insts, i), f2->data, f2->len)) { i += f2->len; - inst = dvec_add_slot(main, NULL); + inst = dvec_add_slot(main); *inst = c2; } else if (i + cur->len <= insts->len && !memcmp(dvec_at(insts, i), cur->data, cur->len)) { i += cur->len; - inst = dvec_add_slot(main, NULL); + inst = dvec_add_slot(main); *inst = cc; } else { break; diff --git a/18/info.mk b/18/info.mk @@ -0,0 +1,4 @@ +18_SRC = 18/main.c common/main.c common/aoc.c common/util.c +18_SRC += lib/libdvec/build/libdvec.a lib/liballoc/build/liballoc.a +18_SRC += lib/libhmap/build/libhmap.a lib/liblist/build/liblist.a +18_HDR = common/aoc.h common/util.h diff --git a/18/input b/18/input @@ -0,0 +1,82 @@ +################################################################################# +#.......#.A...........#.........#.......#.#.........#.............#...#..r......# +#.###.###.#####.#####.#.#######.#.#######.#.#.#####.#.#.#.#######.###.#.###.###.# +#...#.#...#.....#.#...#...#.....#.......#...#.#...#o#.#.#.#...#.#...#.#...#.#...# +#####.#.###.#####.#.###.#W###.#########.#####.#.#.###.#.#.#.#.#.###.#K###.#.##### +#.....#.#.#...#...#...#.#...#...........#...#...#.....#.#.#.#.#.#...#...#.#.....# +#M###.#.#.###.#.#####.#####.###########.#.#.###.#######.###.#.#.#.#####.#.#####.# +#.#...#...#...#.#...#.....#...#..z....#.#.#...#...#.....#...#.#.#.#...#.......#.# +#.###.###.#.###.#.#.#####.###.#.###.#.#.#.###.#####.#.###.###.#.#.#.#.#####.###.# +#...#...#.#.#....y#.....#.....#.#.#.#.#.#...#.#...#.#.#...#...#.#...#.#...#.#m..# +#.#.#####.#.###########.#######.#.#.#.#.###.#.#.#X#.#.#.###.###.#####.#.#.###.### +#.#.#...#.#s..#.......#.......#...#.#.#.#...#...#.#.#.#.#.#.#.......#...#...#...# +###.#.#.#.###.#.#####.###.###.#####.#.#.#.#######.#.###.#.#.#.#####.#######.###.# +#...#.#...#.#...#...#.#...#.......#.#.#.#.#.......#.....#.#.#.#.....#.....#.....# +#.###.#####.#####.#.#C#.###.#####.#.#.#.#.#####.###.#####.#.###.#####.#.#.#####.# +#.....#.....#.....#.#.#.#...#...#...#.#.#.#.F.#...#...#...#...#.#...#.#.#.....#.# +#.#######.#.###.###.#.#.#####.#.#######.#.#.#.###.###.###.###.#.#.#.#.#.#######.# +#.#.......#...#.#...#.#...#...#.#.......#...#.#.#...#.......#.#.#.#.#.#.........# +#.#.#########.#.#####.###N#.###.#.###########.#.###.#########.#.#.#.#.########### +#...#...#.......#.....#...#.#.#.#.......#...#.#...............#...#.#.#....q....# +#####.###.#######.#####.###.#.#.#######.#.#.#.#############.#####.#.#.#.#.#####.# +#g..#.....#.....#...#.#...#...#.......#.#.#.#..l..........#.#...#.#.#.#.#...#...# +###.#.#####.###.###.#T#.#.###.###.#####.###.#############.#####B#.#.#.#.###.##### +#...#.#...#...#.....#.#.#...#.#.#.#...#.#.......#.......#...#...#.#...#...#.....# +#.###.###.###.#####.#.###.###.#.#.#.#.#.#.#####.#.###.#####.#.###.#####.#######.# +#...#...#...#.#.#...#...#.I...#.#...#...#.....#.#...#.#...#...#.#...#.#.#.......# +###.###.#.###.#.#.#####.#####.#.#############.#####.#.#.#.#####.###.#.#.#.####### +#.....#.#...#.#.......#.#...#...........#.....#.....#...#.......#.#.#.#.#.......# +#.#####.###.#.#######.#.#.#.#####.#######.###.#.###########.###.#.#.#.#.#######.# +#...........#.#e....#.#...#...#...#.....#.#...#...#.........#.....#.#...#.......# +#.###########.#.###.#########.#####.###.#.#######.#.#####.#######.#.#####.#####.# +#.....#.#.....#.#.#.........#.......#...#.#.......#.#.....#.....#.#...#...#.#...# +#####.#.#.#####.#.#####.#.###########.###.#.#######.#####.#.###.#.###.#.###.#.### +#...#.#.#.......#.#.....#.#.........#.#.#...#.#.....#...#.#.#...#...#.#...#...#.# +#.###.#.#########.#.#####.#V#.#####.#.#.#.###.#.###.#.#.###.#.#######.###.#.###.# +#.#...#.....#.....#.#.....#.#...#...#.#.#.#...#.#.#.#.#.....#.........#...#.#...# +#.#.###.###.#.#####.#######.###.#.###.#.#.###.#.#.#.#.#################.###.#.#.# +#.#.....#.#.#.....#.......#.#.#.#...#.#.#.#...#.#...#.#.........#.......#.S.#.#.# +#.#######.#.#####.#######.#.#.#.#####.#.#.#.#.#.#####.#####.###.#.#######.###.#.# +#.......................#.....#.............#.#.............#.....#...........#.# +#######################################.@.####################################### +#...............#.....#.....#.............#.........................D.......#..u# +#.#############.#####.#.#.###.###.#####.#.#.#.#####.#######.###############.#.### +#.....#.....#...#...#...#.....#.#.#.....#...#....v#.#.....#.#.......#.....#.#.E.# +#####.#.#####.###.#.#.#########.#.#######.#######.###.###.###.#####.#.#####.#.#.# +#.....#.....#.....#.#.#...#.....#.......#.#.......#.....#.#...#...#.#....j#t#.#.# +#.#########.#######.#.#.#.#.###########.#.#.#######.#####.#.###.###.#####.#.#.#.# +#.......#...#...#...#...#...#.......#...#.#.#.....#.#.....#.#.....#.#...#.#.#.#.# +#######.#.#.###.#.#######.###.#####.#.#####.#.###.#.#.#####.#####.#.#.#.#.#.###.# +#.....#.#.#.....#...#.....#...#.....#...#...#.#.#...#.....#.......#...#.#.#.#...# +###.###.#.#####.###.#####.#.###.#####.#.#.#.#.#.#########.#######.#####.#.#.#.#.# +#...#...#...#...#.#.....#.#.#.#.#.....#.#.#d#.#.....#.......#.....#.....#.#...#.# +#.###.#####.#.###.#####.#.#.#.#.#######.#.###.###.#.#.#######.#####.#####.#####.# +#.....#...#.#.....#.....#.#.#.#...#.....#.....#...#.#.....#.....#...#...#.....#.# +#.#####.#.#.#.#####.#####.#.#.###.#.###.#######.#.#######.#.#####.#####.#.#.#.#.# +#.#.....#.#.#.#.....#.....#.#...#.#.#...#.......#.#.....#...#.#...#.....#.#.#.#.# +#.###.#.#.#.#.#.###########.###.#.#.#.###.#########.#.#######.#.#####.###.#.###.# +#.H.#.#.#.#.#.#.....#.....#.#...#...#...#.#.....#...#.........#.#.....#...#.....# +###.###.#.#.#.#####.#.###.#.#.#########.#.#.###.#.#######.#####.#.#.#.#.######### +#.#.#...#...#...#.#...#...#.#...#.......#...#.#...#.....#...#...#.#.#.#.........# +#.#.#.#.#######.#.#####.###.#.#.#.#######.###.#########.###.#.#####.#.#########.# +#...#.#.#...#.#.#...#...#...#.#.#...#...#...#...#.....#...#.#.#.....#.#.......#.# +#.###.###.#.#.#.###.#######.#.#.###.#.#.###.#.###.#.#.#.#.#.###.#####.###.###.#.# +#.#...#...#.#.#.#...........#.#...#.#.#.#.#.#.#...#.#...#.#.....#...#...#.#.#.#.# +#.###.#.###.#.#.#.###########.###.#.#.#.#.#.#.#.###.#####.#####.###.###.#.#.#.#.# +#...#...#.#.#.#...#...#.U...#.#.#...#.#.#...#.....#.#.....#...#...#...#...#...#.# +###.#.###.#.#.#####J#.#.#.###.#.#####.###.#######.#.#####.#.#####.#.#####.#####.# +#...#.Q.#.#.#.....#.#...#.....#.........#.......#.#f....#.L.#.....#h....#.#...#.# +#.#####.#.#.###.#.#.#################.#.#######.#.#####.###.#.#####.#.###.#.#.#.# +#.#...#...#...#.#.#.#...#.......#...#.#.#.#...G.#.....#.Z.#.#...#...#.....#.#...# +#.#.#####.###.###.#.#.#.#####.#.#.#.###.#.#.#############.#.###.#########.#.##### +#k#.#.....#.#...#.#..c#...#...#...#.#...#.#.#........n#...#...#.#.......#.#...#.# +#.#.#.#####.###.#.#######.#.#######.#.#.#.#.#.#######.#.###.###.#.#####.#####.#.# +#.#...#.....#...#.#.P...#.#.#.....#...#.#.#.#.#...#...#.#...#...#.....#.....#p..# +#.###.#.#.###.###.#.#.###.#.###.#.#####.#.#.#.#.###.###.###.#.#######.###.#####.# +#...#...#.#...#.#...#...#.#...#.#...#...#.O.#.#.....#.#...#b#.#.......#.#.#...#.# +###.#####.#.###.#.#####.#.###.#####.#.###.###.#.#####.#.#.###.#.#######.#.#.#.#.# +#.#.#.....#.#a..#.#...#.#.#...#.....#...#...#i#.#...#...#.Y.#.#.#....w..#.#.#...# +#.#R#######.#.#.#.###.#.#.#.###.#######.###.#.#.#.#.#######.#.#.#####.#.#.#.##### +#...........#.#.......#.....#...........#.....#x..#.........#.........#.#.......# +################################################################################# + diff --git a/18/main.c b/18/main.c @@ -0,0 +1,898 @@ +#include "aoc.h" +#include "allocator.h" +#include "dvec_s.h" +#include "hmap.h" +#include "util.h" +#include "list.h" +#include "vec.h" + +#include <signal.h> +#include <assert.h> +#include <ctype.h> +#include <stdbool.h> +#include <stdint.h> + +struct state { + struct vec2i pos; + uint32_t acquired; + size_t dist; + struct list_link link; +}; + +struct state_multi { + struct vec2i pos[4]; + uint32_t acquired; + size_t dist; + struct list_link link; +}; + +struct prog { + uint32_t acquired; + size_t dist; +}; + +struct path { + uint32_t required; + size_t dist; +}; + +struct keypath { + uint32_t required; + size_t dist; + struct vec2i pos; + uint32_t keymask; + char key; +}; + +struct node { + struct dvec edges; + int key; +}; + +const struct vec2i dirs[] = { + { 0, -1 }, + { 1, 0 }, + { 0, 1 }, + { -1, 0 }, +}; + +static inline bool +bit_subset(uint32_t a, uint32_t b) +{ + return (a & b) == a; +} + +static inline bool +bit_proper_subset(uint32_t a, uint32_t b) +{ + return bit_subset(a, b) && (a < b); +} + +static inline bool +bit_superset(uint32_t a, uint32_t b) +{ + return (a & b) == b; +} + +static inline bool +bit_proper_superset(uint32_t a, uint32_t b) +{ + return bit_superset(a, b) && (a > b); +} + +bool +vec2i_hmap_keycmp(struct hmap_key k1, struct hmap_key k2) +{ + const struct vec2i *v1 = k1.p, *v2 = k2.p; + + return vec2i_eql(v1, v2); +} + +uint32_t +vec2i_hmap_hash(struct hmap_key key) +{ + const struct vec2i *v = key.p; + + return (uint32_t) v->x + (uint32_t) v->y * 80; +} + +bool +vec2i_multi_hmap_keycmp(struct hmap_key k1, struct hmap_key k2) +{ + const struct vec2i *v1 = k1.p, *v2 = k2.p; + size_t i; + + for (i = 0; i < 4; i++) { + if (!vec2i_eql(&v1[i], &v2[i])) + return false; + } + + return true; +} + +uint32_t +vec2i_multi_hmap_hash(struct hmap_key key) +{ + const struct vec2i *v = key.p; + uint32_t hash; + size_t i; + + hash = 0; + for (i = 0; i < 4; i++) + hash += (uint32_t) v[i].x + (uint32_t) v[i].y * 80; + + return hash; +} + +void +print_map(const struct hmap *map, struct hmap *distmap, + const struct vec2i *start, uint32_t acquired) +{ + struct hmap_iter iter; + struct hmap_link *link; + struct vec2i min, max; + struct vec2i pos; + uint32_t mask; + char c; + + vec2i_set(&min, start); + vec2i_set(&max, start); + for (HMAP_ITER(map, &iter)) { + vec2i_set(&pos, iter.link->key._p); + if (pos.x < min.x) min.x = pos.x; + if (pos.y < min.y) min.y = pos.y; + if (pos.x > max.x) max.x = pos.x; + if (pos.y > max.y) max.y = pos.y; + } + + aoc_debug("+"); + for (pos.x = min.x; pos.x <= max.x; pos.x++) + aoc_debug("-"); + aoc_debug("+\n"); + + for (pos.y = min.y; pos.y <= max.y; pos.y++) { + aoc_debug("|"); + for (pos.x = min.x; pos.x <= max.x; pos.x++) { + if (vec2i_eql(&pos, start)) { + aoc_debug("\x1b[5m@\x1b[0m"); + continue; + } + + link = hmap_get(map, (struct hmap_key) {.p = &pos}); + if (link) { + c = link->value.c; + if (isalpha(c)) { + mask = islower(c) ? 1 << (c - 'a') + : 1 << (c - 'A'); + if (acquired & mask) { + aoc_debug("\x1b[90m%c\x1b[0m", c); + } else { + aoc_debug("%c", c); + } + continue; + } + + link = hmap_get(distmap, + (struct hmap_key) {.p = &pos}); + if (link) { + aoc_debug(" "); + } else { + aoc_debug("%c", c); + } + } else { + aoc_debug(" "); + } + } + aoc_debug("|\n"); + } + + aoc_debug("+"); + for (pos.x = min.x; pos.x <= max.x; pos.x++) + aoc_debug("-"); + aoc_debug("+\n"); +} + +void +parse_input(struct hmap *map, struct vec2i *start, size_t *keycnt) +{ + const char *lpos, *lend; + const char *c; + char line[256]; + struct vec2i pos; + void *key; + + *keycnt = 0; + + lpos = aoc.input; + lend = aoc.input + aoc.input_size; + vec2i_setv(&pos, 0, 0); + while (readtok(line, sizeof(line), '\n', &lpos, lend)) { + pos.x = 0; + for (c = line; *c; c++) { + key = memdup(&pos, sizeof(struct vec2i)); + hmap_add(map, (struct hmap_key) {.p = key}, + (struct hmap_val) {.c = *c}); + if (islower(*c)) *keycnt += 1; + if (*c == '@') vec2i_set(start, &pos); + pos.x += 1; + } + pos.y += 1; + } +} + +bool +update_paths(struct dvec *paths, size_t dist, uint32_t required) +{ + struct path *path; + + for (DVEC_ITER(paths, path)) { + if (dist < path->dist) { + if (bit_subset(required, path->required)) { + path->required = required; + path->dist = dist; + return true; + } + } else if (dist == path->dist) { + if (bit_proper_subset(required, path->required)) { + path->required = required; + return true; + } + } else { + if (bit_superset(required, path->required)) + return false; + } + } + + path = dvec_add_slot(paths); + path->dist = dist; + path->required = required; + + return true; +} + +void +add_node(struct hmap *graph, const struct hmap *map, + const struct vec2i *start, int key) +{ + struct hmap_iter iter; + struct dvec active, active_next; + struct state state, *cstate, *nstate; + struct dvec *paths; + struct path *path; + struct keypath *keypath; + struct hmap_link *link, **linkp; + struct hmap distmap; + struct node *node; + struct vec2i *pos; + size_t i; + char c; + + /* + * - dijkstra from start pos + * - when we encounter a door, must have acquired corresponding key + * - distmap resolves to dvec of paths + * - only save new distance if less than all known paths + * and/or path requires a subset of keys + * + * - loop over keys in generated distance map + * - add keys as edges to node + */ + + dvec_init(&active, sizeof(struct state), 10, + &stdlib_strict_heap_allocator); + dvec_init(&active_next, sizeof(struct state), 10, + &stdlib_strict_heap_allocator); + hmap_init(&distmap, 64, vec2i_hmap_hash, vec2i_hmap_keycmp, + &stdlib_strict_heap_allocator); + + cstate = dvec_add_slot(&active_next); + vec2i_set(&cstate->pos, start); + cstate->acquired = 0; + cstate->dist = 0; + + pos = memdup(start, sizeof(struct vec2i)); + dvec_alloc(&paths, sizeof(struct path), 1, + &stdlib_strict_heap_allocator); + path = dvec_add_slot(paths); + path->dist = 0; + path->required = 0; + hmap_add(&distmap, (struct hmap_key) {.p = pos}, + (struct hmap_val) {.p = paths}); + + while (!dvec_empty(&active_next)) { + dvec_swap(&active, &active_next); + dvec_clear(&active_next); + + for (DVEC_ITER(&active, cstate)) { + for (i = 0; i < ARRLEN(dirs); i++) { + vec2i_add(&state.pos, &cstate->pos, &dirs[i]); + + link = hmap_get(map, + (struct hmap_key) {.p = &state.pos}); + assert(link != NULL); + c = link->value.c; + if (c == '#') continue; + + state.dist = cstate->dist + 1; + state.acquired = cstate->acquired; + if (isupper(c)) /* need key for door */ + state.acquired |= (1U << (c - 'A')); + + linkp = hmap_link_pos(&distmap, + (struct hmap_key) {.p = &state.pos}); + if (*linkp) { + paths = (*linkp)->value._p; + } else { + pos = memdup(&state.pos, sizeof(struct vec2i)); + dvec_alloc(&paths, sizeof(struct path), + 1, &stdlib_strict_heap_allocator); + hmap_link_alloc(&distmap, linkp, + (struct hmap_key) {.p = pos}, + (struct hmap_val) {.p = paths}); + } + + if (update_paths(paths, state.dist, state.acquired)) { + nstate = dvec_add_slot(&active_next); + memcpy(nstate, &state, sizeof(struct state)); + } + } + } + } + + node = malloc(sizeof(struct node)); + node->key = key; + dvec_init(&node->edges, sizeof(struct keypath), 1, + &stdlib_strict_heap_allocator); + for (HMAP_ITER(map, &iter)) { + c = iter.link->value.c; + if (vec2i_eql(iter.link->key.p, start)) + continue; + if (islower(c)) { + link = hmap_get(&distmap, iter.link->key); + if (!link) continue; + pos = iter.link->key._p; + for (DVEC_ITER(link->value.p, path)) { + keypath = dvec_add_slot(&node->edges); + keypath->key = c; + keypath->keymask = 1U << (c - 'a'); + keypath->dist = path->dist; + keypath->required = path->required; + vec2i_set(&keypath->pos, iter.link->key.p); + } + } + } + pos = memdup(start, sizeof(struct vec2i)); + hmap_add(graph, (struct hmap_key) {.p = pos}, + (struct hmap_val) {.p = node}); + + if (aoc.debug == 2) { + aoc_debug("add_node: %li %li\n", start->x, start->y); + print_map(map, &distmap, start, 0); + for (DVEC_ITER(&node->edges, keypath)) { + aoc_debug("path: %c %3lu %032b\n", keypath->key, + keypath->dist, keypath->required); + } + aoc_debug("\n"); + } + + for (HMAP_ITER(&distmap, &iter)) { + free(iter.link->key._p); + dvec_free(iter.link->value._p); + } + hmap_deinit(&distmap); + dvec_deinit(&active_next); + dvec_deinit(&active); +} + +void +gen_graph(struct hmap *graph, const struct hmap *map, + const struct vec2i *start, size_t keycnt) +{ + struct hmap_iter iter; + + add_node(graph, map, start, -1); + for (HMAP_ITER(map, &iter)) { + if (islower(iter.link->value.c)) { + add_node(graph, map, iter.link->key.p, + iter.link->value.c - 'a'); + } + } +} + +void +sort_edges(struct dvec *edges) +{ + struct keypath tmp, *a, *b; + size_t i, k; + + if (!edges->len) return; + for (i = 0; i < edges->len - 1; i++) { + for (k = 0; k < edges->len - 1; k++) { + a = dvec_at(edges, k); + b = dvec_at(edges, k + 1); + if (a->dist > b->dist) { + memcpy(&tmp, b, sizeof(struct keypath)); + memcpy(b, a, sizeof(struct keypath)); + memcpy(a, &tmp, sizeof(struct keypath)); + } + } + } +} + +struct prog * +update_progs(struct dvec *progs, size_t dist, uint32_t acquired) +{ + struct prog *prog; + + for (DVEC_ITER(progs, prog)) { + if (bit_superset(acquired, prog->acquired)) { + if (dist < prog->dist) { + prog->acquired = acquired; + prog->dist = dist; + return prog; + } else if (dist == prog->dist) { + if (acquired > prog->acquired) { + prog->acquired = acquired; + return prog; + } else { + return NULL; + } + } + } + } + + prog = dvec_add_slot(progs); + prog->dist = dist; + prog->acquired = acquired; + + return prog; +} + +struct state * +state_alloc(const struct vec2i *pos, size_t dist, uint32_t acquired) +{ + struct state *state; + + state = malloc(sizeof(struct state)); + vec2i_set(&state->pos, pos); + state->dist = dist; + state->acquired = acquired; + state->link = LIST_LINK_INIT; + + return state; +} + +bool +state_dist_ordering(struct list_link *_a, struct list_link *_b) +{ + struct state *a, *b; + + a = LIST_UPCAST(_a, struct state, link); + b = LIST_UPCAST(_b, struct state, link); + + return a->dist < b->dist; +} + +void +add_keys(struct list *active, struct hmap *graph, + struct hmap *distmap, struct state *cstate, size_t keycnt) +{ + struct hmap_link *link, **linkp; + struct state state, *nstate; + struct keypath *keypath; + struct dvec *progs; + struct node *node; + struct prog *prog; + struct vec2i *pos; + + link = hmap_get(graph, (struct hmap_key) {.p = &cstate->pos}); + assert(link != NULL); + node = link->value._p; + + for (DVEC_ITER(&node->edges, keypath)) { + if (cstate->acquired & keypath->keymask) + continue; + + if (!bit_superset(cstate->acquired, keypath->required)) + continue; + + vec2i_set(&state.pos, &keypath->pos); + state.dist = cstate->dist + keypath->dist; + state.acquired = cstate->acquired | keypath->keymask; + + linkp = hmap_link_pos(distmap, (struct hmap_key) {.p = &state.pos}); + if (*linkp) { + progs = (*linkp)->value._p; + } else { + pos = memdup(&state.pos, sizeof(struct vec2i)); + dvec_alloc(&progs, sizeof(struct prog), 1, + &stdlib_strict_heap_allocator); + hmap_link_alloc(distmap, linkp, + (struct hmap_key) {.p = pos}, + (struct hmap_val) {.p = progs}); + } + + if ((prog = update_progs(progs, state.dist, state.acquired))) { + aoc_debug("updated prog %i %i %032b %032b\n", + prog->dist, state.dist, prog->acquired, state.acquired); + nstate = state_alloc(&state.pos, state.dist, state.acquired); + list_insert_sorted(active, &nstate->link, false, + state_dist_ordering); + } + } +} + +size_t +min_dist_all_keys(struct hmap *graph, const struct vec2i *start, size_t keycnt) +{ + struct list_link *list_link; + struct hmap_link *hmap_link; + struct hmap_iter iter; + struct hmap distmap; + struct list active; + struct state *cstate; + struct dvec *progs; + struct prog *prog; + struct node *node; + struct vec2i *pos; + + /* + * - dijkstra from starting node + * - only move over edges that we collected keys for + * - distmap resolves to dvec of progs + * - only save new distance if less than all known progs + * and/or we have a superset of previous keys + * - if we reach key and have, save mindist + */ + + list_init(&active); + hmap_init(&distmap, 256, vec2i_hmap_hash, vec2i_hmap_keycmp, + &stdlib_strict_heap_allocator); + + cstate = state_alloc(start, 0, 0); + list_push_back(&active, &cstate->link); + + pos = memdup(start, sizeof(struct vec2i)); + dvec_alloc(&progs, sizeof(struct prog), 1, + &stdlib_strict_heap_allocator); + prog = dvec_add_slot(progs); + prog->acquired = 0; + prog->dist = 0; + hmap_add(&distmap, (struct hmap_key) {.p = pos}, + (struct hmap_val) {.p = progs}); + + for (HMAP_ITER(graph, &iter)) { + node = iter.link->value._p; + sort_edges(&node->edges); + } + + while (!list_empty(&active)) { + list_link = list_front(&active); + cstate = LIST_UPCAST(list_link, struct state, link); + if (cstate->acquired == (1U << keycnt) - 1) + return cstate->dist; + + list_link_pop(list_link); + + hmap_link = hmap_get(&distmap, (struct hmap_key) {.p = &cstate->pos}); + assert(link != NULL); + for (DVEC_ITER(hmap_link->value.p, prog)) { + if (prog->dist < cstate->dist) { + if (bit_superset(prog->acquired, + cstate->acquired)) + break; + } else if (prog->dist == cstate->dist) { + if (bit_proper_superset(prog->acquired, + cstate->acquired)) + break; + } + } + if (prog) { + free(cstate); + continue; + } + + aoc_debug("active %02li %02li %032b %lu\n", + cstate->pos.x, cstate->pos.y, + cstate->acquired, cstate->dist); + + add_keys(&active, graph, &distmap, cstate, keycnt); + + free(cstate); + } + + for (HMAP_ITER(&distmap, &iter)) { + free(iter.link->key._p); + dvec_free(iter.link->value._p); + } + hmap_deinit(&distmap); + list_free_items(&active, free, LIST_OFFSET(struct state, link)); + + return 0; +} + +void +write_map(struct hmap *map, ssize_t x, ssize_t y, char c) +{ + struct hmap_link *hmap_link; + struct vec2i pos; + + vec2i_setv(&pos, x, y); + hmap_link = hmap_get(map, (struct hmap_key) {.p = &pos}); + assert(hmap_link); + hmap_link->value.c = c; +} + +struct state_multi * +state_multi_alloc(const struct vec2i *pos, size_t dist, uint32_t acquired) +{ + struct state_multi *state; + + state = malloc(sizeof(struct state_multi)); + memcpy(state->pos, pos, 4 * sizeof(struct vec2i)); + state->dist = dist; + state->acquired = acquired; + state->link = LIST_LINK_INIT; + + return state; +} + +bool +state_multi_dist_ordering(struct list_link *_a, struct list_link *_b) +{ + struct state_multi *a, *b; + + a = LIST_UPCAST(_a, struct state_multi, link); + b = LIST_UPCAST(_b, struct state_multi, link); + + return a->dist < b->dist; +} + +void +add_keys_multi(struct list *active, struct hmap *graphs, + struct hmap *distmap, struct state_multi *cstate, size_t keycnt) +{ + struct hmap_link *link, **linkp; + struct state_multi state, *nstate; + struct keypath *keypath; + struct dvec *progs; + struct node *node; + struct prog *prog; + struct vec2i *pos; + size_t i; + + for (i = 0; i < 4; i++) { + link = hmap_get(&graphs[i], (struct hmap_key) {.p = &cstate->pos[i]}); + assert(link != NULL); + node = link->value._p; + + for (DVEC_ITER(&node->edges, keypath)) { + if (cstate->acquired & keypath->keymask) + continue; + + if (!bit_subset(keypath->required, cstate->acquired)) + continue; + + memcpy(state.pos, cstate->pos, 4 * sizeof(struct vec2i)); + vec2i_set(&state.pos[i], &keypath->pos); + state.dist = cstate->dist + keypath->dist; + state.acquired = cstate->acquired | keypath->keymask; + + linkp = hmap_link_pos(distmap, (struct hmap_key) {.p = state.pos}); + if (*linkp) { + aoc_debug("old\n"); + progs = (*linkp)->value._p; + } else { + pos = memdup(state.pos, sizeof(struct vec2i) * 4); + dvec_alloc(&progs, sizeof(struct prog), 1, + &stdlib_strict_heap_allocator); + hmap_link_alloc(distmap, linkp, + (struct hmap_key) {.p = pos}, + (struct hmap_val) {.p = progs}); + } + + if ((prog = update_progs(progs, state.dist, state.acquired))) { + aoc_debug("update prog %li,%li %li,%li %li,%li %li,%li " + "%i %i %032b %032b\n", + state.pos[0].x, state.pos[0].y, + state.pos[1].x, state.pos[1].y, + state.pos[2].x, state.pos[2].y, + state.pos[3].x, state.pos[3].y, + prog->dist, state.dist, + prog->acquired, state.acquired); + nstate = state_multi_alloc(state.pos, state.dist, state.acquired); + list_insert_sorted(active, &nstate->link, + false, state_multi_dist_ordering); + } + } + } +} + +size_t +min_dist_all_keys_multi(struct hmap *graphs, const struct vec2i *starts, size_t keycnt) +{ + struct list_link *list_link; + struct hmap_link *hmap_link; + struct hmap_iter hmap_iter; + struct hmap distmap; + struct list active; + struct state_multi *cstate; + struct dvec *progs; + struct prog *prog; + struct node *node; + struct vec2i *pos; + size_t i; + + /* + * - dijkstra from starting node + * - only move over edges that we collected keys for + * - distmap resolves to dvec of progs + * - only save new distance if less than all known progs + * and/or we have a superset of previous keys + * - if we reach key and have, save mindist + */ + + list_init(&active); + hmap_init(&distmap, 256, vec2i_multi_hmap_hash, vec2i_multi_hmap_keycmp, + &stdlib_strict_heap_allocator); + + cstate = state_multi_alloc(starts, 0, 0); + list_push_back(&active, &cstate->link); + + pos = memdup(starts, sizeof(struct vec2i) * 4); + dvec_alloc(&progs, sizeof(struct prog), 1, + &stdlib_strict_heap_allocator); + prog = dvec_add_slot(progs); + prog->acquired = 0; + prog->dist = 0; + hmap_add(&distmap, (struct hmap_key) {.p = pos}, + (struct hmap_val) {.p = progs}); + + for (i = 0; i < 4; i++) { + for (HMAP_ITER(&graphs[i], &hmap_iter)) { + node = hmap_iter.link->value._p; + sort_edges(&node->edges); + } + } + + while (!list_empty(&active)) { + list_link = list_front(&active); + cstate = LIST_UPCAST(list_link, struct state_multi, link); + if (cstate->acquired == (1U << keycnt) - 1) + return cstate->dist; + + list_link_pop(list_link); + + hmap_link = hmap_get(&distmap, (struct hmap_key) {.p = cstate->pos}); + assert(hmap_link != NULL); + for (DVEC_ITER(hmap_link->value.p, prog)) { + if (prog->dist < cstate->dist) { + if (bit_superset(prog->acquired, + cstate->acquired)) + break; + } else if (prog->dist == cstate->dist) { + if (bit_proper_superset(prog->acquired, + cstate->acquired)) + break; + } + } + if (prog) { + free(cstate); + continue; + } + + aoc_debug("active %li,%li %li,%li %li,%li %li,%li %032b %li\n", + cstate->pos[0].x, cstate->pos[0].y, + cstate->pos[1].x, cstate->pos[1].y, + cstate->pos[2].x, cstate->pos[2].y, + cstate->pos[3].x, cstate->pos[3].y, + cstate->acquired, cstate->dist); + + add_keys_multi(&active, graphs, &distmap, cstate, keycnt); + + free(cstate); + } + + for (HMAP_ITER(&distmap, &hmap_iter)) { + free(hmap_iter.link->key._p); + dvec_free(hmap_iter.link->value._p); + } + hmap_deinit(&distmap); + list_free_items(&active, free, LIST_OFFSET(struct state_multi, link)); + + return 0; +} + +void +part1(void) +{ + struct hmap_iter iter; + struct hmap map; + struct hmap graph; + struct vec2i start; + struct node *node; + size_t keycnt; + size_t answer; + + signal(SIGUSR1, exit); + + hmap_init(&map, 256, vec2i_hmap_hash, vec2i_hmap_keycmp, + &stdlib_strict_heap_allocator); + hmap_init(&graph, 32, vec2i_hmap_hash, vec2i_hmap_keycmp, + &stdlib_strict_heap_allocator); + + parse_input(&map, &start, &keycnt); + + gen_graph(&graph, &map, &start, keycnt); + + answer = min_dist_all_keys(&graph, &start, keycnt); + aoc.answer = aprintf("%lu", answer); + aoc.solution = "5402"; + + for (HMAP_ITER(&graph, &iter)) { + free(iter.link->key._p); + node = iter.link->value._p; + dvec_deinit(&node->edges); + free(node); + } + hmap_deinit(&graph); + + for (HMAP_ITER(&map, &iter)) { + free(iter.link->key._p); + } + hmap_deinit(&map); +} + +void +part2(void) +{ + struct hmap_iter hmap_iter; + struct hmap map; + struct hmap graphs[4]; + struct vec2i center; + struct vec2i starts[4]; + struct node *node; + size_t keycnt; + size_t answer; + size_t i; + + signal(SIGUSR1, exit); + + hmap_init(&map, 256, vec2i_hmap_hash, vec2i_hmap_keycmp, + &stdlib_strict_heap_allocator); + + parse_input(&map, &center, &keycnt); + + write_map(&map, center.x - 1, center.y, '#'); + write_map(&map, center.x + 1, center.y, '#'); + write_map(&map, center.x, center.y - 1, '#'); + write_map(&map, center.x, center.y + 1, '#'); + write_map(&map, center.x, center.y, '#'); + + for (i = 0; i < 4; i++) { + vec2i_add(&starts[i], &center, &dirs[i]); + vec2i_add(&starts[i], &starts[i], &dirs[(i+1)%4]); + hmap_init(&graphs[i], 32, vec2i_hmap_hash, vec2i_hmap_keycmp, + &stdlib_strict_heap_allocator); + gen_graph(&graphs[i], &map, &starts[i], keycnt); + } + + answer = min_dist_all_keys_multi(graphs, starts, keycnt); + aoc.answer = aprintf("%lu", answer); + aoc.solution = "2138"; + + for (i = 0; i < 4; i++) { + for (HMAP_ITER(&graphs[i], &hmap_iter)) { + free(hmap_iter.link->key._p); + node = hmap_iter.link->value._p; + dvec_deinit(&node->edges); + free(node); + } + hmap_deinit(&graphs[i]); + } + + for (HMAP_ITER(&map, &hmap_iter)) { + free(hmap_iter.link->key._p); + } + hmap_deinit(&map); +} diff --git a/18/part1 b/18/part1 @@ -0,0 +1,117 @@ +--- Day 18: Many-Worlds Interpretation --- + +As you approach Neptune, a planetary security system detects you and activates a giant tractor beam +on Triton! You have no choice but to land. + +A scan of the local area reveals only one interesting feature: a massive underground vault. You +generate a map of the tunnels (your puzzle input). The tunnels are too narrow to move diagonally. + +Only one entrance (marked @) is present among the open passages (marked .) and stone walls (#), but +you also detect an assortment of keys (shown as lowercase letters) and doors (shown as uppercase +letters). Keys of a given letter open the door of the same letter: a opens A, b opens B, and so on. +You aren't sure which key you need to disable the tractor beam, so you'll need to collect all of +them. + +For example, suppose you have the following map: + +######### +#b.A.@.a# +######### + +Starting from the entrance (@), you can only access a large door (A) and a key (a). Moving toward +the door doesn't help you, but you can move 2 steps to collect the key, unlocking A in the process: + +######### +#b.....@# +######### + +Then, you can move 6 steps to collect the only other key, b: + +######### +#@......# +######### + +So, collecting every key took a total of 8 steps. + +Here is a larger example: + +######################## +#f.D.E.e.C.b.A.@.a.B.c.# +######################.# +#d.....................# +######################## + +The only reasonable move is to take key a and unlock door A: + +######################## +#f.D.E.e.C.b.....@.B.c.# +######################.# +#d.....................# +######################## + +Then, do the same with key b: + +######################## +#f.D.E.e.C.@.........c.# +######################.# +#d.....................# +######################## + +...and the same with key c: + +######################## +#f.D.E.e.............@.# +######################.# +#d.....................# +######################## + +Now, you have a choice between keys d and e. While key e is closer, collecting it now would be +slower in the long run than collecting key d first, so that's the best choice: + +######################## +#f...E.e...............# +######################.# +#@.....................# +######################## + +Finally, collect key e to unlock door E, then collect key f, taking a grand total of 86 steps. + +Here are a few more examples: + + + - ######################## +#...............b.C.D.f# +#.###################### +#.....@.a.B.c.d.A.e.F.g# +######################## + +Shortest path is 132 steps: b, a, c, d, f, e, g + + + - ################# +#i.G..c...e..H.p# +########.######## +#j.A..b...f..D.o# +########@######## +#k.E..a...g..B.n# +########.######## +#l.F..d...h..C.m# +################# + +Shortest paths are 136 steps;one is: a, f, b, j, g, n, h, d, l, o, e, p, c, i, k, m + + + - ######################## +#@..............ac.GI.b# +###d#e#f################ +###A#B#C################ +###g#h#i################ +######################## + +Shortest paths are 81 steps; one is: a, c, f, i, d, g, b, e, h + + + +How many steps is the shortest path that collects all of the keys? + + diff --git a/18/part2 b/18/part2 @@ -0,0 +1,184 @@ +--- Part Two --- + +You arrive at the vault only to discover that there is not one vault, but four - each with its own +entrance. + +On your map, find the area in the middle that looks like this: + +... +.@. +... + +Update your map to instead use the correct data: + +@#@ +### +@#@ + +This change will split your map into four separate sections, each with its own entrance: + +####### ####### +#a.#Cd# #a.#Cd# +##...## ##@#@## +##.@.## --> ####### +##...## ##@#@## +#cB#Ab# #cB#Ab# +####### ####### + +Because some of the keys are for doors in other vaults, it would take much too long to collect all +of the keys by yourself. Instead, you deploy four remote-controlled robots. Each starts at one of +the entrances (@). + +Your goal is still to collect all of the keys in the fewest steps, but now, each robot has its own +position and can move independently. You can only remotely control a single robot at a time. +Collecting a key instantly unlocks any corresponding doors, regardless of the vault in which the key +or door is found. + +For example, in the map above, the top-left robot first collects key a, unlocking door A in the +bottom-right vault: + +####### +#@.#Cd# +##.#@## +####### +##@#@## +#cB#.b# +####### + +Then, the bottom-right robot collects key b, unlocking door B in the bottom-left vault: + +####### +#@.#Cd# +##.#@## +####### +##@#.## +#c.#.@# +####### + +Then, the bottom-left robot collects key c: + +####### +#@.#.d# +##.#@## +####### +##.#.## +#@.#.@# +####### + +Finally, the top-right robot collects key d: + +####### +#@.#.@# +##.#.## +####### +##.#.## +#@.#.@# +####### + +In this example, it only took 8 steps to collect all of the keys. + +Sometimes, multiple robots might have keys available, or a robot might have to wait for multiple +keys to be collected: + +############### +#d.ABC.#.....a# +######@#@###### +############### +######@#@###### +#b.....#.....c# +############### + +First, the top-right, bottom-left, and bottom-right robots take turns collecting keys a, b, and c, a +total of 6 + 6 + 6 = 18 steps. Then, the top-left robot can access key d, spending another 6 steps; +collecting all of the keys here takes a minimum of 24 steps. + +Here's a more complex example: + +############# +#DcBa.#.GhKl# +#.###@#@#I### +#e#d#####j#k# +###C#@#@###J# +#fEbA.#.FgHi# +############# + + + - Top-left robot collects key a. + + - Bottom-left robot collects key b. + + - Top-left robot collects key c. + + - Bottom-left robot collects key d. + + - Top-left robot collects key e. + + - Bottom-left robot collects key f. + + - Bottom-right robot collects key g. + + - Top-right robot collects key h. + + - Bottom-right robot collects key i. + + - Top-right robot collects key j. + + - Bottom-right robot collects key k. + + - Top-right robot collects key l. + + +In the above example, the fewest steps to collect all of the keys is 32. + +Here's an example with more choices: + +############# +#g#f.D#..h#l# +#F###e#E###.# +#dCba@#@BcIJ# +############# +#nK.L@#@G...# +#M###N#H###.# +#o#m..#i#jk.# +############# + +One solution with the fewest steps is: + + + - Top-left robot collects key e. + + - Top-right robot collects key h. + + - Bottom-right robot collects key i. + + - Top-left robot collects key a. + + - Top-left robot collects key b. + + - Top-right robot collects key c. + + - Top-left robot collects key d. + + - Top-left robot collects key f. + + - Top-left robot collects key g. + + - Bottom-right robot collects key k. + + - Bottom-right robot collects key j. + + - Top-right robot collects key l. + + - Bottom-left robot collects key n. + + - Bottom-left robot collects key m. + + - Bottom-left robot collects key o. + + +This example requires at least 72 steps to collect all keys. + +After updating your map and using the remote-controlled robots, what is the fewest steps necessary +to collect all of the keys? + + diff --git a/18/test1 b/18/test1 @@ -0,0 +1,6 @@ +######################## +#@..............ac.GI.b# +###d#e#f################ +###A#B#C################ +###g#h#i################ +######################## diff --git a/18/test2 b/18/test2 @@ -0,0 +1,5 @@ +######################## +#f.D.E.e.C.b.A.@.a.B.c.# +######################.# +#d.....................# +######################## diff --git a/18/test3 b/18/test3 @@ -0,0 +1,7 @@ +####### +#a.#Cd# +##...## +##.@.## +##...## +#cB#Ab# +####### diff --git a/Makefile b/Makefile @@ -1,14 +1,21 @@ CFLAGS = -I lib/libdvec/include -I lib/libhmap/include CFLAGS += -I lib/libmaxint/include -I lib/liballoc/include +CFLAGS += -I lib/liblist/include + CFLAGS += -Wunused-variable -Wunused-function -Wformat CFLAGS += -Wconversion -Wsign-compare CFLAGS += -I common -g -Werror ifeq "$(DEBUG)" "1" +CFLAGS += -DLIBDVEC_ASSERT_ARGS -DLIBDVEC_ASSERT_ALLOC LIBDVEC_ENV = DEBUG=1 ASSERT_ALLOC=1 ASSERT_ARGS=1 -LIBHMAP_ENV = DEBUG=1 +CFLAGS += -DLIBHMAP_ASSERT_ARGS -DLIBHMAP_ASSERT_ALLOC +LIBHMAP_ENV = DEBUG=1 ASSERT_ALLOC=1 ASSERT_ARGS=1 LIBMAXINT_ENV = DEBUG=1 LIBALLOC_ENV = DEBUG=1 +CFLAGS += -DLIBLIST_ASSERT_ARGS +LIBLIST_ENV = DEBUG=1 +LIBPQ_ENV = DEBUG=1 endif DAYS = $(shell seq 1 24 | xargs printf "%02i\n") @@ -29,6 +36,12 @@ lib/libmaxint/build/libmaxint.a: lib/liballoc/build/liballoc.a: make -C lib/liballoc build/liballoc.a $(LIBALLOC_ENV) +lib/liblist/build/liblist.a: + make -C lib/liblist build/liblist.a $(LIBLIST_ENV) + +lib/libpq/build/libpq.a: + make -C lib/libpq build/libpq.a $(LIBPQ_ENV) + define make-day all:: $1/main run:: $1/run @@ -48,7 +61,7 @@ clean: cleanall: clean make -C lib/libdvec clean - make -C lib/libhashmap clean + make -C lib/libhmap clean make -C lib/libmaxint clean make -C lib/liballoc clean diff --git a/common/dvec_s.h b/common/dvec_s.h @@ -0,0 +1,29 @@ +#include "dvec.h" + +#include <assert.h> + +static inline void * +dvec_add_slots(struct dvec *dvec, size_t count) +{ + assert(!dvec_add(dvec, dvec->len, count)); + + return dvec->data + (dvec->len - count) * dvec->dsize; +} + +static inline void * +dvec_add_slot(struct dvec *dvec) +{ + return dvec_add_slots(dvec, 1); +} + +static inline void +dvec_rm_slots(struct dvec *dvec, void *slot, size_t count) +{ + dvec_remove(dvec, ((size_t) (slot - dvec->data)) / dvec->dsize, count); +} + +static inline void +dvec_rm_slot(struct dvec *dvec, void *slot) +{ + dvec_rm_slots(dvec, slot, 1); +} diff --git a/common/icc.c b/common/icc.c @@ -2,7 +2,7 @@ #include "allocator.h" #include "aoc.h" #include "util.h" -#include "dvec.h" +#include "dvec_s.h" #include <assert.h> #include <stdbool.h> @@ -76,14 +76,12 @@ icc_parse_inst(struct icc *icc, const char *str, size_t len) const char *pos, *end; char buf[256]; int val, *slot; - int rc; pos = str; end = str + len; while (readtok(buf, sizeof(buf), ',', &pos, end)) { val = (int) parsei64(buf); - slot = dvec_add_slot(&icc->instructions, &rc); - assert(slot); + slot = dvec_add_slot(&icc->instructions); *slot = val; } } diff --git a/common/main.c b/common/main.c @@ -8,6 +8,7 @@ struct aoc aoc; int main(int argc, const char **argv) { + const char *envvar; FILE *f; if (argc <= 1) { @@ -18,7 +19,8 @@ main(int argc, const char **argv) aoc.part = atoi(argv[1]); aoc.args = &argv[2]; - aoc.debug = getenv("AOC_DEBUG") != NULL; + envvar = getenv("AOC_DEBUG"); + aoc.debug = envvar ? atoi(envvar) : 0; /* parse user input */ aoc.inputfile = getenv("AOC_INPUT"); diff --git a/common/util.c b/common/util.c @@ -106,7 +106,7 @@ apprintf(char *str, const char *fmtstr, ...) } void * -memdup(void *data, size_t size) +memdup(const void *data, size_t size) { void *new; diff --git a/common/util.h b/common/util.h @@ -26,7 +26,7 @@ int64_t parsei64(const char *str); char *aprintf(const char *fmtstr, ...); char *apprintf(char *alloc, const char *fmtstr, ...); -void *memdup(void *data, size_t size); +void *memdup(const void *data, size_t size); void readall(FILE *f, void **data, size_t *read); diff --git a/lib/liblist b/lib/liblist @@ -0,0 +1 @@ +Subproject commit ff57ed0d47a21614c0fd3f7f3be9d7a0233958e7 diff --git a/lib/libpq b/lib/libpq @@ -0,0 +1 @@ +Subproject commit 46465417e54567584f6d26c157e7ae345e01088e