From f490c2353e39211a8147e05daf1e70a8df00c649 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Tue, 21 Mar 2023 11:17:04 +0100 Subject: [PATCH] 2022 day 15 (C) part 1 (dirty and even more) --- 2022/day15/README.org | 22 ++ 2022/day15/aoc-c.c | 491 ++++++++++++++++++++++++++++++------------ 2 files changed, 372 insertions(+), 141 deletions(-) diff --git a/2022/day15/README.org b/2022/day15/README.org index 36a3171..8778b70 100644 --- a/2022/day15/README.org +++ b/2022/day15/README.org @@ -137,3 +137,25 @@ a beacon cannot be present. Consult the report from the sensors you just deployed. /In the row where =y=2000000=, how many positions cannot contain a beacon?/ + +Your puzzle answer was =5176944=. + +The first half of this puzzle is complete! It provides one gold star: * + +** --- Part Two --- +Your handheld device indicates that the distress signal is coming from a +beacon nearby. The distress beacon is not detected by any sensor, but +the distress beacon must have =x= and =y= coordinates each no lower than +=0= and no larger than =4000000=. + +To isolate the distress beacon's signal, you need to determine its +/tuning frequency/, which can be found by multiplying its =x= coordinate +by =4000000= and then adding its =y= coordinate. + +In the example above, the search space is smaller: instead, the =x= and +=y= coordinates can each be at most =20=. With this reduced search area, +there is only a single position that could have a beacon: =x=14, y=11=. +The tuning frequency for this distress beacon is =56000011=. + +Find the only possible position for the distress beacon. /What is its +tuning frequency?/ diff --git a/2022/day15/aoc-c.c b/2022/day15/aoc-c.c index b610f96..086699d 100644 --- a/2022/day15/aoc-c.c +++ b/2022/day15/aoc-c.c @@ -20,175 +20,384 @@ #include "br.h" #include "list.h" #include "pool.h" +#include "hashtable.h" +#include "pjwhash-inline.h" #include "debug.h" #include "aoc.h" -pool_t *pool_segment; +static pool_t *pool_segment, *pool_row; -#define INF (-1) /* found infinite fall position */ +#define HBITS 20 /* 20 bits: 1,048,576 buckets */ +static DEFINE_HASHTABLE(hasht_rows, HBITS); -typedef enum { /* map cells */ - EMPTY = '.', - STONE = '#', - SAND = 'o', -} type_t; - -typedef struct segment { - int x1, y1, x2, y2; - struct list_head list; -} segment_t; - -LIST_HEAD(segments); - -typedef struct map { +/** + * struct map - full map + * @xmin, @xmax: most left/right x-coordinates. + * @ymin, @ymax: most top/bottom y-coordinates. + * @hash: rows hash table + */ +struct map { int xmin, xmax, ymin, ymax; - int size_x, size_y; - int deltax, deltay; - int dropcol; /* drop sand here */ - int dropped; /* number of sand units */ - char *m; -} map_t; + struct hlist_head *hash; +} map = { + INT_MAX, INT_MIN, INT_MAX, INT_MIN, hasht_rows +}; -#define XY(m, x, y) ((y) * m->size_x + (x)) -#define P(m, x, y) (m->m[XY(m, (x), (y))]) +/** + * struct row - row description + * @row: row number. + * @segments: segments list. + * @hlist: htable bucket list. + */ +struct row { + int row; + int beacons[64]; + int nbeacons; + struct list_head segments; + struct hlist_node hlist; +}; -static int drop_sand(struct map *m, int x, int y) +/** + * struct segment - The main segment structure + * @row: the row number + * @start, @end: segment start and end + * @list: sorted row's segments list + * + * If a row contains 2 segments 1-3 and 7-8, it would be represented as: + * +----------+ +----------+ + * | start: 1 |<------>| start: 7 | + * | end: 3 | | end: 8 | + * +----------+ +----------+ + * + * This implies adding a segment must manage merging. For example, adding + * segment 2-4 above would change the first segment to 1-4, or adding 0-9 + * should change the first segment to 0-9 and remove the second one. + */ +struct segment { + int row; + int start, end; + struct list_head list; +}; + +static struct row *find_row(struct hlist_head *head, int row) { - int ret = 0, tmp; - - if (y >= m->ymax) /* part 1: nothing left under */ - return INF; - - if (P(m, x, y+1) == EMPTY) { /* down */ - if ((tmp = drop_sand(m, x, y+1)) < 0) - return INF; - ret += tmp; - } - if (P(m, x-1, y+1) == EMPTY) { /* left */ - if ((tmp = drop_sand(m, x-1, y+1)) < 0) - return INF; - ret += tmp; - } - if (P(m, x+1, y+1) == EMPTY) { /* right */ - if ((tmp = drop_sand(m, x+1, y+1)) < 0) - return INF; - ret += tmp; - } - /* the 3 lower adjacent cells are filled */ - P(m, x, y) = SAND; - m->dropped++; - if (y == 0) /* part 2 */ - return INF; - return ret; + struct row *cur; + hlist_for_each_entry(cur, head, hlist) + if (cur->row == row) + return cur; + return NULL; } -static struct map *gen_map(struct map *m, int part) +static void print_map() { - segment_t *cur; + struct segment *s; - if (part == 1) { - m->size_x = (m->xmax - m->xmin) + 3; - m->size_y = m->ymax + 1; - m->deltax = m->xmin - 1; - } else { - m->ymax += 2; - m->xmin = 500 - m->ymax - 1; - m->xmax = 500 + m->ymax + 1; - m->deltax = m->xmin; - m->size_x = (m->xmax - m->xmin); - m->size_y = m->ymax + 3; - } - m->dropcol = 500 - m->deltax; - m->dropped = 0; - - m->m = malloc(m->size_x * m->size_y); - memset(m->m, '.', m->size_x * m->size_y); - list_for_each_entry(cur, &segments, list) { - int x1 = cur->x1 - m->deltax, y1 = cur->y1 - m->deltay, - x2 = cur->x2 - m->deltax, y2 = cur->y2 - m->deltay; - int dx = 0, dy = 0; - if (x1 == x2) - dy = y2 - y1 > 0 ? 1 : -1; - else - dx = x2 - x1 > 0 ? 1 : -1; - do { - P(m, x1, y1) = '#'; - x1 += dx, y1 += dy; - } while (x1 != x2 || y1 != y2); - P(m, x2, y2) = '#'; - } - if (part == 2) - for (int i = 0; i < m->size_x; ++i) - P(m, m->xmin - m->deltax + i, m->ymax) = STONE; - return m; -} - -static struct map *parse(map_t *m) -{ - size_t alloc = 0; - ssize_t buflen; - char *buf = NULL, *cur; - int i, scanned; - int x1, y1, x2, y2, x, y, n; - segment_t *segment; - - while ((buflen = getline(&buf, &alloc, stdin)) > 0) { - i = 0; - buf[--buflen] = 0; - cur = buf; - while (1) { - scanned = sscanf(cur, "%d,%d ->%n", &x, &y, &n); - if (scanned != 2) - break; - m->xmin = min(x, m->xmin); - m->xmax = max(x, m->xmax); - m->ymin = min(y, m->ymin); - m->ymax = max(y, m->ymax); - if (i) { - x1 = x2; - y1 = y2; + printf("XXXXXXX "); + for (int x = map.xmin; x < 0; ++x) + putchar(' '); + printf("0\n"); + for (int y = map.ymin; y <= map.ymax; ++y) { + printf("%7d ", y); + struct row *row = find_row(&map.hash[hash_32(y, HBITS)], y); + int x = map.xmin; + list_for_each_entry(s, &row->segments, list) { + for (; x < s->start; ++x) { + putchar('.'); + } + for (; x <= s->end; ++x) { + putchar('#'); } - x2 = x; - y2 = y; - if (!i) /* first point */ - goto next; - segment = pool_get(pool_segment); - segment->x1 = x1; - segment->y1 = y1; - segment->x2 = x2; - segment->y2 = y2; - list_add_tail(&segment->list, &segments); - next: - i++; - cur += n; - if (cur - buf >= buflen) - break; } + for (; x <= map.xmax; ++x) { + putchar('.'); + } + putchar('\n'); } - free(buf); - return m; } -static int doit(map_t *m, int part) + +static struct segment *get_segment(int row, int x1, int x2) { - m = gen_map(parse(m), part); - drop_sand(m, m->dropcol, 0); - return m->dropped; + struct segment *new = pool_get(pool_segment); + + log_f(5, "alloc segment (%d,%d) on row (%d)\n", x1, x2, row); + new->row=row; + new->start=x1; + new->end=x2; + INIT_LIST_HEAD(&new->list); + return new; +} + +static int merge_segment(struct row *prow, int x1, int x2) +{ + struct segment *seg, *seg2, *new; + struct list_head *cur, *tmp, *next; + + log_f(3, "merging segment (%d,%d) on row (%d)\n", x1, x2, prow->row); + new = get_segment(prow->row, x1, x2); + if (list_empty(&prow->segments)) { + log_f(3, " first segment\n"); + list_add(&new->list, &prow->segments); + return 1; + } + list_for_each_safe(cur, tmp, &prow->segments) { + seg = list_entry(cur, struct segment, list); + log_f(3, "compare to (start=%d end=%d)\n", seg->start, seg->end); + if (x1 > seg->end) { + log_f(3, " skipping to next\n"); + continue; + } + if (x2 < seg->start) { + log_f(3, " adding to left\n"); + list_add_tail(&new->list, &seg->list); + return 2; + } + /* we know here there is at least an overlap */ + + /* new is inside cur: do nothing */ + if (x1 >= seg->start && x2 <= seg->end) { + log_f(3, " overlap IN, do nothing\n"); + pool_add(pool_segment, new); + return 3; + } + /* cur inside new: remove cur */ + if (x1 <= seg->start && x2 >= seg->end) { + log_f(3, " overlap OUT, remove current\n"); + // TODO: avoid this + list_del(cur); + pool_add(pool_segment, seg); + continue; + } + + /* exactly one overlap */ + log_f(3, " exactly one overlap\n"); + + if (x1 > seg->start) { + log_f(3, " overlap left new=(%d,%d)\n", new->start, new->end); + new->start = seg->start; + } + if (x2 >= seg->end){ + log_f(3, " overlap right: delete cur\n"); + list_del(cur); + pool_add(pool_segment, seg); + continue; + } + /* we stop here */ + log_f(3, " stop here\n"); + new->end = seg->end; + list_add_tail(&new->list, cur); + list_del(cur); + pool_add(pool_segment, seg); + return 4; + + if (x2 <= seg->end) { + log_f(3, " extending left side %d->%d\n", seg->start, x1); + list_add(&new->list, cur); + list_del(cur); + } + if (x1 < seg->start) { + log_f(3, " extending left side %d->%d\n", seg->start, x1); + seg->start = x1; + if (x2 <= seg->end) { + } else { + log_f(3, " already covered\n"); + } + return 3; + } + /* here we know that 1) x2 > end and 2) x1 <= start */ + if (x1 < seg->start) { + log_f(3, " extending temp left side %d->%d\n", seg->start, x1); + seg->start = x1; + } + /* now x1 and cur->start are fixed: only x2 is left, and > end */ + log_f(3, " fixing end\n"); + next = cur; + do { + seg2 = list_entry(next, struct segment, list); + + if (x2 <= seg2->end) { /* we stop here */ + log_f(3, " found end\n"); + if (next != cur) { + log_f(3, " extending cur end\n"); + seg->end = seg2->end; + list_del(next); + pool_add(pool_segment, seg2); + } + return 6; + } + if (list_is_last(next, &prow->segments)) { + if (x2 > seg2->end) { + log_f(3, " extending cur end\n"); + seg->end = x2; + if (next != cur) { + log_f(3, " removing element\n"); + list_del(next); + pool_add(pool_segment, seg2); + } + return 5; + } + } + //next: + next = next->next; + } while (1); + } + log_f(3, " adding at end of list\n"); + list_add_tail(&new->list, &prow->segments); + return 10; +} + +static int _add_segment(int row, int center, int half) +{ + int x1 = center - half, x2 = center + half; + uint hash = row, bucket = hash_32(hash, HBITS); + struct row *prow = find_row(&map.hash[bucket], hash); + + log_f(3, "adding segment (%d,%d) on row (%d) - bucket(%u) = %u prow=%p\n", + x1, x2, row, hash, bucket, prow); + if (row < map.ymin) { + log(5, "new ymin=%d->%d\n", map.ymin, row); + map.ymin = row; + } + if (row > map.ymax) { + log(5, "new ymax=%d->%d\n", map.ymax, row); + map.ymax = row; + } + if (x1 < map.xmin) { + log(5, "new xmin=%d->%d\n", map.xmin, x1); + map.xmin = x1; + } + if (x2 > map.xmax) { + log(5, "new xmax=%d->%d\n", map.xmax, x2); + map.xmax = x2; + } + log(3, "map borders: xmin=%d xmax=%d ymin=%d ymax=%d\n", + map.xmin, map.xmax, map.ymin, map.ymax); + if (!prow) { + prow = pool_get(pool_row); + prow->row = row; + prow->nbeacons = 0; + INIT_HLIST_NODE(&prow->hlist); + INIT_LIST_HEAD(&prow->segments); + hlist_add_head(&prow->hlist, &map.hash[bucket]); + } + merge_segment(prow, x1, x2); + /* + * struct segment *segment = pool_get(pool_segment); + * segment->start = x1; + * segment->end = x2; + * segment->row = row; + * hlist_add_head(&map.hash, &segment->list, &map.hash[bucket]); + */ + + + return 1; +} + +static void add_beacon(int bx, int by) +{ + uint hash = by, bucket = hash_32(hash, HBITS); + struct row *prow = find_row(&map.hash[bucket], hash); + + if (!prow) { + puts("fuck)"); + exit(1); + } + for (int i = 0; i < prow->nbeacons; ++i) { + if (prow->beacons[i] == bx) + return; + } + prow->beacons[prow->nbeacons++] = bx; +} + +static int add_segment(int sx, int sy, int bx, int by) +{ + int manhattan; + + log_f(3, "sensor4=(%d, %d) beacon=(%d, %d) - ", sx, sy, bx, by); + manhattan = abs(bx - sx) + abs(by - sy); + printf("manhattan=%u\n", manhattan); + _add_segment(sy, sx, manhattan); + for (int dy = 1, half = manhattan - 1; dy <= manhattan; ++dy, half--) { + _add_segment(sy - dy, sx, half); + _add_segment(sy + dy, sx, half); + } + add_beacon(bx, by); + return 1; +} + +static int parse(int part) +{ + int scanned, sx, sy, bx, by; + int line = 1; + //while ((buflen = getline(&buf, &alloc, stdin)) > 0) { + // buf[--buflen] = 0; + while (1) { + scanned = scanf("%*[^-0-9]%d%*[^-0-9]%d%*[^-0-9]%d%*[^-0-9]%d", + &sx, &sy, &bx, &by); + if (scanned != 4) + break; + printf("line %d scanned=%d sx=%d sy=%d bx=%d by=%d\n", + line++, scanned, sx, sy, bx, by); + if (part == 1) { + int row = 2000000; + map.xmin = -2; + map.xmax = 25; + int manhattan = abs(bx - sx) + abs(by - sy); + log(3, "m=%d : ", manhattan); + if (row >= sy && row <= (sy + manhattan)) { + int half = manhattan - (row - sy); + log(3, "min ok half=%d\n", half); + _add_segment(row, sx, half); + } else if (row < sy && row >= (sy - manhattan)) { + int half = manhattan - (sy - row); + log(3, "max ok half=%d\n", half); + _add_segment(row, sx, half); + } else { + log(3, "OUT\n"); + } + if (by == row) + add_beacon(bx, by); + + } + //add_segment(sx, sy, bx, by); + } + //free(buf); + return 1; +} + +static int doit(int part) +{ + int res = 0; + parse(part); + + uint row = 2000000, bucket = hash_32(row, HBITS); + struct row *prow = find_row(&map.hash[bucket], row); + struct segment *cur; + + if (prow) { + print_map(); + list_for_each_entry(cur, &prow->segments, list) { + printf("counting segment (%d,%d) = %d nbeac=%d\n", cur->start, cur->end, + cur->end - cur->start + 1, prow->nbeacons); + res += cur->end - cur->start + 1; + } + res -= prow->nbeacons; + } + print_map(); + return res; } int main(int ac, char **av) { int part = parseargs(ac, av); - static map_t m; - m.xmin = INT_MAX; m.xmax = INT_MIN; - m.ymin = INT_MAX, m.ymax = INT_MIN; - pool_segment = pool_create("segment", 512, sizeof(segment_t)); + pool_row = pool_create("rows", 512, sizeof(struct row)); + pool_segment = pool_create("segments", 512, sizeof(struct segment)); - printf("%s: res=%d\n", *av, doit(&m, part)); - free(m.m); + printf("%s: res=%d\n", *av, doit(part)); + pool_destroy(pool_row); pool_destroy(pool_segment); exit(0); }