diff --git a/2021/day23/aoc-c.c b/2021/day23/aoc-c.c new file mode 100644 index 0000000..7805ec9 --- /dev/null +++ b/2021/day23/aoc-c.c @@ -0,0 +1,379 @@ +/* aoc-c.c: Advent of Code 2021, day 23 parts 1 & 2 + * + * Copyright (C) 2022 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* Warning: Work in progress. Should not work before I spend a lot of time + * on it, the original "bitboard" approach for moves generation is pretty + * difficult to program/debug + */ +#include +#include +#include +#include + +#include "pool.h" +#include "debug.h" +#include "bits.h" +#include "list.h" + +typedef enum { + A = 0, + B, + C, + D +} amphipod_t; + +static const u64 cost[] = { + 1, 10, 100, 1000 +}; + +/* bitboards usage: + * + * + * ############# + * #abcdefghijk# + * ###l#m#n#o### + * #p#q#r#s# + * ######### + * + * bit 63: a + * bit 62: b + * bit 61: c + * ... + * bit 59: e + * ... + * bit 57: g + * ... + * bit 55: i + * ... + * bit 53: k + * ... + * bit 61 - 16 = 45: l + * bit 59 - 16 = 43: m + * ... + * bit 53 - 16 = 39: o + * ... + * bit 61 - 32 = 29: p + * ... + * bit 55 - 32 = 23: s + * + * move up is current << 16 + * move left is current << 1 + * move right is current >> 1 + * + * forbidden mask: + * abcdefghijk l m n o p q r s + * ...........1111111.1.1.1.111111111.1.1.1.11111111111111111111111 + * 0000000000011111110101010111111111010101011111111111111111111111 + * + * allowed mask: + * abcdefghijk l m n o p q r s + * 1111111111100000001010101000000000101010100000000000000000000000 + * ...........1111111.1.1.1.111111111.1.1.1.11111111111111111111111 + * 0000000000011111110101010111111111010101011111111111111111111111 + */ +#define FORBIDDEN 0x1FD57FD57FFFFFUL +#define ALLOWED (~FORBIDDEN) + +typedef struct pos { + u64 amp[4]; /* bitboards */ + int moves; + u64 cost; + u64 occupied; + u64 available; /* ALLOWED & ~occupied */ + struct list_head list_pos; +} pos_t; + +pool_t *pool_pos; +pos_t *pos_zero; + +static inline u64 get_occupancy(pos_t *pos) +{ + return pos->amp[0] | pos->amp[1] | pos->amp[2] | pos->amp[3]; +} + +/* here we allow mask to be in forbidden places + */ +static void mask_print(u64 bits) +{ + //log_f(1, "A=%lx %lu\n", pos->amp[A], pos->amp[A]); + //log(1, " B=%lx %lu\n", pos->amp[B], pos->amp[B]); + //log(1, " C=%lx %lu\n", pos->amp[C], pos->amp[C]); + //log(1, " D=%lx %lu\n", pos->amp[D], pos->amp[D]); + + u64 tmp; + int count; + log(4, "%lu: popcount=%d ", bits, popcount64(bits)); + bit_for_each64_2(count, tmp, bits) { + log(4, " %d", count); + } + log(4, "\n"); + + log(1, "#############\n#"); + for (int i = 63; i > 21; --i) { + u64 mask = 1UL << i; + //log(1, "bit = %d A=%d B=%d C=%d D=%d\n"); + if (bits & mask) + printf("X"); + else if (FORBIDDEN & mask) + printf("#"); + else + printf("."); + + if (i == 52) { /* hallway line */ + printf("\n"); + i -= 3; + } else if (i == (52 - 16)) { /* rooms top line */ + printf("\n "); + i -= 5; + } else if (i == (52 - 32)) { /* rooms bottom line */ + printf("\n "); + i -= 5; + } + } + printf("\n #########\n"); +} + +static void burrow_print(pos_t *pos) +{ + log_f(4, "A=%lx %lu\n", pos->amp[A], pos->amp[A]); + log(4, " B=%lx %lu\n", pos->amp[B], pos->amp[B]); + log(4, " C=%lx %lu\n", pos->amp[C], pos->amp[C]); + log(4, " D=%lx %lu\n", pos->amp[D], pos->amp[D]); + + for (int i = 0; i < 4; ++i) { + u64 tmp; + int count; + log(4, "%c: popcount=%d ", i + 'A', popcount64(pos->amp[i])); + bit_for_each64_2(count, tmp, pos->amp[i]) { + log(4, " %d", count); + } + log(4, "\n"); + } + + log(1, "#############\n#"); + for (int i = 63; i > 21; --i) { + u64 mask = 1UL << i; + if (FORBIDDEN & mask) + printf("#"); + else { + //log(1, "bit = %d A=%d B=%d C=%d D=%d\n"); + if (pos->amp[A] & mask) + printf("A"); + else if (pos->amp[B] & mask) + printf("B"); + else if (pos->amp[C] & mask) + printf("C"); + else if (pos->amp[D] & mask) + printf("D"); + else + printf("."); + } + if (i == 52) { /* hallway line */ + printf("\n"); + i -= 3; + } else if (i == (52 - 16)) { /* rooms top line */ + printf("\n "); + i -= 5; + } else if (i == (52 - 32)) { /* rooms bottom line */ + printf("\n "); + i -= 5; + } + } + printf("\n #########\n"); +} + +/* get a position from memory pool + */ +static pos_t *get_pos(pos_t *from) +{ + pos_t *new; + if (!(new = pool_get(pool_pos))) + return NULL; + if (!from) { + new->amp[0] = new->amp[1] = new->amp[2] = new->amp[3] = 0; + new->moves = 0; + new->cost = new->occupied = new->available = 0; + } else { + *new = *from; + } + INIT_LIST_HEAD(&new->list_pos); + return new; +} + +static u64 move_up(pos_t *pos, int amphipod) +{ + pos_t newpos = *pos; + u64 new, tmp, occupied = 0; + new = pos->amp[amphipod] << 16; + + log(1, "********* moving %c up\n", amphipod + 'A'); + for (int i = 0; i < 4; ++i) { + occupied |= pos->amp[i]; + } + log(1, "occupied:\n"); + mask_print(occupied); + log(1, "current occupation:\n"); + mask_print(pos->amp[amphipod]); + log(1, "new occupation:\n"); + mask_print(new); + + tmp = new & ALLOWED; + if (tmp != new) + log(1, "%c cannot go up (forbidden)\n", amphipod + 'A'); + tmp = new & ~occupied; + if (tmp != new) + log(1, "%c cannot go up (occupied)\n", amphipod + 'A'); + newpos.amp[amphipod] = new & ALLOWED & ~occupied; + //burrow_print(&newpos); + return newpos.amp[amphipod]; +} + +static u64 move_down(pos_t *pos, int amphipod) +{ + pos_t newpos = *pos; + u64 new; + log(1, "********* moving %c down\n", amphipod + 'A'); + new = pos->amp[amphipod] >> 16; + printf("occupied\n"); + mask_print(pos->occupied); + printf("available\n"); + mask_print(pos->available); + newpos.amp[amphipod] = new & pos->available; + return newpos.amp[amphipod]; +} + +static u64 move_left(pos_t *pos, int amphipod) +{ + pos_t newpos = *pos; + u64 new, tmp, occupied = 0; + new = pos->amp[amphipod] << 1; + + log(1, "********* moving %c left\n", amphipod + 'A'); + for (int i = 0; i < 4; ++i) { + occupied |= pos->amp[i]; + } + log(1, "occupied:\n"); + mask_print(occupied); + log(1, "current occupation:\n"); + mask_print(pos->amp[amphipod]); + log(1, "new occupation:\n"); + mask_print(new); + + tmp = new & ALLOWED; + if (tmp != new) + log(1, "%c cannot go left (forbidden)\n", amphipod + 'A'); + tmp = new & ~occupied; + if (tmp != new) + log(1, "%c cannot go left (occupied)\n", amphipod + 'A'); + newpos.amp[amphipod] = new & ALLOWED & ~occupied; + //burrow_print(&newpos); + return newpos.amp[amphipod]; +} + +static u64 move_right(pos_t *pos, int amphipod) +{ + pos_t newpos = *pos; + u64 new; + log(1, "********* moving %c right\n", amphipod + 'A'); + new = pos->amp[amphipod] >> 1; + printf("occupied\n"); + mask_print(pos->occupied); + printf("available\n"); + mask_print(pos->available); + newpos.amp[amphipod] = new & ALLOWED & pos->available; + return newpos.amp[amphipod]; +} + + +/* minimal parsing: We just read the 3-5 lines to get + * the amphipods location in side rooms + */ +static void read_input() +{ + size_t alloc = 0; + ssize_t buflen; + char *buf = NULL; + int line = 0; + pos_t *pos = get_pos(NULL); + + while ((buflen = getline(&buf, &alloc, stdin)) > 0) { + buf[--buflen] = 0; + printf("line=%d str=%s\n", line, buf); + if (line == 2 || line == 3) { + u64 bit = 45 - 16 * (line - 2); + //printf("bit = %d\n", bit); + for (int i=0; i < 4; ++i) { + int amp = buf[i * 2 + 3] - 'A'; + //printf("bit = %lu char = %c\n", bit, amp + 'A'); + pos->amp[amp] |= 1UL << bit; + bit -= 2; + } + } + line++; + } + pos->occupied = get_occupancy(pos); + pos->available = ALLOWED & ~pos->occupied; + burrow_print(pos); + for (int i = 0; i < 4; ++i) { + mask_print(move_up(pos, i)); + mask_print(move_left(pos, i)); + mask_print(move_down(pos, i)); + mask_print(move_right(pos, i)); + } + free(buf); +} + +static s64 part1() +{ + return 1; +} + +static s64 part2() +{ + return 2; +} + +static int usage(char *prg) +{ + fprintf(stderr, "Usage: %s [-d debug_level] [-p part]\n", prg); + return 1; +} + +int main(int ac, char **av) +{ + int opt, part = 1; + + while ((opt = getopt(ac, av, "d:p:")) != -1) { + switch (opt) { + case 'd': + debug_level_set(atoi(optarg)); + break; + case 'p': /* 1 or 2 */ + part = atoi(optarg); + if (part < 1 || part > 2) + return usage(*av); + break; + default: + return usage(*av); + } + } + if (optind < ac) + return usage(*av); + + pool_pos = pool_create("pos", 1024, sizeof(pos_t)); + + read_input(); + + printf("%s : res=%ld\n", *av, part == 1? part1(): part2()); + + exit(0); +}