diff --git a/2020/day24/aoc-c.c b/2020/day24/aoc-c.c index d246346..51a9b26 100644 --- a/2020/day24/aoc-c.c +++ b/2020/day24/aoc-c.c @@ -16,6 +16,11 @@ * a new list after each process. * So we will rely on a hashtable, which will allow to quickly find a * given point. + * We use here two hash-tables: One for the current situation(cur), one + * for the next step (next); between steps, we clean cur and memcopy next + * to cur. It would be much better to swap hashtable pointers, but I wanted + * to use the hashtable.h API, where HASH_BITS() and HASH_SIZE() have to be + * macros (calculated during compilation; pointers not allowed here). */ typedef union coord { u64 val; @@ -26,24 +31,27 @@ typedef union coord { typedef struct point { coord_t pos; + int count; struct hlist_node coll; /* entry in hash table */ struct list_head all; } point_t; -#define HBITS 12 /* in bits: 12 bits = 4096 */ -#define HSIZE (1 << HBITS) +#define HBITS 9 /* in bits: 12 bits = 4096 */ +//#define HSIZE (1 << HBITS) -DEFINE_HASHTABLE(hash1, HBITS); -DEFINE_HASHTABLE(hash2, HBITS); -struct hlist_head *cur = hash1, *next = hash2; +DEFINE_HASHTABLE(hasht_cur, HBITS); +DEFINE_HASHTABLE(hasht_count, HBITS); pool_t *pt_pool; static __always_inline u32 hash(coord_t p) { - return hash_64(p.val, HBITS); + return hash_64(p.val, HASH_BITS(hasht_cur)); } +/** + * find_point - find point in hashtable + */ static point_t *find_point(struct hlist_head *head, coord_t p) { point_t *point; @@ -54,21 +62,29 @@ static point_t *find_point(struct hlist_head *head, coord_t p) return NULL; } -static point_t *add_point(struct hlist_head *p, coord_t pos) +/** + * add_point - add point in hasht_count hashtable (used to count neighbours) + */ +static point_t *add_point(coord_t pos) { point_t *new; u32 h; h = hash(pos); - if (!(new = find_point(p + h, pos))) { + if (!(new = find_point(&hasht_count[h], pos))) { new = pool_get(pt_pool); new->pos.val = pos.val; - hlist_add_head(&new->coll, p + h); + new->count = 0; + hlist_add_head(&new->coll, &hasht_count[h]); } + new->count++; return new; } -static point_t *flip_point(struct hlist_head *p, coord_t pos) +/** + * flip_point - add point in hasht_cur hashtable, remove if it exists (init) + */ +static point_t *flip_point(coord_t pos) { point_t *new; u32 h; @@ -76,7 +92,7 @@ static point_t *flip_point(struct hlist_head *p, coord_t pos) log_f(3, "val=%lu x=%d y=%d ", pos.val, pos.x, pos.y); h = hash(pos); log(3, "hash=%d ", h); - if ((new = find_point(p + h, pos))) { + if ((new = find_point(&hasht_cur[h], pos))) { log(3, "removing tile\n"); hlist_del(&new->coll); pool_add(pt_pool, new); @@ -85,21 +101,123 @@ static point_t *flip_point(struct hlist_head *p, coord_t pos) log(3, "adding tile\n"); new = pool_get(pt_pool); new->pos.val = pos.val; - hlist_add_head(&new->coll, p + h); + new->count = 0; + hlist_add_head(&new->coll, &hasht_cur[h]); } return new; } -static int count_points(struct hlist_head *h) +/** + * count_black - count elements in hasht_cur + */ +static int count_black() { point_t *cur; int res = 0; - for (int bkt = 0; bkt < HSIZE; ++bkt) { - hlist_for_each_entry(cur, &h[bkt], coll) { - res++; + ulong bucket; + + hash_for_each(hasht_cur, bucket, cur, coll) + res++; + return res; +} + +/** + * reset_hasht_count - remove all points from hasht_count hashtable. + */ +static void reset_hasht_count() +{ + point_t *cur; + struct hlist_node *tmp; + u32 bucket; + int count = 0; + hash_for_each_safe(hasht_count, bucket, tmp, cur, coll) { + hlist_del(&cur->coll); + pool_add(pt_pool, cur); + count++; + } + printf("removed %d counts\n", count); +} + +/** + * move_next_cur - move hasht_next to hasht_cur + */ +/* +static void move_next_cur() +{ + release_hasht_cur(); + memcpy(hasht_cur, hasht_next, sizeof(hasht_next)); +} +*/ + +static const coord_t neighbours [] = { + { .x = 2, .y = 0 }, { .x = -2, .y = 0 }, /* east and west */ + { .x = 1, .y = -1}, { .x = 1, .y = 1 }, /* SE and NE */ + { .x = -1, .y = -1}, { .x = -1, .y = 1 } /* SW and NW */ +}; + +/** + * count_neighbours - count hasht_cur neighbours, result in hasht_next + */ +static void count_neighbours() +{ + point_t *cur; + u32 bucket; + + hash_for_each(hasht_cur, bucket, cur, coll) { + for (ulong i = 0; i < ARRAY_SIZE(neighbours); ++i) { + coord_t neigh = cur->pos; + neigh.x += neighbours[i].x; + neigh.y += neighbours[i].y; + add_point(neigh); + } + } + hash_for_each(hasht_count, bucket, cur, coll) { + log(3, "(%d,%d)=%d ", cur->pos.x, cur->pos.y, cur->count); + u32 h = hash(cur->pos); + point_t *tmp = find_point(&hasht_count[h], cur->pos); + if (tmp != cur) + log(3, "err:%p!=%p ", cur, tmp); + } +} + +/** + * adjust_neighbours - adjust hasht_next according to rules + */ +static void adjust_neighbours() +{ + point_t *pt_cur, *pt_count; + u32 bucket; + struct hlist_node *tmp; + + /* 1) check hasht_cur tiles (currently black) + */ + hash_for_each_safe(hasht_cur, bucket, tmp, pt_cur, coll) { + int h = hash(pt_cur->pos); + point_t *pt_count = find_point(&hasht_count[h], pt_cur->pos); + if (!pt_count || pt_count->count > 2) { + log(3, "P1 removing %d (%d,%d)\n", pt_count? pt_count->count: 0, + pt_cur->pos.x, pt_cur->pos.y); + hlist_del(&pt_cur->coll); + pool_add(pt_pool, pt_cur); + } else { + log(3, "P1 keeping %d (%d,%d)\n", pt_count? pt_count->count: 0, + pt_cur->pos.x, pt_cur->pos.y); + } + /* we do not want to re-consider this point in next loop + */ + if (pt_count) { + hlist_del(&pt_count->coll); + pool_add(pt_pool, pt_count); + } + } + /* 2) check remaining points in hasht_next (currently white) + */ + hash_for_each_safe(hasht_count, bucket, tmp, pt_count, coll) { + if (pt_count->count == 2) { + hash_del(&pt_count->coll); + hash_add(hasht_cur, &pt_count->coll, hash(pt_count->pos)); } } - return res; } static void parse() @@ -115,19 +233,10 @@ static void parse() char *c = buf; while (*c) { switch (*c) { - case 'e': - ++p.x; - break; - case 'w': - --p.x; - break; - case 's': - --p.y; - ++c; - break; - case 'n': - ++p.y; - ++c; + case 'e': ++p.x; break; + case 'w': --p.x; break; + case 's': --p.y; ++c; break; + case 'n': ++p.y; ++c; } if (*c == 'e') ++p.x; @@ -136,7 +245,7 @@ static void parse() c++; //printf("pos=%ld x=%d y=%d\n", c - buf, p.x, p.y); } - flip_point(cur, p); + flip_point(p); } } @@ -151,7 +260,7 @@ int main(ac, av) char **av; { int opt, part = 1; - ulong res = 0; + int res = 0; while ((opt = getopt(ac, av, "d:p:")) != -1) { switch (opt) { @@ -165,10 +274,20 @@ int main(ac, av) return usage(*av); } } - pt_pool = pool_create("pool_points", 256, sizeof(point_t)); - + pt_pool = pool_create("pool_points", 512, sizeof(point_t)); parse(); - printf("count=%d\n", count_points(cur)); - printf("%s : res=%lu\n", *av, res); + printf("initial count = %d\n", count_black()); + if (part == 2) { + for (int i = 0; i < 100; ++i) { + reset_hasht_count(); + count_neighbours(); + adjust_neighbours(); + printf("count after loop %d = %d\n", i + 1, count_black()); + } + } + res = count_black(); + printf("size=%lu\n", sizeof(hasht_count)); + printf("%s : res=%d\n", *av, res); + pool_stats(pt_pool); exit (0); }