aoc-2019-c

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

commit 1e09f86c26b95fc3f27a214dc46a6966d5311cd0
parent a1f776c9c5390b8409e7f44cb2a33d728d1200f3
Author: Louis Burda <quent.burda@gmail.com>
Date:   Wed,  5 Apr 2023 08:13:30 -0400

Add day 24

Diffstat:
A24/info.mk | 5+++++
A24/input | 6++++++
A24/part1 | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A24/part1.c | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A24/part2 | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A24/part2.c | 288+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A24/test1 | 5+++++
A24/test2 | 5+++++
Mcommon/hmap_s.c | 8++++----
Mcommon/hmap_s.h | 4++--
Mcommon/vec.h | 8++++++++
11 files changed, 732 insertions(+), 6 deletions(-)

diff --git a/24/info.mk b/24/info.mk @@ -0,0 +1,5 @@ +24_SRC = 24/part1.c 24/part2.c common/main.c common/aoc.c common/util.c +24_SRC += common/vec_s.c common/hmap_s.c +24_SRC += lib/liballoc/build/liballoc.a lib/libdvec/build/libdvec.a +24_SRC += lib/libhmap/build/libhmap.a +24_HDR = common/aoc.h common/util.h diff --git a/24/input b/24/input @@ -0,0 +1,6 @@ +#..## +#.#.. +#...# +##..# +#..## + diff --git a/24/part1 b/24/part1 @@ -0,0 +1,77 @@ +--- Day 24: Planet of Discord --- + +You land on Eris, your last stop before reaching Santa. As soon as you do, your sensors start +picking up strange life forms moving around: Eris is infested with bugs! With an over 24-hour +roundtrip for messages between you and Earth, you'll have to deal with this problem on your own. + +Eris isn't a very large place; a scan of the entire area fits into a 5x5 grid (your puzzle input). +The scan shows bugs (#) and empty spaces (.). + +Each minute, The bugs live and die based on the number of bugs in the four adjacent tiles: + + + - A bug dies (becoming an empty space) unless there is exactly one bug adjacent to it. + + - An empty space becomes infested with a bug if exactly one or two bugs are adjacent to it. + + +Otherwise, a bug or empty space remains the same. (Tiles on the edges of the grid have fewer than +four adjacent tiles; the missing tiles count as empty space.) This process happens in every location +simultaneously; that is, within the same minute, the number of adjacent bugs is counted for every +tile first, and then the tiles are updated. + +Here are the first few minutes of an example scenario: + +Initial state: +....# +#..#. +#..## +..#.. +#.... + +After 1 minute: +#..#. +####. +###.# +##.## +.##.. + +After 2 minutes: +##### +....# +....# +...#. +#.### + +After 3 minutes: +#.... +####. +...## +#.##. +.##.# + +After 4 minutes: +####. +....# +##..# +..... +##... + +To understand the nature of the bugs, watch for the first time a layout of bugs and empty spaces +matches any previous layout. In the example above, the first layout to appear twice is: + +..... +..... +..... +#.... +.#... + +To calculate the biodiversity rating for this layout, consider each tile left-to-right in the top +row, then left-to-right in the second row, and so on. Each of these tiles is worth biodiversity +points equal to increasing powers of two: 1, 2, 4, 8, 16, 32, and so on. Add up the biodiversity +points for tiles with bugs; in this example, the 16th tile (32768 points) and 22nd tile (2097152 +points) have bugs, a total biodiversity rating of 2129920. + +What is the biodiversity rating for the first layout that appears twice? + + diff --git a/24/part1.c b/24/part1.c @@ -0,0 +1,147 @@ +#include "allocator.h" +#include "aoc.h" +#include "vec_s.h" +#include "dvec_s.h" +#include "util.h" + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> + +static char * +load_map(size_t *maxx, size_t *h) +{ + const char *lpos, *lend; + size_t rows, cols, len; + char line[256]; + char *map; + + map = NULL; + rows = 0; + cols = 0; + + lpos = aoc.input; + lend = lpos + aoc.input_size; + while (readtok(line, sizeof(line), '\n', &lpos, lend)) { + len = strlen(line); + if (!len) continue; + if (!cols) cols = len; + assert(len == cols); + map = realloc(map, cols * (rows + 1) + 1); + memcpy(map + cols * rows, line, cols); + rows += 1; + } + map[rows * cols] = '\0'; + + *maxx = cols; + *h = rows; + + return map; +} + +static void +print_map(char *map, size_t w, size_t h) +{ + size_t y; + + for (y = 0; y < h; y++) + aoc_debug("%.*s\n", w, &map[y * w]); +} + +static uint64_t +map_id(char *map, size_t w, size_t h) +{ + size_t x, y, b; + uint64_t id; + + b = 0; + id = 0; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + if (map[y * w + x] == '#') + id |= 1 << b; + b += 1; + } + } + + return id; +} + +static void +step(char *prev, char *next, size_t w, size_t h) +{ + struct vec2i pos, npos; + size_t i, k, count; + + for (pos.y = 0; (size_t) pos.y < h; pos.y++) { + for (pos.x = 0; (size_t) pos.x < w; pos.x++) { + count = 0; + for (i = 0; i < 4; i++) { + vec2i_add(&npos, &pos, &adj[i]); + if (npos.x < 0 || (size_t) npos.x >= w) + continue; + if (npos.y < 0 || (size_t) npos.y >= h) + continue; + k = (size_t) npos.y * w + (size_t) npos.x; + if (prev[k] == '#') count += 1; + } + k = (size_t) pos.y * w + (size_t) pos.x; + if (prev[k] == '#' && count != 1) { + next[k] = '.'; + } else if (prev[k] == '.' && (count == 1 || count == 2)) { + next[k] = '#'; + } else { + next[k] = prev[k]; + } + } + } +} + +void +part1(void) +{ + struct dvec ids; + char *map, *next, *tmp; + uint64_t *slot, id; + size_t w, h, i; + uint64_t answer; + + map = load_map(&w, &h); + next = memdup(map, w * h + 1); + + dvec_init(&ids, sizeof(uint64_t), 0, &stdlib_strict_heap_allocator); + slot = dvec_add_slot(&ids); + *slot = map_id(map, w, h); + + aoc_debug("INIT\n"); + print_map(map, w, h); + + answer = 0; + while (!answer) { + step(map, next, w, h); + tmp = map; + map = next; + next = tmp; + + aoc_debug("$ %i\n", ids.len); + print_map(map, w, h); + + id = map_id(map, w, h); + for (i = 0; i < ids.len; i++) { + aoc_debug("%lu vs %lu\n", id, *(uint64_t *)dvec_at(&ids, i)); + if (*(uint64_t *)dvec_at(&ids, i) == id) { + answer = id; + break; + } + } + + slot = dvec_add_slot(&ids); + *slot = id; + } + + aoc.answer = aprintf("%lu", answer); + + dvec_deinit(&ids); + free(next); + free(map); +} diff --git a/24/part2 b/24/part2 @@ -0,0 +1,185 @@ +--- Part Two --- + +After careful analysis, one thing is certain: you have no idea where all these bugs are coming from. + +Then, you remember: Eris is an old Plutonian settlement! Clearly, the bugs are coming from +recursively-folded space. + +This 5x5 grid is only one level in an infinite number of recursion levels. The tile in the middle of +the grid is actually another 5x5 grid, the grid in your scan is contained as the middle tile of a +larger 5x5 grid, and so on. Two levels of grids look like this: + + | | | | + | | | | + | | | | +-----+-----+---------+-----+----- + | | | | + | | | | + | | | | +-----+-----+---------+-----+----- + | | | | | | | | + | |-+-+-+-+-| | + | | | | | | | | + | |-+-+-+-+-| | + | | | |?| | | | + | |-+-+-+-+-| | + | | | | | | | | + | |-+-+-+-+-| | + | | | | | | | | +-----+-----+---------+-----+----- + | | | | + | | | | + | | | | +-----+-----+---------+-----+----- + | | | | + | | | | + | | | | + +(To save space, some of the tiles are not drawn to scale.) Remember, this is only a small part of +the infinitely recursive grid; there is a 5x5 grid that contains this diagram, and a 5x5 grid that +contains that one, and so on. Also, the ? in the diagram contains another 5x5 grid, which itself +contains another 5x5 grid, and so on. + +The scan you took (your puzzle input) shows where the bugs are on a single level of this structure. +The middle tile of your scan is empty to accommodate the recursive grids within it. Initially, no +other levels contain bugs. + +Tiles still count as adjacent if they are directly up, down, left, or right of a given tile. Some +tiles have adjacent tiles at a recursion level above or below its own level. For example: + + | | | | + 1 | 2 | 3 | 4 | 5 + | | | | +-----+-----+---------+-----+----- + | | | | + 6 | 7 | 8 | 9 | 10 + | | | | +-----+-----+---------+-----+----- + | |A|B|C|D|E| | + | |-+-+-+-+-| | + | |F|G|H|I|J| | + | |-+-+-+-+-| | + 11 | 12 |K|L|?|N|O| 14 | 15 + | |-+-+-+-+-| | + | |P|Q|R|S|T| | + | |-+-+-+-+-| | + | |U|V|W|X|Y| | +-----+-----+---------+-----+----- + | | | | + 16 | 17 | 18 | 19 | 20 + | | | | +-----+-----+---------+-----+----- + | | | | + 21 | 22 | 23 | 24 | 25 + | | | | + + + - Tile 19 has four adjacent tiles: 14, 18, 20, and 24. + + - Tile G has four adjacent tiles: B, F, H, and L. + + - Tile D has four adjacent tiles: 8, C, E, and I. + + - Tile E has four adjacent tiles: 8, D, 14, and J. + + - Tile 14 has eight adjacent tiles: 9, E, J, O, T, Y, 15, and 19. + + - Tile N has eight adjacent tiles: I, O, S, and five tiles within the sub-grid marked ?. + + +The rules about bugs living and dying are the same as before. + +For example, consider the same initial state as above: + +....# +#..#. +#.?## +..#.. +#.... + +The center tile is drawn as ? to indicate the next recursive grid. Call this level 0; the grid +within this one is level 1, and the grid that contains this one is level -1. Then, after ten +minutes, the grid at each level would look like this: + +Depth -5: +..#.. +.#.#. +..?.# +.#.#. +..#.. + +Depth -4: +...#. +...## +..?.. +...## +...#. + +Depth -3: +#.#.. +.#... +..?.. +.#... +#.#.. + +Depth -2: +.#.## +....# +..?.# +...## +.###. + +Depth -1: +#..## +...## +..?.. +...#. +.#### + +Depth 0: +.#... +.#.## +.#?.. +..... +..... + +Depth 1: +.##.. +#..## +..?.# +##.## +##### + +Depth 2: +###.. +##.#. +#.?.. +.#.## +#.#.. + +Depth 3: +..### +..... +#.?.. +#.... +#...# + +Depth 4: +.###. +#..#. +#.?.. +##.#. +..... + +Depth 5: +####. +#..#. +#.?#. +####. +..... + +In this example, after 10 minutes, a total of 99 bugs are present. + +Starting with your scan, how many bugs are present after 200 minutes? + + diff --git a/24/part2.c b/24/part2.c @@ -0,0 +1,288 @@ +#include "allocator.h" +#include "hmap.h" +#include "hmap_s.h" +#include "aoc.h" +#include "vec_s.h" +#include "dvec_s.h" +#include "util.h" + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> + +struct layer { + char cells[5][5]; + uint8_t adj[5][5]; +}; + +struct cell { + size_t adj; + char c; +}; + +static void +load_map(struct hmap *map) +{ + const char *lpos, *lend; + struct layer *layer; + size_t x, y, len; + char line[256]; + + y = 0; + layer = malloc(sizeof(struct layer)); + lpos = aoc.input; + lend = lpos + aoc.input_size; + while (readtok(line, sizeof(line), '\n', &lpos, lend)) { + len = strlen(line); + for (x = 0; x < len; x++) + layer->cells[y][x] = line[x]; + y += 1; + } + + hmap_add(map, (struct hmap_key) {.ss = 0}, + (struct hmap_val) {.p = layer}); +} + +static void +print_layer(struct hmap *map, ssize_t z) +{ + struct hmap_link *link; + const struct layer *layer; + size_t x, y; + + link = hmap_get(map, (struct hmap_key) {.ss = z}); + if (link) layer = link->value.p; + for (y = 0; y < 5; y++) { + for (x = 0; x < 5; x++) { + if (x == 2 && y == 2) { + aoc_debug("?"); + } else if (link) { + aoc_debug("%c", layer->cells[y][x]); + } else { + aoc_debug("."); + } + } + aoc_debug("\n"); + } +} + +static void +print_layers(struct hmap *map, ssize_t minz, ssize_t maxz) +{ + ssize_t z; + + for (z = minz; z <= maxz; z++) { + aoc_debug("Z %li\n", z); + print_layer(map, z); + aoc_debug("\n"); + } +} + +static size_t +map_count(struct hmap *map) +{ + const struct layer *layer; + struct hmap_iter iter; + size_t x, y, count; + + count = 0; + for (HMAP_ITER(map, &iter)) { + layer = iter.link->value.p; + for (y = 0; y < 5; y++) { + for (x = 0; x < 5; x++) { + if (x == 2 && y == 2) + continue; + if (layer->cells[y][x] == '#') + count += 1; + } + } + } + + return count; +} + +static size_t +top_cells(const struct layer *layer) +{ + size_t i, count; + + count = 0; + for (i = 0; i < 5; i++) + count += layer->cells[0][i] == '#'; + + return count; +} + +static size_t +right_cells(const struct layer *layer) +{ + size_t i, count; + + count = 0; + for (i = 0; i < 5; i++) + count += layer->cells[i][4] == '#'; + + return count; +} + +static size_t +bottom_cells(const struct layer *layer) +{ + size_t i, count; + + count = 0; + for (i = 0; i < 5; i++) + count += layer->cells[4][i] == '#'; + + return count; +} + +static size_t +left_cells(const struct layer *layer) +{ + size_t i, count; + + count = 0; + for (i = 0; i < 5; i++) + count += layer->cells[i][0] == '#'; + + return count; +} + +static size_t +cell_count(struct hmap *map, struct layer *layer, + struct vec2i *pos, size_t dir, ssize_t z) +{ + struct hmap_link *link; + const struct layer *nlayer; + size_t count; + + count = 0; + if (pos->x == 2 && pos->y == 2) { + link = hmap_get(map, (struct hmap_key) {.ss = z + 1}); + if (!link) return 0; + nlayer = link->value.p; + switch (dir) { + case ADJ_NORTH: + return bottom_cells(nlayer); + case ADJ_EAST: + return left_cells(nlayer); + case ADJ_SOUTH: + return top_cells(nlayer); + case ADJ_WEST: + return right_cells(nlayer); + } + return count; + } else if (pos->x < 0 || pos->x >= 5 || pos->y < 0 || pos->y >= 5) { + link = hmap_get(map, (struct hmap_key) {.ss = z - 1}); + if (!link) return 0; + nlayer = link->value.p; + if (pos->x < 0) { + return nlayer->cells[2][1] == '#'; + } else if (pos->x >= 5) { + return nlayer->cells[2][3] == '#'; + } else if (pos->y < 0) { + return nlayer->cells[1][2] == '#'; + } else if (pos->y >= 5) { + return nlayer->cells[3][2] == '#'; + } + } else { + return layer->cells[pos->y][pos->x] == '#'; + } + + assert(0); +} + +static void +step(struct hmap *map) +{ + struct hmap_iter iter; + struct hmap_link **linkp; + struct hmap_link *link; + struct vec2i pos, npos; + ssize_t z, minz, maxz; + struct layer *layer; + size_t i, count; + char *cell; + + minz = maxz = 0; + for (HMAP_ITER(map, &iter)) { + layer = iter.link->value._p; + if (memchr(layer->cells, '#', 5 * 5)) { + minz = MIN(minz, iter.link->key.ss); + maxz = MAX(maxz, iter.link->key.ss); + } + } + minz -= 1; + maxz += 1; + + for (z = minz; z <= maxz; z++) { + linkp = hmap_link_pos(map, + (struct hmap_key) {.ss = z}); + if (!*linkp) { + layer = malloc(sizeof(struct layer)); + memset(layer->cells, '.', 5 * 5); + hmap_link_alloc(map, linkp, + (struct hmap_key) {.ss = z}, + (struct hmap_val) {.p = layer}); + } else { + layer = (*linkp)->value._p; + } + + for (pos.y = 0; pos.y < 5; pos.y++) { + for (pos.x = 0; pos.x < 5; pos.x++) { + count = 0; + for (i = 0; i < 4; i++) { + vec2i_add(&npos, &pos, &adj[i]); + count += cell_count(map, + layer, &npos, i, z); + } + layer->adj[pos.y][pos.x] = (uint8_t) count; + } + } + } + + for (z = minz; z <= maxz; z++) { + for (pos.y = 0; pos.y < 5; pos.y++) { + for (pos.x = 0; pos.x < 5; pos.x++) { + link = hmap_get(map, + (struct hmap_key) {.ss = z}); + layer = link->value._p; + cell = &layer->cells[pos.y][pos.x]; + count = layer->adj[pos.y][pos.x]; + if (*cell == '#' && count != 1) { + *cell = '.'; + } else if (*cell == '.' && (count == 1 || count == 2)) { + *cell = '#'; + } + } + } + } +} + + +void +part2(void) +{ + struct hmap_iter iter; + struct hmap map; + size_t i, answer; + + hmap_init(&map, 32, ssizet_hmap_hash, ssizet_hmap_keycmp, + &stdlib_strict_heap_allocator); + load_map(&map); + + for (i = 0; i < 200; i++) { + step(&map); + aoc_debug("-- MIN %lu --\n\n", i); + print_layers(&map, -5, 5); + } + + answer = map_count(&map); + aoc.answer = aprintf("%lu", answer); + + for (HMAP_ITER(&map, &iter)) + free(iter.link->value._p); + hmap_deinit(&map); +} diff --git a/24/test1 b/24/test1 @@ -0,0 +1,5 @@ +#..#. +####. +###.# +##.## +.##.. diff --git a/24/test2 b/24/test2 @@ -0,0 +1,5 @@ +....# +#..#. +#.?## +..#.. +#.... diff --git a/common/hmap_s.c b/common/hmap_s.c @@ -36,13 +36,13 @@ vec3i_hmap_keycmp(struct hmap_key k1, struct hmap_key k2) } uint32_t -sizet_hmap_hash(struct hmap_key key) +ssizet_hmap_hash(struct hmap_key key) { - return (uint32_t) key.s * 1737; + return (uint32_t) (key.s * 1737); } bool -sizet_hmap_keycmp(struct hmap_key k1, struct hmap_key k2) +ssizet_hmap_keycmp(struct hmap_key k1, struct hmap_key k2) { - return k1.s == k2.s; + return k1.ss == k2.ss; } diff --git a/common/hmap_s.h b/common/hmap_s.h @@ -6,5 +6,5 @@ bool vec2i_hmap_keycmp(struct hmap_key k1, struct hmap_key k2); uint32_t vec3i_hmap_hash(struct hmap_key key); bool vec3i_hmap_keycmp(struct hmap_key k1, struct hmap_key k2); -uint32_t sizet_hmap_hash(struct hmap_key key); -bool sizet_hmap_keycmp(struct hmap_key k1, struct hmap_key k2); +uint32_t ssizet_hmap_hash(struct hmap_key key); +bool ssizet_hmap_keycmp(struct hmap_key k1, struct hmap_key k2); diff --git a/common/vec.h b/common/vec.h @@ -128,6 +128,14 @@ vec3f_setv(struct vec3f *dst, double x, double y, double z) dst->z = z; } +static inline void +vec3i_add_2i(struct vec3i *dst, const struct vec3i *a, const struct vec2i *b) +{ + dst->x = a->x + b->x; + dst->y = a->y + b->y; + dst->z = a->z; +} + #undef DEFINE_VEC_SET #undef DEFINE_VEC_ADD #undef DEFINE_VEC_SUB