aoc-2019-c

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

commit 347f52c467baff5257ee25e59b0688dac823abbe
parent 018821728749aa6ae0e0455a4dd81f8a913f0369
Author: Louis Burda <quent.burda@gmail.com>
Date:   Fri, 24 Mar 2023 21:17:15 +0100

Add day 15

Diffstat:
A15/info.mk | 4++++
A15/input | 2++
A15/main.c | 372+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A15/part1 | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A15/part2 | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcommon/iccmp.c | 10+++++-----
6 files changed, 544 insertions(+), 5 deletions(-)

diff --git a/15/info.mk b/15/info.mk @@ -0,0 +1,4 @@ +15_SRC = 15/main.c common/main.c common/iccmp.c common/aoc.c common/util.c +15_SRC += lib/libdvec/build/libdvec.a lib/liballoc/build/liballoc.a +15_SRC += lib/libmaxint/build/libmaxint.a lib/libhmap/build/libhmap.a +15_HDR = common/aoc.h common/iccmp.h common/util.h diff --git a/15/input b/15/input @@ -0,0 +1,2 @@ +3,1033,1008,1033,1,1032,1005,1032,31,1008,1033,2,1032,1005,1032,58,1008,1033,3,1032,1005,1032,81,1008,1033,4,1032,1005,1032,104,99,1002,1034,1,1039,1002,1036,1,1041,1001,1035,-1,1040,1008,1038,0,1043,102,-1,1043,1032,1,1037,1032,1042,1105,1,124,1001,1034,0,1039,1001,1036,0,1041,1001,1035,1,1040,1008,1038,0,1043,1,1037,1038,1042,1105,1,124,1001,1034,-1,1039,1008,1036,0,1041,1002,1035,1,1040,1001,1038,0,1043,1002,1037,1,1042,1105,1,124,1001,1034,1,1039,1008,1036,0,1041,1002,1035,1,1040,1001,1038,0,1043,101,0,1037,1042,1006,1039,217,1006,1040,217,1008,1039,40,1032,1005,1032,217,1008,1040,40,1032,1005,1032,217,1008,1039,3,1032,1006,1032,165,1008,1040,5,1032,1006,1032,165,1101,2,0,1044,1105,1,224,2,1041,1043,1032,1006,1032,179,1102,1,1,1044,1105,1,224,1,1041,1043,1032,1006,1032,217,1,1042,1043,1032,1001,1032,-1,1032,1002,1032,39,1032,1,1032,1039,1032,101,-1,1032,1032,101,252,1032,211,1007,0,55,1044,1106,0,224,1102,0,1,1044,1105,1,224,1006,1044,247,102,1,1039,1034,102,1,1040,1035,102,1,1041,1036,1002,1043,1,1038,101,0,1042,1037,4,1044,1106,0,0,5,20,51,81,57,10,21,4,5,12,94,86,11,35,82,29,14,52,78,53,41,88,58,48,50,16,2,36,58,7,93,31,1,99,43,9,47,67,54,39,78,89,3,17,63,95,70,84,41,59,32,80,35,7,91,36,80,66,28,78,20,26,68,69,59,14,90,22,31,86,16,67,67,45,77,29,61,44,44,77,52,81,54,66,15,43,95,13,22,79,80,37,90,65,58,11,14,80,82,42,84,47,71,14,94,78,24,71,25,6,11,71,47,86,20,97,37,18,92,57,15,98,44,78,91,44,83,59,4,12,87,3,12,14,86,70,19,31,72,29,12,22,23,73,61,91,40,66,68,66,16,73,59,41,83,8,7,48,61,54,95,2,25,61,13,17,76,85,96,16,79,84,39,96,49,24,67,88,88,88,66,46,52,54,71,47,63,84,4,33,7,63,84,27,6,26,76,70,29,49,93,31,63,64,26,16,40,60,30,60,10,85,85,62,32,4,98,39,20,1,85,98,48,29,24,74,30,92,90,37,49,29,95,12,98,49,57,36,43,96,99,17,18,95,26,80,20,29,50,73,69,51,50,9,46,78,38,72,88,39,3,92,96,50,88,14,98,93,7,62,15,97,15,33,21,96,15,74,76,38,12,63,77,80,29,91,96,23,18,75,52,96,78,94,88,49,65,43,82,58,46,27,62,2,32,81,45,67,83,80,62,54,40,85,66,48,54,72,87,3,7,86,84,2,45,46,82,84,17,36,29,94,12,47,59,89,28,93,40,50,77,83,48,66,18,15,70,13,68,26,86,46,18,63,6,97,21,76,75,80,1,30,67,38,74,8,9,65,90,68,11,66,60,12,4,96,94,60,36,25,78,13,67,70,35,76,53,11,72,40,70,59,9,11,88,27,44,61,11,54,98,69,35,93,93,9,85,2,78,21,99,96,27,81,40,9,99,42,66,77,10,95,7,31,90,44,89,90,24,24,48,75,69,36,5,94,89,17,81,52,92,15,52,76,65,35,22,17,58,40,18,2,77,72,49,73,36,35,62,24,64,12,70,1,11,24,82,20,53,80,97,49,70,6,95,12,62,58,12,49,77,80,24,49,86,97,95,45,71,90,60,38,94,23,37,85,1,77,26,57,81,30,58,67,39,60,10,3,82,21,41,71,38,49,65,19,4,93,57,2,74,12,96,12,22,7,50,87,76,51,33,1,90,66,12,85,79,28,18,66,35,21,89,51,83,14,32,63,12,71,40,63,4,95,7,72,65,20,45,79,16,75,85,58,16,74,17,53,88,64,75,29,21,24,51,85,51,97,44,49,67,59,90,29,7,8,98,22,52,94,65,31,83,64,29,43,95,11,68,88,18,35,80,78,39,96,22,94,10,31,93,9,71,43,64,80,67,17,63,50,49,75,14,76,31,89,21,73,30,3,69,97,60,27,24,22,66,27,68,89,69,12,49,91,48,54,60,5,84,69,18,67,1,63,51,28,23,97,4,62,21,13,45,99,33,69,99,5,95,32,54,45,72,99,65,8,54,1,91,27,50,91,65,13,91,16,90,48,12,58,66,86,15,78,68,50,94,7,71,84,87,38,39,16,27,70,61,5,95,92,85,54,72,8,95,81,78,5,92,77,50,74,86,1,31,69,94,1,37,57,32,3,7,92,82,68,90,42,22,71,25,71,71,91,37,93,52,57,18,57,23,83,39,72,25,58,6,69,46,83,19,82,48,70,28,89,98,62,48,69,81,78,24,82,47,82,40,97,10,93,0,0,21,21,1,10,1,0,0,0,0,0,0 + diff --git a/15/main.c b/15/main.c @@ -0,0 +1,372 @@ +#include "allocator.h" +#include "util.h" +#include "aoc.h" +#include "iccmp.h" +#include "hmap.h" + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> + +enum { + MOVE_NORTH = 1, + MOVE_SOUTH, + MOVE_WEST, + MOVE_EAST +}; + +enum { + BLOCK_WALL = 0, + BLOCK_OPEN, + BLOCK_GOAL +}; + +struct vec2 { + int x, y; +}; + +struct state { + int back; + int next; +}; + +const char block_lut[] = { + '#', + '.', + 'O' +}; + +const struct vec2 dirs[] = { + { 0 }, + { 0, -1 }, + { 0, 1 }, + { -1, 0 }, + { 1, 0 }, +}; + +const int rev[] = { + 0, 2, 1, 4, 3 +}; + +uint32_t +vec_hmap_hash(struct hmap_key key) +{ + const struct vec2 *vec = key.p; + + return (uint32_t) (vec->x << 16) ^ (uint32_t) vec->y; +} + +bool +vec_hmap_keycmp(struct hmap_key k1, struct hmap_key k2) +{ + const struct vec2 *v1 = k1.p, *v2 = k2.p; + + return v1->x == v2->x && v1->y == v2->y; +} + +int +robot_move(struct icc *icc, int dir) +{ +// static struct vec2 pos = { 0 }; +// struct vec2 npos; +// +// npos.x = pos.x + dirs[dir].x; +// npos.y = pos.y + dirs[dir].y; +// +// +// if (ABS(npos.x) + ABS(npos.y) > 10) +// return BLOCK_WALL; +// +// pos.x = npos.x; +// pos.y = npos.y; +// +// return BLOCK_OPEN; +// + while (icc->state != ICC_HALT) { + icc_step_inst(icc); + switch (icc->state) { + case ICC_INPUT: + mi_setv(&icc->in, (mi_ul) dir, MI_POS); + continue; + case ICC_OUTPUT: + return (int) mi_cast_ul(&icc->out); + case ICC_HALT: + case ICC_RUN: + continue; + } + } + + return -1; +} + +void +print_map(struct hmap *map, struct vec2 droid) +{ + struct hmap_link *link; + struct hmap_iter iter; + struct vec2 min = { 0 }; + struct vec2 max = { 0 }; + struct vec2 pos; + const struct vec2 *vec; + + for (HMAP_ITER(map, &iter)) { + vec = iter.link->key.p; + min.x = MIN(min.x, vec->x); + min.y = MIN(min.y, vec->y); + max.x = MAX(max.x, vec->x); + max.y = MAX(max.y, vec->y); + } + + fprintf(stderr, "+"); + for (pos.x = min.x; pos.x <= max.x; pos.x++) + fprintf(stderr, "-"); + fprintf(stderr, "+\n"); + + for (pos.y = min.y; pos.y <= max.y; pos.y++) { + fprintf(stderr, "|"); + for (pos.x = min.x; pos.x <= max.x; pos.x++) { + if (pos.x == droid.x && pos.y == droid.y) { + fprintf(stderr, "D"); + } else { + link = hmap_get(map, + (struct hmap_key) {.p = &pos}); + if (link) { + fprintf(stderr, "%c", link->value.c); + } else { + fprintf(stderr, " "); + } + } + } + fprintf(stderr, "|\n"); + } + + fprintf(stderr, "+"); + for (pos.x = min.x; pos.x <= max.x; pos.x++) + fprintf(stderr, "-"); + fprintf(stderr, "+\n"); +} + +size_t +dijkstra_dist(struct hmap *map, struct vec2 start, const struct vec2 *end) +{ + struct hmap_link *link, **linkp; + struct hmap_iter iter; + struct hmap seen; + struct dvec outer; + struct vec2 *vec; + struct vec2 pos, npos; + size_t dist, maxdist; + void *key; + int dir; + + hmap_init(&seen, 32, vec_hmap_hash, vec_hmap_keycmp, + &stdlib_strict_heap_allocator); + dvec_init(&outer, sizeof(struct vec2), + 10, &stdlib_strict_heap_allocator); + + vec = dvec_add_slot(&outer, NULL); + vec->x = start.x; + vec->y = start.y; + + key = memdup(vec, sizeof(struct vec2)); + hmap_add(&seen, (struct hmap_key) {.p = key}, + (struct hmap_val) {.s = 0}); + + maxdist = 0; + while (!dvec_empty(&outer)) { + vec = dvec_back(&outer); + pos.x = vec->x; + pos.y = vec->y; + link = hmap_get(&seen, (struct hmap_key) {.p = &pos}); + assert(link); + dist = link->value.s + 1; + dvec_rm_slot(&outer, vec); + + for (dir = 1; dir <= 4; dir++) { + npos.x = pos.x + dirs[dir].x; + npos.y = pos.y + dirs[dir].y; + linkp = hmap_link_pos(&seen, + (struct hmap_key) {.p = &npos}); + if (*linkp) continue; + + if (end && !memcmp(&npos, end, sizeof(struct vec2))) + goto exit; + + link = hmap_get(map, (struct hmap_key) {.p = &npos}); + assert(link); + if (link->value.c != '.') continue; + + if (!maxdist || dist > maxdist) + maxdist = dist; + + vec = dvec_add_slot(&outer, NULL); + vec->x = npos.x; + vec->y = npos.y; + key = memdup(&npos, sizeof(struct vec2)); + hmap_link_alloc(&seen, linkp, + (struct hmap_key) {.p = key}, + (struct hmap_val) {.s = dist}); + } + } + +exit: + for (HMAP_ITER(&seen, &iter)) + free(iter.link->key._p); + dvec_deinit(&outer); + hmap_deinit(&seen); + + return end ? dist : maxdist; +} + +void +explore_map(struct icc *icc, struct hmap *map, + struct vec2 start, struct vec2 *tank) +{ + struct hmap_link **linkp; + struct hmap_link *link; + struct dvec states; + struct vec2 pos, npos; + struct state *state; + int move_result; + int move_dir; + void *key; + + dvec_init(&states, sizeof(struct state), + 10, &stdlib_strict_heap_allocator); + + state = dvec_add_slot(&states, NULL); + state->back = -1; + state->next = 1; + + pos = (struct vec2) { start.x, start.y }; + key = memdup(&pos, sizeof(struct vec2)); + hmap_add(map, (struct hmap_key) {.p = key}, + (struct hmap_val) {.c = '.'}); + + while (!dvec_empty(&states)) { + assert(!dvec_empty(&states)); + state = dvec_back(&states); + for (; state->next <= 4; state->next++) { + if (state->next == state->back) + continue; + move_dir = state->next; + npos.x = pos.x + dirs[move_dir].x; + npos.y = pos.y + dirs[move_dir].y; + link = hmap_get(map, + (struct hmap_key) {.p = &npos}); + if (!link) break; + } + + if (state->next > 4) { + if (state->back <= 0) break; + move_result = robot_move(icc, state->back); + assert(move_result == BLOCK_OPEN); + pos.x += dirs[state->back].x; + pos.y += dirs[state->back].y; + dvec_rm_slot(&states, state); + if (aoc.debug) { + aoc_debug("Step: backtrack x %i y %i\n", pos.x, pos.y); + print_map(map, pos); + aoc_debug("\n"); + } + continue; + } + state->next += 1; + + assert(move_dir >= 1 && move_dir <= 4); + move_result = robot_move(icc, move_dir); + assert(move_result >= 0 && move_result <= 2); + + linkp = hmap_link_pos(map, (struct hmap_key) {.p = &npos}); + if (*linkp) { + (*linkp)->value.c = block_lut[move_result]; + } else { + key = memdup(&npos, sizeof(struct vec2)); + hmap_link_alloc(map, linkp, + (struct hmap_key) {.p = key}, + (struct hmap_val) {.c = block_lut[move_result]}); + } + + switch (move_result) { + case BLOCK_GOAL: + tank->x = npos.x; + tank->y = npos.y; + case BLOCK_OPEN: + pos.x = npos.x; + pos.y = npos.y; + state = dvec_add_slot(&states, NULL); + state->back = rev[move_dir]; + state->next = 1; + break; + case BLOCK_WALL: + break; + default: + assert(0); + } + + if (aoc.debug) { + aoc_debug("Step: x %i y %i dx %i dy %i\n", + pos.x, pos.y, dirs[move_dir].x, dirs[move_dir].y); + print_map(map, pos); + aoc_debug("\n"); + } + } + + dvec_deinit(&states); +} + +void +part1(void) +{ + struct icc icc; + struct hmap_iter iter; + struct hmap map; + struct vec2 start, tank; + size_t dist; + + icc_init(&icc); + hmap_init(&map, 32, vec_hmap_hash, vec_hmap_keycmp, + &stdlib_strict_heap_allocator); + + icc_parse_inst(&icc, aoc.input, aoc.input_size); + + start = (struct vec2) { 0, 0 }; + explore_map(&icc, &map, start, &tank); + + dist = dijkstra_dist(&map, start, &tank); + aoc.answer = aprintf("%lu", dist); + + for (HMAP_ITER(&map, &iter)) + free(iter.link->key._p); + hmap_deinit(&map); + icc_deinit(&icc); +} + +void +part2(void) +{ + struct icc icc; + struct hmap_iter iter; + struct hmap map; + struct vec2 start, tank; + size_t dist; + + icc_init(&icc); + hmap_init(&map, 32, vec_hmap_hash, vec_hmap_keycmp, + &stdlib_strict_heap_allocator); + + icc_parse_inst(&icc, aoc.input, aoc.input_size); + + start = (struct vec2) { 0, 0 }; + explore_map(&icc, &map, start, &tank); + + dist = dijkstra_dist(&map, tank, NULL); + aoc.answer = aprintf("%lu", dist); + + for (HMAP_ITER(&map, &iter)) + free(iter.link->key._p); + hmap_deinit(&map); + icc_deinit(&icc); +} diff --git a/15/part1 b/15/part1 @@ -0,0 +1,104 @@ +--- Day 15: Oxygen System --- + +Out here in deep space, many things can go wrong. Fortunately, many of those things have indicator +lights. Unfortunately, one of those lights is lit: the oxygen system for part of the ship has +failed! + +According to the readouts, the oxygen system must have failed days ago after a rupture in oxygen +tank two; that section of the ship was automatically sealed once oxygen levels went dangerously low. +A single remotely-operated repair droid is your only option for fixing the oxygen system. + +The Elves' care package included an Intcode program (your puzzle input) that you can use to remotely +control the repair droid. By running that program, you can direct the repair droid to the oxygen +system and fix the problem. + +The remote control program executes the following steps in a loop forever: + + + - Accept a movement command via an input instruction. + + - Send the movement command to the repair droid. + + - Wait for the repair droid to finish the movement operation. + + - Report on the status of the repair droid via an output instruction. + + +Only four movement commands are understood: north (1), south (2), west (3), and east (4). Any other +command is invalid. The movements differ in direction, but not in distance: in a long enough +east-west hallway, a series of commands like 4,4,4,4,3,3,3,3 would leave the repair droid back where +it started. + +The repair droid can reply with any of the following status codes: + + + - 0: The repair droid hit a wall. Its position has not changed. + + - 1: The repair droid has moved one step in the requested direction. + + - 2: The repair droid has moved one step in the requested direction; its new position is the +location of the oxygen system. + + +You don't know anything about the area around the repair droid, but you can figure it out by +watching the status codes. + +For example, we can draw the area using D for the droid, # for walls, . for locations the droid can +traverse, and empty space for unexplored locations. Then, the initial state looks like this: + + + + D + + + +To make the droid go north, send it 1. If it replies with 0, you know that location is a wall and +that the droid didn't move: + + + # + D + + + +To move east, send 4; a reply of 1 means the movement was successful: + + + # + .D + + + +Then, perhaps attempts to move north (1), south (2), and east (4) are all met with replies of 0: + + + ## + .D# + # + + +Now, you know the repair droid is in a dead end. Backtrack with 3 (which you already know will get a +reply of 1 because you already know that location is open): + + + ## + D.# + # + + +Then, perhaps west (3) gets a reply of 0, south (2) gets a reply of 1, south again (2) gets a reply +of 0, and then west (3) gets a reply of 2: + + + ## + #..# + D.# + # + +Now, because of the reply of 2, you know you've found the oxygen system! In this example, it was +only 2 moves away from the repair droid's starting position. + +What is the fewest number of movement commands required to move the repair droid from its starting +position to the location of the oxygen system? + + diff --git a/15/part2 b/15/part2 @@ -0,0 +1,57 @@ +--- Part Two --- + +You quickly repair the oxygen system; oxygen gradually fills the area. + +Oxygen starts in the location containing the repaired oxygen system. It takes one minute for oxygen +to spread to all open locations that are adjacent to a location that already contains oxygen. +Diagonal locations are not adjacent. + +In the example above, suppose you've used the droid to explore the area fully and have the following +map (where locations that currently contain oxygen are marked O): + + ## +#..## +#.#..# +#.O.# + ### + +Initially, the only location which contains oxygen is the location of the repaired oxygen system. +However, after one minute, the oxygen spreads to all open (.) locations that are adjacent to a +location containing oxygen: + + ## +#..## +#.#..# +#OOO# + ### + +After a total of two minutes, the map looks like this: + + ## +#..## +#O#O.# +#OOO# + ### + +After a total of three minutes: + + ## +#O.## +#O#OO# +#OOO# + ### + +And finally, the whole region is full of oxygen after a total of four minutes: + + ## +#OO## +#O#OO# +#OOO# + ### + +So, in this example, all locations contain oxygen after 4 minutes. + +Use the repair droid to get a complete map of the area. How many minutes will it take to fill with +oxygen? + + diff --git a/common/iccmp.c b/common/iccmp.c @@ -95,8 +95,8 @@ icc_deinit(struct icc *icc) for (HMAP_ITER(&icc->instructions, &iter)) { mi_deinit(iter.link->key._p); free(iter.link->key._p); - mi_deinit(iter.link->value.p); - free(iter.link->value.p); + mi_deinit(iter.link->value._p); + free(iter.link->value._p); } hmap_deinit(&icc->instructions); @@ -565,7 +565,7 @@ icc_write_any(struct icc *icc, struct maxint *addr, struct maxint *value) link = hmap_link_pos(&icc->instructions, (struct hmap_key) {.p = addr}); if (*link) { - mi_set((*link)->value.p, value); + mi_set((*link)->value._p, value); } else { key = malloc(sizeof(struct maxint)); assert(key != NULL); @@ -601,7 +601,7 @@ icc_write(struct icc *icc, struct maxint *addr, struct maxint *val) icc_write_any(icc, addr, val); } else { - mi_set(link->value.p, val); + mi_set(link->value._p, val); } return 0; @@ -622,7 +622,7 @@ icc_read(struct icc *icc, struct maxint *addr, struct maxint **out) link = icc_write_any(icc, addr, mi_tmp(0)); } - *out = link->value.p; + *out = link->value._p; return 0; }