aoc-2019-c

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

commit 019fd07126c76913fdf62ee2bc1813e666ddf25c
parent 976f5e7b4682dc7f1735b9fd11dd4d7c56f5c8df
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sat, 25 Mar 2023 19:25:58 +0100

Add day 17

Diffstat:
A17/info.mk | 4++++
A17/input | 2++
A17/main.c | 606+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A17/part1 | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A17/part2 | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcommon/iccmp.c | 3++-
Mcommon/main.c | 10++++++----
Mcommon/util.c | 26++++++++++++++++++++++++++
Mcommon/util.h | 3++-
Mcommon/vec.h | 20+++++++++++++++++++-
10 files changed, 845 insertions(+), 7 deletions(-)

diff --git a/17/info.mk b/17/info.mk @@ -0,0 +1,4 @@ +17_SRC = 17/main.c common/main.c common/iccmp.c common/aoc.c common/util.c +17_SRC += lib/libdvec/build/libdvec.a lib/liballoc/build/liballoc.a +17_SRC += lib/libmaxint/build/libmaxint.a lib/libhmap/build/libhmap.a +17_HDR = common/aoc.h common/iccmp.h common/util.h diff --git a/17/input b/17/input @@ -0,0 +1,2 @@ +1,330,331,332,109,2538,1102,1182,1,15,1102,1407,1,24,1001,0,0,570,1006,570,36,1002,571,1,0,1001,570,-1,570,1001,24,1,24,1106,0,18,1008,571,0,571,1001,15,1,15,1008,15,1407,570,1006,570,14,21101,58,0,0,1106,0,786,1006,332,62,99,21101,0,333,1,21102,73,1,0,1106,0,579,1101,0,0,572,1101,0,0,573,3,574,101,1,573,573,1007,574,65,570,1005,570,151,107,67,574,570,1005,570,151,1001,574,-64,574,1002,574,-1,574,1001,572,1,572,1007,572,11,570,1006,570,165,101,1182,572,127,102,1,574,0,3,574,101,1,573,573,1008,574,10,570,1005,570,189,1008,574,44,570,1006,570,158,1106,0,81,21102,1,340,1,1106,0,177,21102,477,1,1,1106,0,177,21102,1,514,1,21101,176,0,0,1105,1,579,99,21102,184,1,0,1106,0,579,4,574,104,10,99,1007,573,22,570,1006,570,165,101,0,572,1182,21101,375,0,1,21102,211,1,0,1106,0,579,21101,1182,11,1,21102,222,1,0,1106,0,979,21101,0,388,1,21101,233,0,0,1105,1,579,21101,1182,22,1,21101,0,244,0,1106,0,979,21101,0,401,1,21102,1,255,0,1106,0,579,21101,1182,33,1,21101,0,266,0,1106,0,979,21101,414,0,1,21101,277,0,0,1105,1,579,3,575,1008,575,89,570,1008,575,121,575,1,575,570,575,3,574,1008,574,10,570,1006,570,291,104,10,21102,1182,1,1,21101,313,0,0,1106,0,622,1005,575,327,1101,0,1,575,21102,1,327,0,1106,0,786,4,438,99,0,1,1,6,77,97,105,110,58,10,33,10,69,120,112,101,99,116,101,100,32,102,117,110,99,116,105,111,110,32,110,97,109,101,32,98,117,116,32,103,111,116,58,32,0,12,70,117,110,99,116,105,111,110,32,65,58,10,12,70,117,110,99,116,105,111,110,32,66,58,10,12,70,117,110,99,116,105,111,110,32,67,58,10,23,67,111,110,116,105,110,117,111,117,115,32,118,105,100,101,111,32,102,101,101,100,63,10,0,37,10,69,120,112,101,99,116,101,100,32,82,44,32,76,44,32,111,114,32,100,105,115,116,97,110,99,101,32,98,117,116,32,103,111,116,58,32,36,10,69,120,112,101,99,116,101,100,32,99,111,109,109,97,32,111,114,32,110,101,119,108,105,110,101,32,98,117,116,32,103,111,116,58,32,43,10,68,101,102,105,110,105,116,105,111,110,115,32,109,97,121,32,98,101,32,97,116,32,109,111,115,116,32,50,48,32,99,104,97,114,97,99,116,101,114,115,33,10,94,62,118,60,0,1,0,-1,-1,0,1,0,0,0,0,0,0,1,2,12,0,109,4,1202,-3,1,586,21001,0,0,-1,22101,1,-3,-3,21101,0,0,-2,2208,-2,-1,570,1005,570,617,2201,-3,-2,609,4,0,21201,-2,1,-2,1106,0,597,109,-4,2106,0,0,109,5,1201,-4,0,629,21002,0,1,-2,22101,1,-4,-4,21102,1,0,-3,2208,-3,-2,570,1005,570,781,2201,-4,-3,653,20102,1,0,-1,1208,-1,-4,570,1005,570,709,1208,-1,-5,570,1005,570,734,1207,-1,0,570,1005,570,759,1206,-1,774,1001,578,562,684,1,0,576,576,1001,578,566,692,1,0,577,577,21101,0,702,0,1105,1,786,21201,-1,-1,-1,1106,0,676,1001,578,1,578,1008,578,4,570,1006,570,724,1001,578,-4,578,21102,1,731,0,1105,1,786,1105,1,774,1001,578,-1,578,1008,578,-1,570,1006,570,749,1001,578,4,578,21101,756,0,0,1106,0,786,1106,0,774,21202,-1,-11,1,22101,1182,1,1,21102,1,774,0,1106,0,622,21201,-3,1,-3,1106,0,640,109,-5,2106,0,0,109,7,1005,575,802,20101,0,576,-6,20101,0,577,-5,1105,1,814,21102,1,0,-1,21102,1,0,-5,21102,1,0,-6,20208,-6,576,-2,208,-5,577,570,22002,570,-2,-2,21202,-5,29,-3,22201,-6,-3,-3,22101,1407,-3,-3,2102,1,-3,843,1005,0,863,21202,-2,42,-4,22101,46,-4,-4,1206,-2,924,21101,1,0,-1,1106,0,924,1205,-2,873,21101,0,35,-4,1105,1,924,1202,-3,1,878,1008,0,1,570,1006,570,916,1001,374,1,374,1201,-3,0,895,1102,2,1,0,1201,-3,0,902,1001,438,0,438,2202,-6,-5,570,1,570,374,570,1,570,438,438,1001,578,558,922,20102,1,0,-4,1006,575,959,204,-4,22101,1,-6,-6,1208,-6,29,570,1006,570,814,104,10,22101,1,-5,-5,1208,-5,39,570,1006,570,810,104,10,1206,-1,974,99,1206,-1,974,1102,1,1,575,21101,973,0,0,1106,0,786,99,109,-7,2106,0,0,109,6,21102,0,1,-4,21101,0,0,-3,203,-2,22101,1,-3,-3,21208,-2,82,-1,1205,-1,1030,21208,-2,76,-1,1205,-1,1037,21207,-2,48,-1,1205,-1,1124,22107,57,-2,-1,1205,-1,1124,21201,-2,-48,-2,1105,1,1041,21102,1,-4,-2,1106,0,1041,21101,0,-5,-2,21201,-4,1,-4,21207,-4,11,-1,1206,-1,1138,2201,-5,-4,1059,1202,-2,1,0,203,-2,22101,1,-3,-3,21207,-2,48,-1,1205,-1,1107,22107,57,-2,-1,1205,-1,1107,21201,-2,-48,-2,2201,-5,-4,1090,20102,10,0,-1,22201,-2,-1,-2,2201,-5,-4,1103,2101,0,-2,0,1105,1,1060,21208,-2,10,-1,1205,-1,1162,21208,-2,44,-1,1206,-1,1131,1105,1,989,21101,0,439,1,1106,0,1150,21102,477,1,1,1106,0,1150,21101,0,514,1,21101,0,1149,0,1105,1,579,99,21101,1157,0,0,1106,0,579,204,-2,104,10,99,21207,-3,22,-1,1206,-1,1138,1201,-5,0,1176,1201,-4,0,0,109,-6,2106,0,0,0,5,24,1,3,1,24,1,3,1,24,1,3,1,24,1,3,5,9,12,7,1,9,1,9,2,7,1,9,1,9,2,7,1,9,1,9,2,7,1,9,1,5,6,7,1,9,1,5,1,4,11,7,1,5,1,12,1,1,1,7,1,5,1,6,11,1,11,12,1,1,1,1,1,1,1,3,1,18,11,20,1,1,1,1,1,22,11,18,1,1,1,1,1,1,1,3,1,16,11,1,1,16,1,1,1,1,1,1,1,1,1,1,1,1,1,10,11,1,1,1,1,1,1,1,1,10,1,5,1,1,1,3,1,1,1,1,1,1,1,10,1,5,7,1,5,10,1,7,1,7,1,12,1,7,1,7,1,20,1,7,1,20,11,26,1,1,1,26,5,26,1,1,1,26,1,1,1,26,1,1,1,18,5,3,1,1,1,18,1,3,1,3,1,1,1,18,1,3,1,3,1,1,1,18,1,3,1,3,1,1,1,18,1,3,5,1,1,18,1,9,1,18,11,8 + diff --git a/17/main.c b/17/main.c @@ -0,0 +1,606 @@ +#include "allocator.h" +#include "util.h" +#include "aoc.h" +#include "iccmp.h" +#include "hmap.h" +#include "vec.h" + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> + +enum { + DIR_NORTH, + DIR_EAST, + DIR_SOUTH, + DIR_WEST, +}; + +struct path { + struct vec2i start, end; +}; + +static const struct vec2i dirs[] = { + [DIR_NORTH] = { 0, -1 }, + [DIR_EAST] = { 1, 0 }, + [DIR_SOUTH] = { 0, 1 }, + [DIR_WEST] = { -1, 0 }, +}; + +bool +vec2i_hmap_keycmp(struct hmap_key k1, struct hmap_key k2) +{ + const struct vec2i *v1 = k1.p, *v2 = k2.p; + + return v1->x == v2->x && v1->y == v2->y; +} + +uint32_t +vec2i_hmap_hash(struct hmap_key key) +{ + const struct vec2i *v = key.p; + + return (uint32_t) (v->x << 16) ^ (uint32_t) v->y; +} + +int +dir_from_char(char c) +{ + switch (c) { + case '^': + return DIR_NORTH; + case '>': + return DIR_EAST; + case 'v': + return DIR_SOUTH; + case '<': + return DIR_WEST; + default: + return -1; + } +} + +int +dir_from_path(struct path *path) +{ + if (path->end.x > path->start.x) + return DIR_EAST; + else if (path->end.x < path->start.x) + return DIR_WEST; + else if (path->end.y > path->start.y) + return DIR_SOUTH; + else if (path->end.y < path->start.y) + return DIR_NORTH; + assert(0); +} + +void +receive_map(struct icc *icc, struct hmap *map, + struct vec2i *min, struct vec2i *max, + struct vec2i *dpos, int *ddir) +{ + struct hmap_iter iter; + struct vec2i pos; + void *key; + char c; + + for (HMAP_ITER(map, &iter)) + free(iter.link->key._p); + hmap_clear(map); + + vec2i_setv(min, 0, 0); + vec2i_setv(max, 0, 0); + vec2i_setv(&pos, 0, 0); + while (icc->state != ICC_HALT) { + icc_step_inst(icc); + if (icc->state == ICC_INPUT) + break; + if (icc->state == ICC_OUTPUT) { + c = (char) mi_cast_ul(&icc->out); + if (c == '\n') { + if (pos.x == 0) break; + pos.y += 1; + pos.x = 0; + } else { + if (dir_from_char(c) >= 0) { + vec2i_set(dpos, &pos); + *ddir = dir_from_char(c); + } + key = memdup(&pos, sizeof(struct vec2i)); + hmap_add(map, (struct hmap_key) {.p = key}, + (struct hmap_val) {.c = c}); + if (pos.x > max->x) max->x = pos.x; + if (pos.y > max->y) max->y = pos.y; + if (pos.x < min->x) min->x = pos.x; + if (pos.y < min->y) min->y = pos.y; + pos.x += 1; + } + } + } +} + +void +receive_output(struct icc *icc) +{ + aoc_debug("ICC> "); + while (icc->state != ICC_HALT) { + icc_step_inst(icc); + if (icc->state == ICC_INPUT) + break; + if (icc->state == ICC_OUTPUT) + aoc_debug("%c", (char) mi_cast_ul(&icc->out)); + } + assert(icc->state != ICC_HALT); +} + +void +print_map(struct hmap *map, struct vec2i min, struct vec2i max) +{ + struct hmap_link *link; + struct vec2i pos; + + 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++) { + link = hmap_get(map, (struct hmap_key) {.p = &pos}); + if (link) aoc_debug("%c", link->value.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 +find_path_end(struct hmap *map, struct vec2i start, + size_t dir, struct vec2i *end) +{ + struct hmap_link *link; + struct vec2i ppos, pos; + + vec2i_set(&pos, &start); + while (1) { + vec2i_set(&ppos, &pos); + vec2i_add(&pos, &pos, &dirs[dir]); + link = hmap_get(map, (struct hmap_key) {.p = &pos}); + if (!link || link->value.c != '#') + break; + } + + vec2i_set(end, &ppos); +} + +void +add_path(struct hmap *paths, struct path *path) +{ + struct hmap_link **link; + struct dvec *list; + void *key; + + 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)); + } 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)); + hmap_add(paths, + (struct hmap_key) {.p = key}, + (struct hmap_val) {.p = list}); + } +} + +void +gen_paths(struct hmap *paths, struct hmap *map, + struct vec2i min, struct vec2i max) +{ + struct hmap_link *link; + struct vec2i pos, npos; + struct vec2i end; + struct path path; + bool side[4]; + bool endpoint; + size_t i; + + vec2i_setv(&pos, 0, 0); + for (pos.y = min.y; pos.y <= max.y; pos.y++) { + for (pos.x = min.x; pos.x <= max.x; pos.x++) { + link = hmap_get(map, (struct hmap_key) {.p = &pos}); + if (!link || link->value.c == '.') + continue; + + for (i = 0; i < ARRLEN(dirs); i++) { + side[i] = false; + vec2i_add(&npos, &pos, &dirs[i]); + link = hmap_get(map, (struct hmap_key) {.p = &npos}); + side[i] = link ? link->value.c == '#' : false; + } + + endpoint = (side[DIR_NORTH] != side[DIR_SOUTH]); + endpoint |= (side[DIR_WEST] != side[DIR_EAST]); + if (endpoint) { + aoc_debug("node %li %li\n", pos.x, pos.y); + for (i = 0; i < ARRLEN(dirs); i++) { + if (!side[i]) continue; + find_path_end(map, pos, i, &end); + vec2i_set(&path.start, &pos); + vec2i_set(&path.end, &end); + add_path(paths, &path); + } + } + } + } +} + +void +gen_insts(struct dvec *insts, struct hmap *paths, + struct vec2i start, int ddir) +{ + struct hmap_link *link; + struct dvec *list; + struct path *path; + struct vec2i ppos, pos; + char *inst, type; + int cnt, dir, ndir; + bool found; + + dir = ddir; + vec2i_set(&pos, &start); + vec2i_set(&ppos, &pos); + while (1) { + link = hmap_get(paths, (struct hmap_key) {.p = &pos}); + assert(link != NULL); + + found = false; + list = link->value._p; + for (DVEC_ITER(list, &path)) { + if (vec2i_eql(&ppos, &path->end)) + continue; + found = true; + break; + } + + if (!found) break; + + aoc_debug("path %i %i -> %i %i\n", path->start.x, path->start.y, + path->end.x, path->end.y); + + vec2i_set(&ppos, &pos); + vec2i_set(&pos, &path->end); + + ndir = dir_from_path(path); + aoc_debug("ndir %i\n", ndir); + type = ndir > dir ? 'R' : 'L'; + cnt = ABS(ndir - dir); + if (cnt > 2) { + cnt = 4 - cnt; + type = type == 'L' ? 'R' : 'L'; + } + for (; cnt > 0; cnt--) { + inst = dvec_add_slot(insts, NULL); + *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 = (char) -cnt; + } +} + +char * +insts_line(struct dvec *insts) +{ + char *str; + char *c; + + str = NULL; + for (DVEC_ITER(insts, &c)) { + if (str) str = apprintf(str, ","); + if (*c < 0) { + str = apprintf(str, "%i", -*c); + } else { + str = apprintf(str, "%c", *c); + } + } + str = apprintf(str, "\n"); + + return str; +} + +size_t +gen_func(struct dvec *insts, struct dvec *main, + struct dvec *cur, char cc, size_t start, size_t len, + struct dvec *f1, char c1, struct dvec *f2, char c2) +{ + char *inst; + size_t i; + + for (i = start; i < MIN(insts->len, start + len); i++) { + inst = dvec_add_slot(cur, NULL); + *inst = *(char *)dvec_at(insts, i); + } + + inst = dvec_add_slot(main, NULL); + *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 = 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 = 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 = cc; + } else { + break; + } + } + + return i; +} + +void +gen_funcs(struct dvec *insts, struct dvec *main, + struct dvec *fa, struct dvec *fb, struct dvec *fc) +{ + size_t alen, blen, clen; + size_t lens, pos; + char *line; + + lens = 0; + while (lens < 20 * 20 * 20) { + dvec_clear(main); + dvec_clear(fa); + dvec_clear(fb); + dvec_clear(fc); + + lens += 1; + alen = (lens / 20 / 20) % 20; + blen = (lens / 20) % 20; + clen = lens % 20; + if (!alen || !blen || !clen) + continue; + + pos = 0; + pos = gen_func(insts, main, fa, 'A', pos, alen, NULL, 0, NULL, 0); + pos = gen_func(insts, main, fb, 'B', pos, blen, fa, 'A', NULL, 0); + pos = gen_func(insts, main, fc, 'C', pos, clen, fa, 'A', fb, 'B'); + + assert(fa->len == alen); + assert(fb->len == blen); + assert(fc->len == clen); + + if (aoc.debug && main->len > 3) { + line = insts_line(main); + aoc_debug("%2li %2li | %2li %2li %2li %s", + pos, insts->len, alen, blen, clen, line); + free(line); + + line = insts_line(insts); + aoc_debug("%s", line); + free(line); + + line = insts_line(fa); + aoc_debug("%s", line); + free(line); + + line = insts_line(fb); + aoc_debug("%s", line); + free(line); + + line = insts_line(fc); + aoc_debug("%s", line); + free(line); + + aoc_debug("\n"); + } + + if (pos == insts->len && main->len <= 20) + return; + } + assert(0); +} + +void +send_line(struct icc *icc, const char *line) +{ + const char *c; + + c = line; + while (icc->state != ICC_HALT && *c) { + icc_step_inst(icc); + switch (icc->state) { + case ICC_HALT: + case ICC_RUN: + continue; + case ICC_INPUT: + mi_setv(&icc->in, (mi_ul) *c, MI_POS); + c += 1; + break; + default: + receive_output(icc); + assert(0); + } + } + assert(icc->state != ICC_HALT); +} + +void +part1(void) +{ + struct icc icc; + struct hmap map; + struct hmap_link *link; + struct hmap_iter iter; + struct vec2i pos, npos; + struct vec2i min, max; + struct vec2i dpos; + size_t answer; + size_t i; + int ddir; + + icc_init(&icc); + hmap_init(&map, 32, vec2i_hmap_hash, vec2i_hmap_keycmp, + &stdlib_strict_heap_allocator); + + icc_parse_inst(&icc, aoc.input, aoc.input_size); + + receive_map(&icc, &map, &min, &max, &dpos, &ddir); + + if (aoc.debug) + print_map(&map, min, max); + + vec2i_setv(&pos, 0, 0); + answer = 0; + for (pos.y = min.y; pos.y <= max.y; pos.y++) { + for (pos.x = min.x; pos.x <= max.x; pos.x++) { + link = hmap_get(&map, (struct hmap_key) {.p = &pos}); + if (!link || link->value.c == '.') + continue; + for (i = 0; i < ARRLEN(dirs); i++) { + vec2i_add(&npos, &pos, &dirs[i]); + link = hmap_get(&map, (struct hmap_key) {.p = &npos}); + if (!link || link->value.c != '#') + break; + } + if (i == ARRLEN(dirs)) + answer += (size_t) pos.x * (size_t) pos.y; + } + } + + aoc.answer = aprintf("%lu", answer); + aoc.solution = "4112"; + + for (HMAP_ITER(&map, &iter)) + free(iter.link->key._p); + hmap_deinit(&map); + icc_deinit(&icc); +} + +void +part2(void) +{ + struct icc icc; + struct hmap map; + struct hmap paths; + struct dvec insts; + struct dvec main; + struct dvec fa, fb, fc; + struct hmap_iter iter; + struct vec2i min, max; + struct vec2i dpos; + struct maxint mi_addr = MI_INIT; + struct maxint mi_val = MI_INIT; + mi_ul ul_addr, ul_val; + char *line, buf[256]; + int ddir; + + icc_init(&icc); + hmap_init(&map, 32, vec2i_hmap_hash, vec2i_hmap_keycmp, + &stdlib_strict_heap_allocator); + hmap_init(&paths, 32, vec2i_hmap_hash, vec2i_hmap_keycmp, + &stdlib_strict_heap_allocator); + dvec_init(&insts, sizeof(char), 32, &stdlib_strict_heap_allocator); + dvec_init(&main, sizeof(char), 0, &stdlib_strict_heap_allocator); + dvec_init(&fa, sizeof(char), 0, &stdlib_strict_heap_allocator); + dvec_init(&fb, sizeof(char), 0, &stdlib_strict_heap_allocator); + dvec_init(&fc, sizeof(char), 0, &stdlib_strict_heap_allocator); + + icc_parse_inst(&icc, aoc.input, aoc.input_size); + + icc_write(&icc, mi_imm(&mi_addr, &ul_addr, 0, MI_POS), + mi_imm(&mi_val, &ul_val, 2, MI_POS)); + + receive_map(&icc, &map, &min, &max, &dpos, &ddir); + + if (aoc.debug) + print_map(&map, min, max); + + gen_paths(&paths, &map, min, max); + + gen_insts(&insts, &paths, dpos, ddir); + + if (aoc.debug) { + aoc_debug("%li insts\n", insts.len); + line = insts_line(&insts); + aoc_debug("%s", line); + free(line); + } + + gen_funcs(&insts, &main, &fa, &fb, &fc); + + receive_output(&icc); + line = insts_line(&main); + if (aoc.debug) printf("main %s", line); + send_line(&icc, line); + free(line); + + receive_output(&icc); + line = insts_line(&fa); + if (aoc.debug) printf("A %s", line); + send_line(&icc, line); + free(line); + + receive_output(&icc); + line = insts_line(&fb); + if (aoc.debug) printf("B %s", line); + send_line(&icc, line); + free(line); + + receive_output(&icc); + line = insts_line(&fc); + if (aoc.debug) printf("C %s", line); + send_line(&icc, line); + free(line); + + receive_output(&icc); + send_line(&icc, "n\n"); + + while (icc.state != ICC_HALT) + icc_step_inst(&icc); +exit: + mi_dec(&icc.out, buf, sizeof(buf), 0); + aoc.answer = aprintf("%s", buf); + aoc.solution = "578918"; + + dvec_deinit(&fc); + dvec_deinit(&fb); + dvec_deinit(&fa); + dvec_deinit(&main); + dvec_deinit(&insts); + + for (HMAP_ITER(&paths, &iter)) { + free(iter.link->key._p); + dvec_free(iter.link->value._p); + } + hmap_deinit(&paths); + + for (HMAP_ITER(&map, &iter)) + free(iter.link->key._p); + hmap_deinit(&map); + + icc_deinit(&icc); +} diff --git a/17/part1 b/17/part1 @@ -0,0 +1,73 @@ +--- Day 17: Set and Forget --- + +An early warning system detects an incoming solar flare and automatically activates the ship's +electromagnetic shield. Unfortunately, this has cut off the Wi-Fi for many small robots that, +unaware of the impending danger, are now trapped on exterior scaffolding on the unsafe side of the +shield. To rescue them, you'll have to act quickly! + +The only tools at your disposal are some wired cameras and a small vacuum robot currently asleep at +its charging station. The video quality is poor, but the vacuum robot has a needlessly bright LED +that makes it easy to spot no matter where it is. + +An Intcode program, the Aft Scaffolding Control and Information Interface (ASCII, your puzzle +input), provides access to the cameras and the vacuum robot. Currently, because the vacuum robot is +asleep, you can only access the cameras. + +Running the ASCII program on your Intcode computer will provide the current view of the scaffolds. +This is output, purely coincidentally, as ASCII code: 35 means #, 46 means ., 10 starts a new line +of output below the current one, and so on. (Within a line, characters are drawn left-to-right.) + +In the camera output, # represents a scaffold and . represents open space. The vacuum robot is +visible as ^, v, <, or > depending on whether it is facing up, down, left, or right respectively. +When drawn like this, the vacuum robot is always on a scaffold; if the vacuum robot ever walks off +of a scaffold and begins tumbling through space uncontrollably, it will instead be visible as X. + +In general, the scaffold forms a path, but it sometimes loops back onto itself. For example, +suppose you can see the following view from the cameras: + +..#.......... +..#.......... +#######...### +#.#...#...#.# +############# +..#...#...#.. +..#####...^.. + +Here, the vacuum robot, ^ is facing up and sitting at one end of the scaffold near the bottom-right +of the image. The scaffold continues up, loops across itself several times, and ends at the top-left +of the image. + +The first step is to calibrate the cameras by getting the alignment parameters of some well-defined +points. Locate all scaffold intersections; for each, its alignment parameter is the distance +between its left edge and the left edge of the view multiplied by the distance between its top edge +and the top edge of the view. Here, the intersections from the above image are marked O: + +..#.......... +..#.......... +##O####...### +#.#...#...#.# +##O###O###O## +..#...#...#.. +..#####...^.. + +For these intersections: + + + - The top-left intersection is 2 units from the left of the image and 2 units from the top of the +image, so its alignment parameter is 2 * 2 = 4. + + - The bottom-left intersection is 2 units from the left and 4 units from the top, so its alignment +parameter is 2 * 4 = 8. + + - The bottom-middle intersection is 6 from the left and 4 from the top, so its alignment parameter +is 24. + + - The bottom-right intersection's alignment parameter is 40. + + +To calibrate the cameras, you need the sum of the alignment parameters. In the above example, this +is 76. + +Run your ASCII program. What is the sum of the alignment parameters for the scaffold intersections? + + diff --git a/17/part2 b/17/part2 @@ -0,0 +1,105 @@ +--- Part Two --- + +Now for the tricky part: notifying all the other robots about the solar flare. The vacuum robot can +do this automatically if it gets into range of a robot. However, you can't see the other robots on +the camera, so you need to be thorough instead: you need to make the vacuum robot visit every part +of the scaffold at least once. + +The vacuum robot normally wanders randomly, but there isn't time for that today. Instead, you can +override its movement logic with new rules. + +Force the vacuum robot to wake up by changing the value in your ASCII program at address 0 from 1 to +2. When you do this, you will be automatically prompted for the new movement rules that the vacuum +robot should use. The ASCII program will use input instructions to receive them, but they need to be +provided as ASCII code; end each line of logic with a single newline, ASCII code 10. + +First, you will be prompted for the main movement routine. The main routine may only call the +movement functions: A, B, or C. Supply the movement functions to use as ASCII text, separating them +with commas (,, ASCII code 44), and ending the list with a newline (ASCII code 10). For example, to +call A twice, then alternate between B and C three times, provide the string A,A,B,C,B,C,B,C and +then a newline. + +Then, you will be prompted for each movement function. Movement functions may use L to turn left, R +to turn right, or a number to move forward that many units. Movement functions may not call other +movement functions. Again, separate the actions with commas and end the list with a newline. For +example, to move forward 10 units, turn left, move forward 8 units, turn right, and finally move +forward 6 units, provide the string 10,L,8,R,6 and then a newline. + +Finally, you will be asked whether you want to see a continuous video feed; provide either y or n +and a newline. Enabling the continuous video feed can help you see what's going on, but it also +requires a significant amount of processing power, and may even cause your Intcode computer to +overheat. + +Due to the limited amount of memory in the vacuum robot, the ASCII definitions of the main routine +and the movement functions may each contain at most 20 characters, not counting the newline. + +For example, consider the following camera feed: + +#######...##### +#.....#...#...# +#.....#...#...# +......#...#...# +......#...###.# +......#.....#.# +^########...#.# +......#.#...#.# +......######### +........#...#.. +....#########.. +....#...#...... +....#...#...... +....#...#...... +....#####...... + +In order for the vacuum robot to visit every part of the scaffold at least once, one path it could +take is: + +R,8,R,8,R,4,R,4,R,8,L,6,L,2,R,4,R,4,R,8,R,8,R,8,L,6,L,2 +Without the memory limit, you could just supply this whole string to function A and have the main +routine call A once. However, you'll need to split it into smaller parts. + +One approach is: + + + - Main routine: A,B,C,B,A,C(ASCII input: 65, 44, 66, 44, 67, 44, 66, 44, 65, 44, 67, 10) + + - Function A:   R,8,R,8(ASCII input: 82, 44, 56, 44, 82, 44, 56, 10) + + - Function B:   R,4,R,4,R,8(ASCII input: 82, 44, 52, 44, 82, 44, 52, 44, 82, 44, 56, 10) + + - Function C:   L,6,L,2(ASCII input: 76, 44, 54, 44, 76, 44, 50, 10) + + +Visually, this would break the desired path into the following parts: + +A, B, C, B, A, C +R,8,R,8, R,4,R,4,R,8, L,6,L,2, R,4,R,4,R,8, R,8,R,8, L,6,L,2 + +CCCCCCA...BBBBB +C.....A...B...B +C.....A...B...B +......A...B...B +......A...CCC.B +......A.....C.B +^AAAAAAAA...C.B +......A.A...C.B +......AAAAAA#AB +........A...C.. +....BBBB#BBBB.. +....B...A...... +....B...A...... +....B...A...... +....BBBBA...... + +Of course, the scaffolding outside your ship is much more complex. + +As the vacuum robot finds other robots and notifies them of the impending solar flare, it also can't +help but leave them squeaky clean, collecting any space dust it finds. Once it finishes the +programmed set of movements, assuming it hasn't drifted off into space, the cleaning robot will +return to its docking station and report the amount of space dust it collected as a large, non-ASCII +value in a single output instruction. + +After visiting every part of the scaffold at least once, how much dust does the vacuum robot report +it has collected? + + diff --git a/common/iccmp.c b/common/iccmp.c @@ -322,9 +322,10 @@ icc_inst_store(struct icc *icc) { int rc; - if (icc->state != ICC_INPUT) { + if (icc->state != ICC_INPUT || !icc->in.size) { icc_debug_op(icc, &icc->tmp, "INPUT", 0); icc->state = ICC_INPUT; + icc->in.size = 0; } else { icc_debug_op_main(icc, &icc->tmp, "STORE", 1); diff --git a/common/main.c b/common/main.c @@ -46,10 +46,12 @@ main(int argc, const char **argv) /* check answer if given */ if (aoc.answer) { printf("%s\n", aoc.answer); - if (aoc.solution && strcmp(aoc.answer, aoc.solution)) - errx(1, "Incorrect solution!"); - else if (!aoc.solution) - errx(1, "Solution missing!"); + if (!strcmp(aoc.inputfile, "input")) { + if (aoc.solution && strcmp(aoc.answer, aoc.solution)) + errx(1, "Incorrect solution!"); + else if (!aoc.solution) + errx(1, "Solution missing!"); + } } free(aoc.answer); diff --git a/common/util.c b/common/util.c @@ -79,6 +79,32 @@ aprintf(const char *fmtstr, ...) return str; } +char * +apprintf(char *str, const char *fmtstr, ...) +{ + va_list ap, cpy; + ssize_t nb; + size_t len; + + len = str ? strlen(str) : 0; + + va_copy(cpy, ap); + + va_start(cpy, fmtstr); + nb = vsnprintf(NULL, 0, fmtstr, cpy); + if (nb < 0) die("util: aprintf: invalid fmtstr: %s", fmtstr); + va_end(cpy); + + str = realloc(str, len + (size_t) nb + 1); + if (!str) die("util: aprintf: realloc %lu", nb + 1); + + va_start(ap, fmtstr); + nb = vsnprintf(str + len, (size_t) nb + 1, fmtstr, ap); + va_end(ap); + + return str; +} + void * memdup(void *data, size_t size) { diff --git a/common/util.h b/common/util.h @@ -7,7 +7,7 @@ #include <stdio.h> #include <stdlib.h> -#define ASSERT(expr) assert(expr, __FILE__, __LINE__, #expr) +#define ARRLEN(x) (sizeof(x) / sizeof(*(x))) #define ABS(a) ((a) > 0 ? (a) : -(a)) @@ -25,6 +25,7 @@ bool readtok(char *buf, size_t buflen, 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 readall(FILE *f, void **data, size_t *read); diff --git a/common/vec.h b/common/vec.h @@ -1,6 +1,9 @@ #include <string.h> +#include <stdbool.h> #include <stdlib.h> +#define VEC_ABS(x) ((x) > 0 ? (x) : -(x)) + struct vec2i { union { struct { @@ -38,7 +41,7 @@ struct vec3f { }; #define DEFINE_VEC_SET(name, type, dims) \ - static inline void name(type *dst, type *src) { \ + static inline void name(type *dst, const type *src) { \ memcpy(dst, src, sizeof(type)); \ } @@ -60,10 +63,18 @@ struct vec3f { dst->dims[i] = a->dims[i] * b; \ } +#define DEFINE_VEC_EQL(name, type, n) \ + static inline bool name(const type *a, const type *b) { \ + for (int i = 0; i < n; i++) \ + if (a->dims[i] != b->dims[i]) return false; \ + return true; \ + } + DEFINE_VEC_SET(vec2i_set, struct vec2i, 2); DEFINE_VEC_ADD(vec2i_add, struct vec2i, 2); DEFINE_VEC_SUB(vec2i_sub, struct vec2i, 2); DEFINE_VEC_MUL(vec2i_mul, struct vec2i, ssize_t, 2); +DEFINE_VEC_EQL(vec2i_eql, struct vec2i, 2); static inline void vec2i_setv(struct vec2i *dst, ssize_t x, ssize_t y) @@ -72,10 +83,17 @@ vec2i_setv(struct vec2i *dst, ssize_t x, ssize_t y) dst->y = y; } +static inline ssize_t +vec2i_len(struct vec2i *a) +{ + return VEC_ABS(a->x) + VEC_ABS(a->y); +} + DEFINE_VEC_SET(vec3i_set, struct vec3i, 3); DEFINE_VEC_ADD(vec3i_add, struct vec3i, 3); DEFINE_VEC_SUB(vec3i_sub, struct vec3i, 3); DEFINE_VEC_MUL(vec3i_mul, struct vec3i, ssize_t, 3); +DEFINE_VEC_EQL(vec3i_eql, struct vec3i, 3); static inline void vec3i_setv(struct vec3i *dst, ssize_t x, ssize_t y, ssize_t z)