2022 day 15 (C) part 1 (dirty and even more)

This commit is contained in:
2023-03-21 11:17:04 +01:00
parent 2ed6284bcd
commit f490c2353e
2 changed files with 372 additions and 141 deletions

View File

@@ -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?/

View File

@@ -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);
}